[
  {
    "path": ".gitattributes",
    "content": "# Set the default behavior of genesis.json, in case core.autocrlf is set incorrectly\ngenesis.json eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Create a detailed report about a deficiency in the BitShares Core implementation.\n\n---\n\n**Instructions**\nPlease include a detailed Title above. Next, please complete the following sections below:\n* Bug Description\n* Impacts\n* Steps To Reproduce\n* Expected Behavior\n* Screenshots (optional)\n* Host Environment (optional)\n* Additional Context (optional)\n\nFinally, press the 'Submit new issue' button. The Core Team will evaluate and prioritize your Bug Report for future development. \n\n**Bug Description**\nA clear and concise description of what the bug is.\n\n**Impacts**\nDescribe which portion(s) of BitShares Core may be impacted by this bug. Please tick at least one box.\n- [ ] API (the application programming interface)\n- [ ] Build (the build process or something prior to compiled code)\n- [ ] CLI (the command line wallet)\n- [ ] Deployment (the deployment process after building such as Docker, Travis, etc.)\n- [ ] DEX (the Decentralized EXchange, market engine, etc.)\n- [ ] P2P (the peer-to-peer network for transaction/block propagation)\n- [ ] Performance (system or user efficiency, etc.)\n- [ ] Protocol (the blockchain logic, consensus, validation, etc.)\n- [ ] Security (the security of system or user data, etc.)\n- [ ] UX (the User Experience)\n- [ ] Other (please add below)\n\n**Steps To Reproduce**\nSteps to reproduce the behavior (example outlined below):\n1. Execute API call '...'\n2. Using JSON payload '...'\n3. Received response '...'\n4. See error in screenshot\n\n**Expected Behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots (optional)**\nIf applicable, add screenshots to help explain process flow and behavior.\n\n**Host Environment**\nPlease provide details about the host environment. Much of this information can be found running: `witness_node --version`. \n - Host OS:             [e.g. Ubuntu 18.04 LTS]\n - Host Physical RAM    [e.g. 4GB]\n - BitShares Version:   [e.g. 2.0.180425]\n - OpenSSL Version:     [e.g. 1.1.0g]\n - Boost Version:       [e.g. 1.65.1]\n \n**Additional Context (optional)**\nAdd any other context about the problem here.\n\n## CORE TEAM TASK LIST\n- [ ] Evaluate / Prioritize Bug Report\n- [ ] Refine User Stories / Requirements\n- [ ] Define Test Cases\n- [ ] Design / Develop Solution\n- [ ] Perform QA/Testing\n- [ ] Update Documentation\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/build_error.md",
    "content": "---\nname: Build Error\nabout: Create a detailed report about an error encountered during the BitShares Core build process.\n\n---\n\n**Instructions**\nPlease include a detailed Title above. Next, please complete the following sections below:\n* Build Error\n* Build Environment\n* Steps To Reproduce\n* Console Logs (optional)\n\nFinally, press the 'Submit new issue' button. The Core Team will evaluate and prioritize your Bug Report for future development. \n\n**Build Error Description**\nA clear and concise description of what the build error is.\n\n**Build Environment**\nDetails about the build environment, including the relevant required libraries. Much of this information can be found in the `CMakeFiles/CMakeOutput.log`. \n - Host OS:             [e.g. Ubuntu 18.04 LTS]\n - Host Physical RAM    [e.g. 4GB]\n - Source Branch/Tag:   [e.g. master or 2.0.180425]\n - OpenSSL Version:     [e.g. 1.1.0g]\n - Boost Version:       [e.g. 1.65.1]\n - C++ Compiler:        [e.g. gcc version 4.8.5]\n\n**Steps To Reproduce**\nSteps to reproduce the behavior (example outlined below):\n1. Using installation guide from this URL...\n2. This is my complete build script...\n3. It fails at this step with the following output...\n4. See the error in the console log below...\n\n**Console Logs (optional)**\nPlease provide the full console log, including all commands entered and their output. This will allow detailed troubleshooting.\n\n## CORE TEAM TASK LIST\n- [ ] Evaluate `Build Error`\n- [ ] Provide build guidance\n- [ ] <OR> Create `Bug Report`\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature Request\nabout: Suggest an idea for the BitShares Core Team to evaluate and prioritize for development.\n\n---\n\n**Instructions**\nPlease include a detailed Title above. Next, please complete the following sections below:\n* User Story\n* Impacts\n* Additional Context (optional)\n\nFinally, press the 'Submit new issue' button. The Core Team will evaluate and prioritize your Feature Request for future development. \n\n**User Story**\nPlease tell us about your feature request using the User Story format:\nAs a `<persona>` I want `<some functionality>` so that `<some benefit is realized>`.\n\nAt minimum, please define the `<who>`, `<what>` and `<why>` for your feature request. The `<who>` may be the system software, a component thereof, the end user, etc.; please be specific describing the context. The `<what>` details the solution your feature will provide; please describe the process flow for the functionality. The `<why>` details the benefits the feature will deliver; consider referencing alternative implementations for context.\n\n**Impacts**\nDescribe which portion(s) of BitShares Core may be impacted by your request. Please tick at least one box.\n- [ ] API (the application programming interface)\n- [ ] Build (the build process or something prior to compiled code)\n- [ ] CLI (the command line wallet)\n- [ ] Deployment (the deployment process after building such as Docker, Travis, etc.)\n- [ ] DEX (the Decentralized EXchange, market engine, etc.)\n- [ ] P2P (the peer-to-peer network for transaction/block propagation)\n- [ ] Performance (system or user efficiency, etc.)\n- [ ] Protocol (the blockchain logic, consensus, validation, etc.)\n- [ ] Security (the security of system or user data, etc.)\n- [ ] UX (the User Experience)\n- [ ] Other (please add below)\n\n**Additional Context (optional)**\nAdd any other context about your request here.\n\n## CORE TEAM TASK LIST\n- [ ] Evaluate / Prioritize Feature Request\n- [ ] Refine User Stories / Requirements\n- [ ] Define Test Cases\n- [ ] Design / Develop Solution\n- [ ] Perform QA/Testing\n- [ ] Update Documentation\n"
  },
  {
    "path": ".gitignore",
    "content": "*.a\n*.sw*\n\n*.cmake\nCMakeCache.txt\nCMakeFiles\nMakefile\ncompile_commands.json\nmoc_*\n*.moc\n\ngenesis.json\nhardfork.hpp\n\nbuild/\n\nlibraries/utilities/git_revision.cpp\n\nlibraries/wallet/Doxyfile\nlibraries/wallet/api_documentation.cpp\nlibraries/wallet/doxygen\n\nprograms/cli_wallet/cli_wallet\nprograms/js_operation_serializer/js_operation_serializer\nprograms/witness_node/witness_node\n\ntests/app_test\ntests/chain_bench\ntests/chain_test\ntests/intense_test\ntests/performance_test\n\ndoxygen\n\nwallet.json\nwitness_node_data_dir\n\n*.wallet\n\nprograms/witness_node/object_database/*\n\nobject_database/*\n\n*.pyc\n*.pyo\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"docs\"]\n    path = docs\n    url = https://github.com/bitshares/bitshares-core.wiki.git\n    ignore = dirty\n[submodule \"libraries/fc\"]\n    path = libraries/fc\n    url = https://github.com/bitshares/bitshares-fc.git\n    ignore = dirty\n"
  },
  {
    "path": ".mailmap",
    "content": "Alfredo Garcia <oxarbitrage@gmail.com> <oxarbitrage@gmail.com>\nAlfredo Garcia <oxarbitrage@gmail.com> <root@NC-PH-1346-07.web-hosting.com>\nChristopher Sanborn <23085117+christophersanborn@users.noreply.github.com>\nChronos <chronos.crypto@gmail.com>\nDaniel Larimer <bytemaster@users.noreply.github.com> <dan.larimer@block.one>\nDaniel Larimer <bytemaster@users.noreply.github.com> <dan@bitshares.org>\nDaniel Larimer <bytemaster@users.noreply.github.com> <dan@invictus-innovations.com>\nDaniel Larimer <bytemaster@users.noreply.github.com> <dlarimer@invictus-innovations.com>\nEric Frias <efrias@syncad.com>\nFabian Schuh <Fabian@chainsquad.com> <mail@xeroc.org>\nFabian Schuh <Fabian@chainsquad.com> <xeroc@chainsquad.com>\nJohn M. Jones <jmjatlanta@gmail.com>\nKen Code <ken@BitShares-Munich.de>\nMatias Romeo <matias.romeo@gmail.com>\nNathan Hourt <themodprobe@protonmail.com> <nat.hourt@gmail.com>\nNathan Hourt <themodprobe@protonmail.com> <nathan@followmyvote.com>\nOpenLedger <service.github@openledger.info> <service.github@openledger.info>\nOpenLedger <service.github@openledger.info> <42674402+OpenLedgerApp@users.noreply.github.com>\nPeter Conrad <conrad@quisquis.de> <cyrano@quisquis.de>\nPeter Conrad <conrad@quisquis.de> <github.com@quisquis.de>\nQi Xing <cwyyprog@163.com> <cwyyprog@163.com>\nRoelandp <dnaleor@gmail.com>\nRyan R. Fox <ryan@ryanrfox.com> <ryan@ryanrfox.com>\nRyan R. Fox <ryan@ryanrfox.com> <ryanRfox@users.noreply.github.com>\nSigve Kvalsvik <bitsharesblocks@gmail.com> <sigvekvalsvik@gmail.com>\nValentine Zavgorodnev <i@valzav.com>\nValera Cogut <info@valeracogut.com> <valerakogut@gmail.com>\nVikram Rajkumar <vikramrajkumar@users.noreply.github.com> <vikram@bitshares.org>\nVikram Rajkumar <vikramrajkumar@users.noreply.github.com> <vikram@soledger.com>\nWilliam <tmfc@homtail.com> <jinwei@gmail.com>\nWilliam <tmfc@homtail.com> <tmfc@homtail.com>\nXiaodong Li <mantianyu@gmail.com> <cifer-lee@users.noreply.github.com>\nXiaodong Li <mantianyu@gmail.com> <mantianyu@gmail.com>\nXiaodong Li <mantianyu@gmail.com> <maintianyu@gmail.com>\nXiaodong Li <mantianyu@gmail.com> <lixiaodongcifer@didichuxing.com>\nabitmore <abitmore@users.noreply.github.com>\nalbert <393259066@qq.com> <393259066@qq.com>\nalbert <393259066@qq.com> <zhuliting@gxb.io>\nbitcube <root@seed.cubeconnex.com>\nbtcinshares <btcinshares@protonmail.com> <33876675+btcinshares@users.noreply.github.com>\ncrazybits <crazybit.github@gmail.com> <crazybit.github@gmail.com>\ncrazybits <crazybit.github@gmail.com> <crazybits@users.noreply.github.com>\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: c++\n\ncache:\n  ccache: true\n  directories:\n  - sonar_cache\n\ngit:\n  depth: 1\n\ndist: xenial\n\nsudo: true\n\ninstall:\n - sudo apt-get install --allow-unauthenticated libboost-thread-dev libboost-iostreams-dev libboost-date-time-dev libboost-system-dev libboost-filesystem-dev libboost-program-options-dev libboost-chrono-dev libboost-test-dev libboost-context-dev libboost-regex-dev libboost-coroutine-dev cmake parallel\n\naddons:\n  sonarcloud:\n    organization: \"flwyiq7go36p6lipr64tbesy5jayad3q\"\n    token:\n      secure: \"Ik4xQhs9imtsFIC1SMAPmdLId9lVadY/4PEgo5tM4M5cQRvyt4xeuMMV+CRIT6tGEEqF71ea74qVJTxT7qinWZ3kmHliFjbqDxk1FbjCpK6NGQDyTdfWMVJFIlk7WefvtGAwFBkf6pSTs553bKNNM0HbBYQGKe08waLwv7R+lOmVjTTKIRF/cCVw+C5QQZdXFnUMTg+mRuUqGk4WvNNPmcBfkX0ekHPrXwAD5ATVS1q0iloA0nzHq8CPNmPE+IyXdPw0EBp+fl3cL9MgrlwRbELxrnCKFy+ObdjhDj7z3FDIxDe+03gVlgd+6Fame+9EJCeeeNLF4G4qNR1sLEvHRqVz12/NYnRU9hQL0c/jJtiUquOJA5+HqrhhB9XUZjS1xbHV3aIU5PR0bdDP6MKatvIVwRhwxwhaDXh7VSimis8eL+LvXT7EO+rGjco0c17RuzZpFCsKmXCej4Q8iDBMdOIWwe2WuWi8zb6MFvnLyK2EcM53hAn2yMwU+nprbpHwzU5oJTFZLD+J78zCSGk7uu7vsF+EEnheMwfqafP9MpMEXGXaXZiq7QKy3KvxQTg+1ozPIu+fgxvY0xdyrjJHOSJlrvXN7osjD4IDTs6D5cLAZ04WGIKsulZDr7ZN5n3gmA9h4cfhJsIEia0uQzLmWnfF6RksxWElK1i1+xmse7E=\"\n\nenv:\n  global:\n    - CCACHE_COMPRESS=exists_means_true\n    - CCACHE_MAXSIZE=1Gi\n    - CCACHE_SLOPPINESS=include_file_ctime,include_file_mtime,time_macros\n\njobs:\n  include:\n    - stage: build for cache\n      script: ./programs/build_helpers/build_for_cache\n    - stage: build and test\n      script: ./programs/build_helpers/build_and_test\n    - stage: scan with sonar, step 1\n      script: ./programs/build_helpers/scan_with_sonar_step_1\n    - stage: scan with sonar, step 2\n      script: ./programs/build_helpers/scan_with_sonar_step_2\n    - stage: scan with sonar, step 3\n      script: ./programs/build_helpers/scan_with_sonar_step_3\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# Defines BitShares library target.\ncmake_minimum_required( VERSION 3.2 FATAL_ERROR )\nproject( BitShares LANGUAGES CXX C)\n\nset( BLOCKCHAIN_NAME \"BitShares\" )\n\nset( CLI_CLIENT_EXECUTABLE_NAME graphene_client )\nset( GUI_CLIENT_EXECUTABLE_NAME BitShares )\nset( CUSTOM_URL_SCHEME \"gcs\" )\nset( INSTALLER_APP_ID \"68ad7005-8eee-49c9-95ce-9eed97e5b347\" )\n\nset( CMAKE_CXX_STANDARD 14 )\nset( CMAKE_CXX_STANDARD_REQUIRED ON )\n\nif( \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\" OR\n    \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\" )\n  set( CMAKE_CXX_EXTENSIONS ON ) # for __int128 support\nelse()\n  set( CMAKE_CXX_EXTENSIONS OFF )\nendif()\n\n# http://stackoverflow.com/a/18369825\nif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)\n        message(FATAL_ERROR \"GCC version must be at least 4.8!\")\n    endif()\nelseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3)\n        message(FATAL_ERROR \"Clang version must be at least 3.3!\")\n    endif()\nelseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"MSVC\")\n    if (\"${CMAKE_CXX_COMPILER_VERSION}\" VERSION_LESS \"19.0\")\n      message(FATAL_ERROR \"MSVC version must be at least 19.0 (Visual Studio 2015 Update 1)!\")\n    endif()\n\n    # allow MSVC VS2015 with Update 1, other 2015 versions are not supported\n    if (\"${CMAKE_CXX_COMPILER_VERSION}\" VERSION_EQUAL \"19.0\")\n        if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL \"19.0.23506.0\")\n            message(FATAL_ERROR \"Your version ${CMAKE_CXX_COMPILER_VERSION} of MSVC is not supported, use version 19.0.23506.0 (Visual Studio 2015 Update 1)!\")\n        endif()\n    endif()\nendif()\n\nlist( APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules\" )\n\ninclude(CheckCCompilerFlag)\ninclude(Utils)\n\n# function to help with cUrl\nmacro(FIND_CURL)\n   if (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB)\n      find_package(OpenSSL REQUIRED)\n      set (OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})\n      set (CMAKE_FIND_LIBRARY_SUFFIXES .a)\n      find_package(CURL REQUIRED)\n      list(APPEND CURL_LIBRARIES ${OPENSSL_LIBRARIES} ${BOOST_THREAD_LIBRARY} ${CMAKE_DL_LIBS})\n      set (CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES})\n   else (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB)\n      find_package(CURL REQUIRED)\n   endif (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB)\n\n   if( WIN32 )\n     if ( MSVC )\n       list( APPEND CURL_LIBRARIES Wldap32 )\n     endif( MSVC )\n\n     if( MINGW )\n       # MinGW requires a specific order of included libraries ( CURL before ZLib )\n       find_package( ZLIB REQUIRED )\n       list( APPEND CURL_LIBRARIES ${ZLIB_LIBRARY} pthread )\n     endif( MINGW )\n\n     list( APPEND CURL_LIBRARIES ${PLATFORM_SPECIFIC_LIBS} )\n   endif( WIN32 )\nendmacro()\n\n# Save the old value of CMAKE_REQUIRED_FLAGS\nset( TEMP_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} )\n\n# Fortify source\nif (CMAKE_COMPILER_IS_GNUCXX)\n\tif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n\t\tmessage (STATUS \"Setting optimizations for clang++\")\n\t\tset(CMAKE_CXX_FLAGS_RELEASE \"-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1\")\n\t\tset(CMAKE_CXX_FLAGS_RELWITHDEBINFO \"-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1 -g\")\n\n\t\t# check and add data execution prevention\n\t\tmessage (STATUS \"Enabling data execution prevention\")\n\t\tadd_linker_flag(\"-fsanitize=safe-stack\")\n\n\t\t# check and add Stack-based buffer overrun detection\n\t\tset(CMAKE_REQUIRED_FLAGS \"-fstack-protector\")\n\t\tcheck_c_compiler_flag(\"\" HAVE_STACKPROTECTOR)\n\t\tif(HAVE_STACKPROTECTOR)\n\t\t\tmessage (STATUS \"Enabling stack-based buffer overrun detection\")\n\t\t\tadd_flag_append(CMAKE_C_FLAGS \"-fstack-protector\")\n\t\t\tadd_flag_append(CMAKE_CXX_FLAGS \"-fstack-protector\")\n\t\tendif()\n\telse ()\n\t\tmessage (STATUS \"Setting optimizations for g++\")\n\t\tset(CMAKE_CXX_FLAGS_RELEASE \"-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1\")\n\t\tset(CMAKE_CXX_FLAGS_RELWITHDEBINFO \"-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1 -g\")\n\n\t\t# check and add data execution prevention\n\t\tset(CMAKE_REQUIRED_FLAGS \"-Wl,-znoexecstack\")\n\t\tcheck_c_compiler_flag(\"\" HAVE_NOEXECSTACK)\n\t\tif(HAVE_NOEXECSTACK)\n\t\t\tmessage (STATUS \"Enabling data execution prevention\")\n\t\t\tadd_linker_flag(\"-znoexecstack\")\n\t\tendif()\n\n\t\t# check and add Stack-based buffer overrun detection\n\t\tset(CMAKE_REQUIRED_FLAGS \"-fstack-protector-strong\")\n\t\tcheck_c_compiler_flag(\"\" HAVE_STACKPROTECTOR)\n\t\tif(HAVE_STACKPROTECTOR)\n\t\t\tmessage (STATUS \"Enabling stack-based buffer overrun detection\")\n\t\t\tadd_flag_append(CMAKE_C_FLAGS \"-fstack-protector-strong\")\n\t\t\tadd_flag_append(CMAKE_CXX_FLAGS \"-fstack-protector-strong\")\n\t\tendif()\n\n\tendif ()\nendif ()\n\n# check for Data relocation and Protection (RELRO)\nset(CMAKE_REQUIRED_FLAGS \"-Wl,-zrelro,-znow\")\ncheck_c_compiler_flag(\"\" HAVE_RELROFULL)\nif(HAVE_RELROFULL)\n\tmessage (STATUS \"Enabling full data relocation and protection\")\n\tadd_linker_flag(\"-zrelro\")\n\tadd_linker_flag(\"-znow\")\nelse()\n\t#if full relro is not available, try partial relro\n\tset(CMAKE_REQUIRED_FLAGS \"-Wl,-zrelro\")\n\tcheck_c_compiler_flag(\"\" HAVE_RELROPARTIAL)\n\tif(HAVE_RELROPARTIAL)\n\t\tmessage (STATUS \"Enabling partial data relocation and protection\")\n\t\tadd_linker_flag(\"-zrelro\")\n\tendif()\nendif()\n\nset(CMAKE_REQUIRED_FLAGS ${TEMP_REQUIRED_FLAGS} )\n\n# position independent executetable (PIE)\n# position independent code (PIC)\nif (NOT MSVC)\n    add_definitions (-fPIC)\nendif(NOT MSVC)\n\nset(CMAKE_EXPORT_COMPILE_COMMANDS \"ON\")\nset( GRAPHENE_EGENESIS_JSON \"${CMAKE_CURRENT_SOURCE_DIR}/libraries/egenesis/genesis.json\"\n     CACHE STRING \"Path to embedded genesis file\" )\n\nif (USE_PCH)\n  include (cotire)\nendif(USE_PCH)\n\noption(USE_PROFILER \"Build with GPROF support(Linux).\" OFF)\n\n# Use Boost config file from fc\nset(Boost_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/CMakeModules/Boost\")\n\nlist( APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/GitVersionGen\" )\ninclude( GetGitRevisionDescription )\nget_git_head_revision( GIT_REFSPEC GIT_SHA2 )\n\nSET(BOOST_COMPONENTS)\nLIST(APPEND BOOST_COMPONENTS thread\n                             iostreams\n                             date_time\n                             system\n                             filesystem\n                             program_options\n                             chrono\n                             unit_test_framework\n                             context\n                             coroutine\n                             regex)\n# boost::endian is also required, but FindBoost can't handle header-only libs\nSET( Boost_USE_STATIC_LIBS ON CACHE STRING \"ON or OFF\" )\n\nIF(WIN32)\n   if($ENV{BOOST_ROOT})\n       SET(BOOST_ROOT $ENV{BOOST_ROOT})\n   endif($ENV{BOOST_ROOT})\n  set(Boost_USE_MULTITHREADED ON)\n  set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries\n  add_definitions(\"-DCURL_STATICLIB\")\n  list(APPEND PLATFORM_SPECIFIC_LIBS ws2_32 crypt32 mswsock userenv )\nELSE( WIN32 )\n   IF( APPLE )\n      set( CMAKE_THREAD_LIBS_INIT \"-lpthread\" )\n      set( CMAKE_HAVE_THREADS_LIBRARY 1 )\n      set( CMAKE_USE_WIN32_THREADS_INIT 0 )\n      set( CMAKE_USE_PTHREADS_INIT 1 )\n      set( THREADS_PREFER_PTHREAD_FLAG ON )\n   ENDIF( APPLE )\nENDIF(WIN32)\n\nFIND_PACKAGE(Boost CONFIG REQUIRED COMPONENTS ${BOOST_COMPONENTS})\n\n# enforce more strict compiler warnings and errors\nadd_compiler_flag_if_available(\"-Wall\")\nadd_compiler_flag_if_available(\"-Wclobbered\")\nadd_compiler_flag_if_available(\"-Wempty-body\")\nadd_compiler_flag_if_available(\"-Wformat-security\")\nadd_compiler_flag_if_available(\"-Wignored-qualifiers\")\nadd_compiler_flag_if_available(\"-Wimplicit-fallthrough=5\")\nadd_compiler_flag_if_available(\"-Wmissing-field-initializers\")\nadd_compiler_flag_if_available(\"-Wpointer-arith\")\nadd_compiler_flag_if_available(\"-Wshift-negative-value\")\nadd_compiler_flag_if_available(\"-Wtype-limits\")\nadd_compiler_flag_if_available(\"-Wunused-but-set-parameter\")\n\nif( WIN32 )\n\n    message( STATUS \"Configuring BitShares on WIN32\")\n\n    if ( MINGW )\n        message( STATUS \"Windows build using MinGW\" )\n        set( FULL_STATIC_BUILD TRUE )\n    else( MINGW )\n        set( ZLIB_LIBRARIES \"\" )\n    endif( MINGW )\n\n    SET( DEFAULT_EXECUTABLE_INSTALL_DIR bin/ )\n\n    if( MSVC )\n        add_definitions(-DWIN32_LEAN_AND_MEAN)\n        #looks like this flag can have different default on some machines.\n        SET(CMAKE_SHARED_LINKER_FLAGS \"${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO\")\n        SET(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO\")\n\n        # Probably cmake has a bug and vcxproj generated for executable in Debug conf. has disabled debug info\n        set(CMAKE_EXE_LINKER_FLAGS_DEBUG \"${CMAKE_EXE_LINKER_FLAGS_DEBUG} /DEBUG\")\n    endif ( MSVC )\n\nelse( WIN32 ) # Apple AND Linux\n\n    if( APPLE )\n        # Apple Specific Options Here\n        message( STATUS \"Configuring BitShares on OS X\" )\n        set( CMAKE_CXX_FLAGS \"${CMAKE_C_FLAGS} -stdlib=libc++ -Wall\" )\n    else( APPLE )\n        if ( \"${CMAKE_SYSTEM_NAME}\" STREQUAL \"OpenBSD\" )\n            # OpenBSD Specific Options\n            message( STATUS \"Configuring BitShares on OpenBSD\" )\n        else()\n            # Linux Specific Options Here\n            message( STATUS \"Configuring BitShares on Linux\" )\n            set( rt_library rt )\n        endif()\n        # Common Linux & OpenBSD Options\n        set( CMAKE_CXX_FLAGS \"${CMAKE_C_FLAGS} -Wall\" )\n        if(USE_PROFILER)\n            set( CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -pg\" )\n        endif( USE_PROFILER )\n        set( pthread_library pthread)\n        if ( NOT DEFINED crypto_library )\n          # I'm not sure why this is here, I guess someone has openssl and can't detect it with find_package()?\n          # if you have a normal install, you can define crypto_library to the empty string to avoid a build error\n          set( crypto_library crypto)\n        endif ()\n    endif( APPLE )\n\n    if( \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\" )\n        set( CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fno-builtin-memcmp\" )\n    elseif( \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\" )\n        if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )\n            set( CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization\" )\n        endif()\n    endif()\n\n    if( \"${CMAKE_GENERATOR}\" STREQUAL \"Ninja\" )\n        if( \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\" )\n            set( CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fcolor-diagnostics\" )\n        endif()\n    endif()\n\n    # based on http://www.delorie.com/gnu/docs/gdb/gdb_70.html\n    # uncomment this line to tell GDB about macros (slows compile times)\n    # set( CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-2 -g3\" )\n\nendif( WIN32 )\n\nif ( NOT MSVC AND FULL_STATIC_BUILD )\n    set( CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -static -static-libstdc++ -static-libgcc\" )\nendif ( NOT MSVC AND FULL_STATIC_BUILD )\n\nset(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL \"Build BitShares for code coverage analysis\")\n\nif(ENABLE_COVERAGE_TESTING)\n    SET(CMAKE_CXX_FLAGS \"--coverage ${CMAKE_CXX_FLAGS}\")\nendif()\n\nadd_subdirectory( libraries )\nadd_subdirectory( programs )\nadd_subdirectory( tests )\n\n\nif (ENABLE_INSTALLER)\n\nset(VERSION_MAJOR 0)\nset(VERSION_MINOR 1)\nset(VERSION_PATCH 0)\n\n\ninclude(InstallRequiredSystemLibraries)\n\nset(CPACK_OUTPUT_FILE_PREFIX ${CMAKE_BINARY_DIR}/packages)\nset(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install)\n\nSET(CPACK_PACKAGE_DIRECTORY \"${CMAKE_INSTALL_PREFIX}\")\nset(CPACK_PACKAGE_NAME \"graphene\")\nset(CPACK_PACKAGE_VENDOR \"Cryptonomex, Inc.\")\nset(CPACK_PACKAGE_VERSION_MAJOR \"${VERSION_MAJOR}\")\nset(CPACK_PACKAGE_VERSION_MINOR \"${VERSION_MINOR}\")\nset(CPACK_PACKAGE_VERSION_PATCH \"${VERSION_PATCH}\")\nset(CPACK_PACKAGE_VERSION \"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}\")\nset(CPACK_PACKAGE_DESCRIPTION \"A client for the BitShares network\")\nset(CPACK_PACKAGE_DESCRIPTION_SUMMARY \"A client for the BitShares network\")\nset(CPACK_RESOURCE_FILE_LICENSE \"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md\")\nset(CPACK_PACKAGE_INSTALL_DIRECTORY \"BitShares ${CPACK_PACKAGE_VERSION}\")\n\nif(WIN32)\n SET(CPACK_GENERATOR \"ZIP;NSIS\")\n set(CPACK_PACKAGE_NAME \"BitShares\") # override above\n set(CPACK_NSIS_EXECUTABLES_DIRECTORY .)\n set(CPACK_NSIS_PACKAGE_NAME \"BitShares v${CPACK_PACKAGE_VERSION}\")\n set(CPACK_NSIS_DISPLAY_NAME \"${CPACK_NSIS_PACKAGE_NAME}\")\n set(CPACK_NSIS_DEFINES \"  !define MUI_STARTMENUPAGE_DEFAULTFOLDER \\\\\\\"BitShares\\\\\\\"\")\n # it seems like windows zip files usually don't have a single directory inside them, unix tgz frequently do\n SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)\n\nendif(WIN32)\n\nif(APPLE)\n  set(CPACK_GENERATOR \"DragNDrop\")\nendif()\n\nif(LINUX)\n  # Linux gets a .tgz\n  SET(CPACK_GENERATOR \"TGZ\")\n  SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 1)\nendif(LINUX)\n\n include(CPack)\nendif(ENABLE_INSTALLER)\n\nMESSAGE( STATUS \"\" )\nMESSAGE( STATUS \"PROFILER: ${USE_PROFILER}\" )\nMESSAGE( STATUS \"\" )\n"
  },
  {
    "path": "CONTRIBUTORS.txt",
    "content": "Contributors to this repository, in descending order by number of commits. Update with\n  head -5 CONTRIBUTORS.txt >contrib.tmp && git shortlog -s -e -n | cut -f 2 >>contrib.tmp && mv contrib.tmp CONTRIBUTORS.txt\n\n==============================================================================\n\nabitmore <abitmore@users.noreply.github.com>\nAlfredo Garcia <oxarbitrage@gmail.com>\ntheoreticalbts <theoreticalbts@users.noreply.github.com>\nJohn M. Jones <jmjatlanta@gmail.com>\nPeter Conrad <conrad@quisquis.de>\nDaniel Larimer <bytemaster@users.noreply.github.com>\nNathan Hourt <themodprobe@protonmail.com>\nVikram Rajkumar <vikramrajkumar@users.noreply.github.com>\nEric Frias <efrias@syncad.com>\nValera Cogut <info@valeracogut.com>\nMichel Santos <MichelSantos@users.noreply.github.com>\nXiaodong Li <mantianyu@gmail.com>\nFabian Schuh <Fabian@chainsquad.com>\nmanikey123 <mansiimohan@gmail.com>\nChristopher Sanborn <23085117+christophersanborn@users.noreply.github.com>\ncrypto-ape <43807588+crypto-ape@users.noreply.github.com>\nlubos.ilcik <lubos.ilcik@touch4it.com>\nMatias Romeo <matias.romeo@gmail.com>\nOpenLedger <service.github@openledger.info>\nSigve Kvalsvik <bitsharesblocks@gmail.com>\nalbert <393259066@qq.com>\nRyan R. Fox <ryan@ryanrfox.com>\nValentine Zavgorodnev <i@valzav.com>\nMichael Vandeberg <vandeberg@cryptonomex.com>\nJames Calfee <james@jcalfee.info>\nAlexey Frolov <alexey.frolov@aetsoft.by>\ntakaaki7 <nakama67006700@gmail.com>\nNicolas Wack <wackou@gmail.com>\nTaconator <TheTaconator@users.noreply.github.com>\nQi Xing <cwyyprog@163.com>\nAnton Autushka <a.autushka@aetsoft.by>\nChronos <chronos.crypto@gmail.com>\nWei Yang <richard.weiyang@gmail.com>\nZapata <marco.tessari@gmail.com>\nbtcinshares <btcinshares@protonmail.com>\ncrazybits <crazybit.github@gmail.com>\nAnzhy Cherrnyavski <a.chernyavski@pixelplex.io>\nTiago Peralta <tperalta82@gmail.com>\nioBanker <37595908+ioBanker@users.noreply.github.com>\nMichael Vandeberg <vandeberg@steemit.com>\nSahkanDesertHawk <panasiuki@gmail.com>\nScott Howard <showard314@gmail.com>\nTydus <Tydus@Tydus.org>\nWilliam <tmfc@homtail.com>\nd.yakovitsky <d.yakovitsky@aetsoft.by>\nddylko <ddylko@ddylkoPC>\niHashFury <iPerky@users.noreply.github.com>\nnecklace <necklace@163.com>\nxuquan316 <xuquan316@vip.qq.com>\nBartek Wrona <wrona@syncad.com>\nBhuzOr <giaquinta.adriano@gmail.com>\nBlockchain Projects BV <info@blockchainprojectsbv.com>\nBruce Steedman <MatzFan@users.noreply.github.com>\nCharles Cooper <cooper.charles.m@gmail.com>\nErkan Yilmaz <erkan77@gmail.com>\nHaruka Ma <mrx@hcc.im>\nJaewoo Cho <clayop@gmail.com>\nJose Marcial Vieira Bisneto <marcial.vieirab@gmail.com>\nJozef Knaperek <jknaperek@gmail.com>\nKarl Semich <0xloem@gmail.com>\nKen Code <ken@BitShares-Munich.de>\nKrzysztof Szumny <krzysztof.szumny@stxnext.pl>\nPaul Brossier <piem@piem.org>\nRoelandp <dnaleor@gmail.com>\nThomas Freedman <thom@ozarkholler.com>\nTroglodactyl <troglodactyl@gmail.com>\nVoR0220 <catalanor0220@gmail.com>\nalt <pch957@163.com>\nbangzi1001 <36911788+bangzi1001@users.noreply.github.com>\nbitcube <root@seed.cubeconnex.com>\nlafona <lafona@protonmail.com>\nliondani <liondani@gmx.com>\nlososeg <ya.lososeg@gmail.com>\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM phusion/baseimage:0.11\nMAINTAINER The bitshares decentralized organisation\n\nENV LANG=en_US.UTF-8\nRUN \\\n    apt-get update -y && \\\n    apt-get install -y \\\n      g++ \\\n      autoconf \\\n      cmake \\\n      git \\\n      libbz2-dev \\\n      libcurl4-openssl-dev \\\n      libssl-dev \\\n      libncurses-dev \\\n      libboost-thread-dev \\\n      libboost-iostreams-dev \\\n      libboost-date-time-dev \\\n      libboost-system-dev \\\n      libboost-filesystem-dev \\\n      libboost-program-options-dev \\\n      libboost-chrono-dev \\\n      libboost-test-dev \\\n      libboost-context-dev \\\n      libboost-regex-dev \\\n      libboost-coroutine-dev \\\n      libtool \\\n      doxygen \\\n      ca-certificates \\\n      fish \\\n    && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\n\nADD . /bitshares-core\nWORKDIR /bitshares-core\n\n# Compile\nRUN \\\n    ( git submodule sync --recursive || \\\n      find `pwd`  -type f -name .git | \\\n\twhile read f; do \\\n\t  rel=\"$(echo \"${f#$PWD/}\" | sed 's=[^/]*/=../=g')\"; \\\n\t  sed -i \"s=: .*/.git/=: $rel/=\" \"$f\"; \\\n\tdone && \\\n      git submodule sync --recursive ) && \\\n    git submodule update --init --recursive && \\\n    cmake \\\n        -DCMAKE_BUILD_TYPE=Release \\\n\t-DGRAPHENE_DISABLE_UNITY_BUILD=ON \\\n        . && \\\n    make witness_node cli_wallet get_dev_key && \\\n    install -s programs/witness_node/witness_node programs/genesis_util/get_dev_key programs/cli_wallet/cli_wallet /usr/local/bin && \\\n    #\n    # Obtain version\n    mkdir /etc/bitshares && \\\n    git rev-parse --short HEAD > /etc/bitshares/version && \\\n    cd / && \\\n    rm -rf /bitshares-core\n\n# Home directory $HOME\nWORKDIR /\nRUN useradd -s /bin/bash -m -d /var/lib/bitshares bitshares\nENV HOME /var/lib/bitshares\nRUN chown bitshares:bitshares -R /var/lib/bitshares\n\n# Volume\nVOLUME [\"/var/lib/bitshares\", \"/etc/bitshares\"]\n\n# rpc service:\nEXPOSE 8090\n# p2p service:\nEXPOSE 1776\n\n# default exec/config files\nADD docker/default_config.ini /etc/bitshares/config.ini\nADD docker/default_logging.ini /etc/bitshares/logging.ini\nADD docker/bitsharesentry.sh /usr/local/bin/bitsharesentry.sh\nRUN chmod a+x /usr/local/bin/bitsharesentry.sh\n\n# Make Docker send SIGINT instead of SIGTERM to the daemon\nSTOPSIGNAL SIGINT\n\n# default execute entry\nCMD [\"/usr/local/bin/bitsharesentry.sh\"]\n"
  },
  {
    "path": "Doxyfile",
    "content": "# Doxyfile 1.8.9.1\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all text\n# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv\n# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv\n# for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"BitShares-Core\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = \"4.0.0\"\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          = \"BitShares blockchain implementation and command-line interface software\"\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = doxygen\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines.\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:\n# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:\n# Fortran. In the later case the parser tries to guess whether the code is fixed\n# or free formatted code, this is the default for Fortran type files), VHDL. For\n# instance to make doxygen treat .inc files as Fortran files (default is PHP),\n# and .f files as C (default is Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See http://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces.\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = README.md doc/main.dox libraries/chain libraries/db libraries/app libraries/wallet libraries/protocol libraries/net libraries/plugins libraries/fc libraries/utilities libraries/egenesis\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: http://www.gnu.org/software/libiconv) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank the\n# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,\n# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,\n# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,\n# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,\n# *.qsf, *.as and *.js.\n\nFILE_PATTERNS          = *.cpp *.hpp *.hxx\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                = libraries/fc/vendor/editline libraries/fc/vendor/secp256k1-zkp libraries/fc/vendor/websocketpp\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        = boost\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE = \"README.md\"\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = YES\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# function all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see http://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the config file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# http://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: http://developer.apple.com/tools/xcode/), introduced with\n# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html\n# for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# http://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from http://www.mathjax.org before deployment.\n# The default value is: http://cdn.mathjax.org/mathjax/latest.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = YES\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when enabling USE_PDFLATEX this option is only used for generating\n# bitmaps for formulas in the HTML output, but not in the Makefile that is\n# written to the output directory.\n# The default file is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. To get the times font for\n# instance you can specify\n# EXTRA_PACKAGES=times\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# http://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's config\n# file, i.e. a series of assignments. You only have to provide replacements,\n# missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's config file. A template extensions file can be generated\n# using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sf.net) file that captures the\n# structure of the code including all documentation. Note that this feature is\n# still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot.\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif and svg.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2015-2016 Cryptonomex Inc. <contact@cryptonomex.com>\nCopyright (c) 2015-2020 contributors, see CONTRIBUTORS.txt\n\nThe MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README-docker.md",
    "content": "# Docker Container\n\nThis repository comes with built-in Dockerfile to support docker\ncontainers. This README serves as documentation.\n\n## Dockerfile Specifications\n\nThe `Dockerfile` performs the following steps:\n\n1. Obtain base image (phusion/baseimage:0.10.1)\n2. Install required dependencies using `apt-get`\n3. Add bitshares-core source code into container\n4. Update git submodules\n5. Perform `cmake` with build type `Release`\n6. Run `make` and `make_install` (this will install binaries into `/usr/local/bin`\n7. Purge source code off the container\n8. Add a local bitshares user and set `$HOME` to `/var/lib/bitshares`\n9. Make `/var/lib/bitshares` and `/etc/bitshares` a docker *volume*\n10. Expose ports `8090` and `1776`\n11. Add default config from `docker/default_config.ini` and\n    `docker/default_logging.ini`\n12. Add an entry point script\n13. Run the entry point script by default\n\nThe entry point simplifies the use of parameters for the `witness_node`\n(which is run by default when spinning up the container).\n\n### Supported Environmental Variables\n\n* `$BITSHARESD_SEED_NODES`\n* `$BITSHARESD_RPC_ENDPOINT`\n* `$BITSHARESD_PLUGINS`\n* `$BITSHARESD_REPLAY`\n* `$BITSHARESD_RESYNC`\n* `$BITSHARESD_P2P_ENDPOINT`\n* `$BITSHARESD_WITNESS_ID`\n* `$BITSHARESD_PRIVATE_KEY`\n* `$BITSHARESD_TRACK_ACCOUNTS`\n* `$BITSHARESD_PARTIAL_OPERATIONS`\n* `$BITSHARESD_MAX_OPS_PER_ACCOUNT`\n* `$BITSHARESD_ES_NODE_URL`\n* `$BITSHARESD_TRUSTED_NODE`\n\n### Default config\n\nThe default configuration is:\n\n    p2p-endpoint = 0.0.0.0:1776\n    rpc-endpoint = 0.0.0.0:8090\n    bucket-size = [60,300,900,1800,3600,14400,86400]\n    history-per-size = 1000\n    max-ops-per-account = 100\n    partial-operations = true\n\n# Docker Compose\n\nWith docker compose, multiple nodes can be managed with a single\n`docker-compose.yaml` file:\n\n    version: '3'\n    services:\n     main:\n      # Image to run\n      image: bitshares/bitshares-core:latest\n      # \n      volumes:\n       - ./docker/conf/:/etc/bitshares/\n      # Optional parameters\n      environment:\n       - BITSHARESD_ARGS=--help\n\n\n    version: '3'\n    services:\n     fullnode:\n      # Image to run\n      image: bitshares/bitshares-core:latest\n      environment:\n      # Optional parameters\n      environment:\n       - BITSHARESD_ARGS=--help\n      ports:\n       - \"0.0.0.0:8090:8090\"\n      volumes:\n      - \"bitshares-fullnode:/var/lib/bitshares\"\n\n\n# Docker Hub\n\nThis container is properly registered with docker hub under the name:\n\n* [bitshares/bitshares-core](https://hub.docker.com/r/bitshares/bitshares-core/)\n\nGoing forward, every release tag as well as all pushes to `develop` and\n`testnet` will be built into ready-to-run containers, there.\n\n# Docker Compose\n\nOne can use docker compose to setup a trusted full node together with a\ndelayed node like this:\n\n```\nversion: '3'\nservices:\n\n fullnode:\n  image: bitshares/bitshares-core:latest\n  ports:\n   - \"0.0.0.0:8090:8090\"\n  volumes:\n  - \"bitshares-fullnode:/var/lib/bitshares\"\n\n delayed_node:\n  image: bitshares/bitshares-core:latest\n  environment:\n   - 'BITSHARESD_PLUGINS=delayed_node witness'\n   - 'BITSHARESD_TRUSTED_NODE=ws://fullnode:8090'\n  ports:\n   - \"0.0.0.0:8091:8090\"\n  volumes:\n  - \"bitshares-delayed_node:/var/lib/bitshares\"\n  links: \n  - fullnode\n\nvolumes:\n bitshares-fullnode:\n```\n"
  },
  {
    "path": "README.md",
    "content": "NewBitShares Core\n==============\n\n\n\n* [Getting Started](#getting-started)\n* [Support](#support)\n* [Using the API](#using-the-api)\n* [Accessing restrictable node API's](#accessing-restrictable-node-apis)\n* [FAQ](#faq)\n* [License](#license)\n\nNew BitShares Core is the BitShares blockchain implementation and command-line interface.\nThe web browser based wallet is [BitShares UI](https://github.com/bitshares-cnvote/newbitshares-ui).\n\nVisit [nbs.plus](https://nbs.plus/) to learn about NewBitShares and join the community .\n\nInformation for developers can be found in the [BitShares Developer Portal](https://dev.bitshares.works/). Users interested in how BitShares works can go to the [BitShares Documentation](https://how.bitshares.works/) site.\n\n\nGetting Started\n----------------\nBuild instructions and additional documentation are available in the\n[Wiki](https://github.com/bitshares-cnvote/newbitshares-core/wiki).\n\n### Build\n\nWe recommend building on Ubuntu 18.04 LTS (64-bit) \n\n**Build Dependencies:**\n\n    sudo apt-get update\n    sudo apt-get install autoconf cmake make automake libtool git libboost-all-dev libssl-dev g++ libcurl4-openssl-dev doxygen\n\n**Build Script:**\n\n    git clone https://github.com/bitshares-cnvote/newbitshares-core.git\n    cd newbitshares-core\n    git checkout master # may substitute \"master\" with current release tag\n    git submodule update --init --recursive\n    mkdir build\n    cd build\n    cmake -DCMAKE_BUILD_TYPE=Release ..\n    make\n\n**Upgrade Script:** (prepend to the Build Script above if you built a prior release):\n\n    git remote set-url origin https://github.com/bitshares-cnvote/newbitshares-core.git\n    git checkout master\n    git remote set-head origin --auto\n    git pull\n    git submodule update --init --recursive # this command may fail\n    git submodule sync --recursive\n    git submodule update --init --recursive\n\n**NOTE:**\n\n* NewBitShares requires a 64-bit operating system to build, and will not build on a 32-bit OS. Tested operating systems:\n  * Linux (heavily tested with Ubuntu 18.04 LTS and Ubuntu 16.04 LTS)\n  * macOS (various versions)\n  * Windows (various versions, Visual Studio and MinGW)\n  * OpenBSD (various versions)\n\n* NewBitShares requires [Boost](http://www.boost.org/) libraries to build, supports version `1.58` to `1.69`.\nNewer versions may work, but have not been tested.\nIf your system came pre-installed with a version of Boost libraries that you do not wish to use, you may\nmanually build your preferred version and use it with NewBitShares by specifying it on the CMake command line.\n\n  Example: `cmake -DBOOST_ROOT=/path/to/boost ..`\n\n* NewBitShares requires [OpenSSL](https://www.openssl.org/) libraries to build, supports version `1.0.2` to `1.1.1`.\nIf your system came pre-installed with a version of OpenSSL libraries that you do not wish to use, you may\nmanually build your preferred version and use it with BitShares by specifying it on the CMake command line.\n\n  Example: `cmake -DOPENSSL_ROOT_DIR=/path/to/openssl ..`\n\n\n### Run the node software\n\n**After Building**, the node software `witness_node` can be launched with:\n\n    ./programs/witness_node/witness_node\n\nThe node will automatically create a `witness_node_data_dir` directory with some config files.\nThe blockchain data will be stored in the directory too.\nIt may take several hours to fully synchronize the blockchain.\n\nYou can exit the node using `Ctrl+C`. Please be aware that the node may need some time (usually a few minutes) to exit cleanly, please be patient.\n\n**IMPORTANT:** By default the node will start in reduced memory mode by using some of the commands detailed in [Memory reduction for nodes](https://github.com/bitshares/bitshares-core/wiki/Memory-reduction-for-nodes).\nIn order to run a full node with all the account histories (which is usually not necessary) you need to remove `partial-operations` and `max-ops-per-account` from your config file. Please note that currently(2018-10-17) a full node will need more than 160GB of RAM to operate and required memory is growing fast. Consider the following table as **minimal requirements** before running a node:\n\n| Default | Full | Minimal  | ElasticSearch\n| --- | --- | --- | ---\n| 150G HDD, 16G RAM | 640G SSD, 64G RAM * | 120G HDD, 4G RAM | 1TB SSD, 32G RAM\n\n\\* For this setup, allocate at least 500GB of SSD as swap.\n\nTo use the command-line wallet or other wallets / clients with the node, the node need to be started with RPC connection enabled, which can be done by starting the node with the `--rpc-endpoint` parameter, E.G.\n\n    ./programs/witness_node/witness_node --rpc-endpoint=127.0.0.1:8090\n\nor configure it in the config file by editing `witness_node_data_dir/config.ini` as follows:\n\n    rpc-endpoint = 127.0.0.1:8090\n\nYou can run the program with `--help` parameter to see more info:\n\n    ./programs/witness_node/witness_node --help\n\n\n### Run the command-line wallet software\n\nTo start the command-line wallet, in a separate terminal you can run:\n\n    ./programs/cli_wallet/cli_wallet\n\n**IMPORTANT:** The cli_wallet or API interfaces to the witness node wouldn't be fully functional unless the witness node is fully synchronized with the blockchain. The cli_wallet command `info` will show result `head_block_age` which will tell you how far you are from the live current block of the blockchain.\n\nTo check your current block:\n\n    new >>> info\n\nTo query the blockchain, E.G. get info about an account:\n\n    new >>> get_account <account_name_or_id>\n\nIf you need to transact with your account but not only query, firstly set your initial password and unlock the wallet:\n\n* For non-Windows operating systems, you can type the commands and press `[ENTER]`, then input the password and press `[ENTER]`, in this case the password won't show:\n\n      new >>> set_password [ENTER]\n      Enter password:\n      locked >>> unlock [ENTER]\n      Enter password:\n      unlocked >>>\n\n* For Windows, or you'd like to show the password, type the commands with the password:\n\n      new >>> set_password <PASSWORD>\n      locked >>> unlock <PASSWORD>\n      unlocked >>>\n\nTo be able to transact with your account, import the corresponding private keys:\n\n    unlocked >>> import_key <ACCOUNT_NAME> <WIF_KEY>\n\nThe private keys will be encrypted and stored in the wallet file, the file name is `wallet.json` by default.\nThe private keys are accessible when the wallet is unlocked.\n\n    unlocked >>> dump_private_keys\n\nUse `lock` command to make the private keys inaccessible. There is no auto-lock feature so far.\n\n    unlocked >>> lock\n\nTo import your initial (genesis) balances, import the private keys corresponding to the balances:\n\n    unlocked >>> import_balance <ACCOUNT_NAME> [<WIF_KEY> ...] true\n\nUse `help` to see all available wallet commands.\n\n    >>> help\n\nUse `gethelp <COMMAND>` to see more info about individual commands. E.G.\n\n    >>> gethelp get_order_book\n\nThe definition of all commands is available in the \n[wallet.hpp](https://github.com/bitshares/bitshares-core/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp) souce code file.\nCorresponding documentation can be found in the [Doxygen documentation](https://doxygen.bitshares.org/classgraphene_1_1wallet_1_1wallet__api.html).\n\nYou can run the program with `--help` parameter to see more info:\n\n    ./programs/cli_wallet/cli_wallet --help\n\nThere is also some info in the [Wiki](https://github.com/bitshares/bitshares-core/wiki/CLI-Wallet-Cookbook).\n\n\nSupport\n-------\n\nTechnical support is available in the [BitSharesTalk technical support subforum](https://bitsharestalk.org/index.php?board=45.0).\n\nNewBitShares Core bugs can be reported directly to the [issue tracker](https://github.com/bitshares-cnovte/newbitshares-core/issues).\n\nNewBitShares UI bugs should be reported to the [UI issue tracker](https://github.com/bitshares-cnvote/newbitshares-ui/issues).\n\nUp to date online Doxygen documentation can be found at [Doxygen.BitShares.org](https://doxygen.bitshares.org/hierarchy.html).\n\n\nUsing the API\n-------------\n\n### Node API\n\nThe `witness_node` software provides several different API's, known as *node API*.\n\nEach API has its own ID and a name.\nWhen running `witness_node` with RPC connection enabled, initially two API's are available:\n* API 0 has name *\"database\"*, it provides read-only access to the database,\n* API 1 has name *\"login\"*, it is used to login and gain access to additional, restrictable API's.\n\nHere is an example using `wscat` package from `npm` for websockets:\n\n    $ npm install -g wscat\n    $ wscat -c ws://127.0.0.1:8090\n    > {\"id\":1, \"method\":\"call\", \"params\":[0,\"get_accounts\",[[\"1.2.0\"]]]}\n    < {\"id\":1,\"result\":[{\"id\":\"1.2.0\",\"annotations\":[],\"membership_expiration_date\":\"1969-12-31T23:59:59\",\"registrar\":\"1.2.0\",\"referrer\":\"1.2.0\",\"lifetime_referrer\":\"1.2.0\",\"network_fee_percentage\":2000,\"lifetime_referrer_fee_percentage\":8000,\"referrer_rewards_percentage\":0,\"name\":\"committee-account\",\"owner\":{\"weight_threshold\":1,\"account_auths\":[],\"key_auths\":[],\"address_auths\":[]},\"active\":{\"weight_threshold\":6,\"account_auths\":[[\"1.2.5\",1],[\"1.2.6\",1],[\"1.2.7\",1],[\"1.2.8\",1],[\"1.2.9\",1],[\"1.2.10\",1],[\"1.2.11\",1],[\"1.2.12\",1],[\"1.2.13\",1],[\"1.2.14\",1]],\"key_auths\":[],\"address_auths\":[]},\"options\":{\"memo_key\":\"GPH1111111111111111111111111111111114T1Anm\",\"voting_account\":\"1.2.0\",\"num_witness\":0,\"num_committee\":0,\"votes\":[],\"extensions\":[]},\"statistics\":\"2.7.0\",\"whitelisting_accounts\":[],\"blacklisting_accounts\":[]}]}\n\nWe can do the same thing using an HTTP client such as `curl` for API's which do not require login or other session state:\n\n    $ curl --data '{\"jsonrpc\": \"2.0\", \"method\": \"call\", \"params\": [0, \"get_accounts\", [[\"1.2.0\"]]], \"id\": 1}' http://127.0.0.1:8090/\n    {\"id\":1,\"result\":[{\"id\":\"1.2.0\",\"annotations\":[],\"membership_expiration_date\":\"1969-12-31T23:59:59\",\"registrar\":\"1.2.0\",\"referrer\":\"1.2.0\",\"lifetime_referrer\":\"1.2.0\",\"network_fee_percentage\":2000,\"lifetime_referrer_fee_percentage\":8000,\"referrer_rewards_percentage\":0,\"name\":\"committee-account\",\"owner\":{\"weight_threshold\":1,\"account_auths\":[],\"key_auths\":[],\"address_auths\":[]},\"active\":{\"weight_threshold\":6,\"account_auths\":[[\"1.2.5\",1],[\"1.2.6\",1],[\"1.2.7\",1],[\"1.2.8\",1],[\"1.2.9\",1],[\"1.2.10\",1],[\"1.2.11\",1],[\"1.2.12\",1],[\"1.2.13\",1],[\"1.2.14\",1]],\"key_auths\":[],\"address_auths\":[]},\"options\":{\"memo_key\":\"GPH1111111111111111111111111111111114T1Anm\",\"voting_account\":\"1.2.0\",\"num_witness\":0,\"num_committee\":0,\"votes\":[],\"extensions\":[]},\"statistics\":\"2.7.0\",\"whitelisting_accounts\":[],\"blacklisting_accounts\":[]}]}\n\nWhen using an HTTP client, the API ID can be replaced by the API name, E.G.\n\n    $ curl --data '{\"jsonrpc\": \"2.0\", \"method\": \"call\", \"params\": [\"database\", \"get_accounts\", [[\"1.2.0\"]]], \"id\": 1}' http://127.0.0.1:8090/\n\nThe definition of all node API's is available in the source code files including\n[database_api.hpp](https://github.com/bitshares/bitshares-core/blob/master/libraries/app/include/graphene/app/database_api.hpp)\nand [api.hpp](https://github.com/bitshares/bitshares-core/blob/master/libraries/app/include/graphene/app/api.hpp).\nCorresponding documentation can be found in Doxygen:\n* [database API](https://doxygen.bitshares.org/classgraphene_1_1app_1_1database__api.html)\n* [other API's](https://doxygen.bitshares.org/namespacegraphene_1_1app.html)\n\n\n### Wallet API\n\nThe `cli_wallet` program can also be configured to serve **all of its commands** as API's, known as *wallet API*.\n\nStart `cli_wallet` with RPC connection enabled:\n\n    $ ./programs/cli_wallet/cli_wallet --rpc-endpoint=127.0.0.8091\n\nAccess the wallet API using an HTTP client:\n\n    $ curl --data '{\"jsonrpc\": \"2.0\", \"method\": \"info\", \"params\": [], \"id\": 1}' http://127.0.0.1:8091/rpc\n    $ curl --data '{\"jsonrpc\": \"2.0\", \"method\": \"get_account\", \"params\": [\"1.2.0\"], \"id\": 1}' http://127.0.0.1:8091/rpc\n\nNote: The syntax to access wallet API is a bit different than accessing node API.\n\n**Important:**\n* When RPC connection is enabled for `cli_wallet`, sensitive data E.G. private keys which is accessible via commands will be accessible via RPC too. It is recommended that only open network connection to localhost or trusted addresses E.G. configure a firewall.\n* When using wallet API, sensitive data E.G. the wallet password and private keys is transmitted as plain text, thus may be vulnerable to network sniffing. It is recommended that only use wallet API with localhost, or in a clean network, and / or use `--rpc-tls-endpoint` parameter to only serve wallet API via secure connections.\n\n\nAccessing restrictable node API's\n---------------------------------\n\nYou can restrict node API's to particular users by specifying an `api-access` file in `config.ini`\nor by using the `--api-access /full/path/to/api-access.json` startup node command.  Here is an example `api-access` file which allows\nuser `bytemaster` with password `supersecret` to access four different API's, while allowing any other user to access the three public API's\nnecessary to use the node:\n\n    {\n       \"permission_map\" :\n       [\n          [\n             \"bytemaster\",\n             {\n                \"password_hash_b64\" : \"9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=\",\n                \"password_salt_b64\" : \"INDdM6iCi/8=\",\n                \"allowed_apis\" : [\"database_api\", \"network_broadcast_api\", \"history_api\", \"network_node_api\"]\n             }\n          ],\n          [\n             \"*\",\n             {\n                \"password_hash_b64\" : \"*\",\n                \"password_salt_b64\" : \"*\",\n                \"allowed_apis\" : [\"database_api\", \"network_broadcast_api\", \"history_api\"]\n             }\n          ]\n       ]\n    }\n\nPasswords are stored in `base64` as salted `sha256` hashes.  A simple Python script,\n[`saltpass.py`](https://github.com/bitshares/bitshares-core/blob/master/programs/witness_node/saltpass.py)\nis avaliable to obtain hash and salt values from a password.\nA single asterisk `\"*\"` may be specified as username or password hash to accept any value.\n\nWith the above configuration, here is an example of how to call `add_node` from the `network_node` API:\n\n    {\"id\":1, \"method\":\"call\", \"params\":[1,\"login\",[\"bytemaster\", \"supersecret\"]]}\n    {\"id\":2, \"method\":\"call\", \"params\":[1,\"network_node\",[]]}\n    {\"id\":3, \"method\":\"call\", \"params\":[2,\"add_node\",[\"127.0.0.1:9090\"]]}\n\nNote, the call to `network_node` is necessary to obtain the correct API identifier for the network API.  It is not guaranteed that the network API identifier will always be `2`.\n\nThe restricted API's are accessible via HTTP too using *basic access authentication*. E.G.\n\n    $ curl --data '{\"jsonrpc\": \"2.0\", \"method\": \"call\", \"params\": [\"network_node\", \"add_node\", [\"127.0.0.1:9090\"]], \"id\": 1}' http://bytemaster:supersecret@127.0.0.1:8090/\n\nOur `doxygen` documentation contains the most up-to-date information\nabout API's for the [node](https://doxygen.bitshares.org/namespacegraphene_1_1app.html) and the\n[wallet](https://doxygen.bitshares.org/classgraphene_1_1wallet_1_1wallet__api.html).\n\n\nFAQ\n---\n\n- Is there a way to generate help with parameter names and method descriptions?\n\n    Yes. Documentation of the code base, including APIs, can be generated using Doxygen. Simply run `doxygen` in this directory.\n\n    If both Doxygen and perl are available in your build environment, the CLI wallet's `help` and `gethelp`\n    commands will display help generated from the doxygen documentation.\n\n    If your CLI wallet's `help` command displays descriptions without parameter names like\n        `signed_transaction transfer(string, string, string, string, string, bool)`\n    it means CMake was unable to find Doxygen or perl during configuration.  If found, the\n    output should look like this:\n        `signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast)`\n\n- Is there a way to allow external program to drive `cli_wallet` via websocket, JSONRPC, or HTTP?\n\n    Yes. External programs may connect to the CLI wallet and make its calls over a websockets API. To do this, run the wallet in\n    server mode, i.e. `cli_wallet -s \"127.0.0.1:9999\"` and then have the external program connect to it over the specified port\n    (in this example, port 9999). Please check the [\"Using the API\"](#using-the-api) section for more info.\n\n- Is there a way to access methods which require login over HTTP?\n\n    Yes. Most of the methods can be accessed by specifying the API name instead of an API ID. If an API is protected by a username and a password, it can be accessed by using *basic access authentication*. Please check the [\"Accessing restrictable node API's\"](#accessing-restrictable-node-apis) section for more info.\n    \n    However, HTTP is not really designed for \"server push\" notifications, and we would have to figure out a way to queue notifications for a polling client. Websockets solves this problem. If you need to access the stateful methods, use Websockets.\n\n- What is the meaning of `a.b.c` numbers?\n\n    The first number specifies the *space*.  Space 1 is for protocol objects, 2 is for implementation objects.\n    Protocol space objects can appear on the wire, for example in the binary form of transactions.\n    Implementation space objects cannot appear on the wire and solely exist for implementation\n    purposes, such as optimization or internal bookkeeping.\n\n    The second number specifies the *type*.  The type of the object determines what fields it has.  For a\n    complete list of type ID's, see `GRAPHENE_DEFINE_IDS(protocol, protocol_ids ...)` in\n    [protocol/types.hpp](https://github.com/bitshares/bitshares-core/blob/master/libraries/protocol/include/graphene/protocol/types.hpp)\n    and `GRAPHENE_DEFINE_IDS(chain, implementation_ids ...)` in [chain/types.hpp](https://github.com/bitshares/bitshares-core/blob/master/libraries/chain/include/graphene/chain/types.hpp).\n\n    The third number specifies the *instance*.  The instance of the object is different for each individual\n    object.\n\n- The answer to the previous question was really confusing.  Can you make it clearer?\n\n    All account ID's are of the form `1.2.x`.  If you were the 9735th account to be registered,\n    your account's ID will be `1.2.9735`.  Account `0` is special (it's the \"committee account,\"\n    which is controlled by the committee members and has a few abilities and restrictions other accounts\n    do not).\n\n    All asset ID's are of the form `1.3.x`.  If you were the 29th asset to be registered,\n    your asset's ID will be `1.3.29`.  Asset `0` is special (it's BTS, which is considered the \"core asset\").\n\n    The first and second number together identify the kind of thing you're talking about (`1.2` for accounts,\n    `1.3` for assets).  The third number identifies the particular thing.\n\n- How do I get the `network_add_nodes` command to work?  Why is it so complicated?\n\n    You need to follow the instructions in the [\"Accessing restrictable node API's\"](#accessing-restrictable-node-apis) section to\n    allow a username/password access to the `network_node` API.  Then you need\n    to pass the username/password to the `cli_wallet` on the command line.\n\n    It's set up this way so that the default configuration is secure even if the RPC port is\n    publicly accessible.  It's fine if your `witness_node` allows the general public to query\n    the database or broadcast transactions (in fact, this is how the hosted web UI works).  It's\n    less fine if your `witness_node` allows the general public to control which p2p nodes it's\n    connecting to.  Therefore the API to add p2p connections needs to be set up with proper access\n    controls.\n \nLicense\n-------\nNewBitShares Core is under the MIT license. See [LICENSE](https://github.com/bitshares-cnvote/newbitshares-core/blob/master/LICENSE.txt)\nfor more information.\n"
  },
  {
    "path": "docker/bitsharesentry.sh",
    "content": "#!/bin/bash\nBITSHARESD=\"/usr/local/bin/witness_node\"\n\n# For blockchain download\nVERSION=`cat /etc/bitshares/version`\n\n## Supported Environmental Variables\n#\n#   * $BITSHARESD_SEED_NODES\n#   * $BITSHARESD_RPC_ENDPOINT\n#   * $BITSHARESD_PLUGINS\n#   * $BITSHARESD_REPLAY\n#   * $BITSHARESD_RESYNC\n#   * $BITSHARESD_P2P_ENDPOINT\n#   * $BITSHARESD_WITNESS_ID\n#   * $BITSHARESD_PRIVATE_KEY\n#   * $BITSHARESD_TRACK_ACCOUNTS\n#   * $BITSHARESD_PARTIAL_OPERATIONS\n#   * $BITSHARESD_MAX_OPS_PER_ACCOUNT\n#   * $BITSHARESD_ES_NODE_URL\n#   * $BITSHARESD_ES_START_AFTER_BLOCK\n#   * $BITSHARESD_TRUSTED_NODE\n#\n\nARGS=\"\"\n# Translate environmental variables\nif [[ ! -z \"$BITSHARESD_SEED_NODES\" ]]; then\n    for NODE in $BITSHARESD_SEED_NODES ; do\n        ARGS+=\" --seed-node=$NODE\"\n    done\nfi\nif [[ ! -z \"$BITSHARESD_RPC_ENDPOINT\" ]]; then\n    ARGS+=\" --rpc-endpoint=${BITSHARESD_RPC_ENDPOINT}\"\nfi\n\nif [[ ! -z \"$BITSHARESD_REPLAY\" ]]; then\n    ARGS+=\" --replay-blockchain\"\nfi\n\nif [[ ! -z \"$BITSHARESD_RESYNC\" ]]; then\n    ARGS+=\" --resync-blockchain\"\nfi\n\nif [[ ! -z \"$BITSHARESD_P2P_ENDPOINT\" ]]; then\n    ARGS+=\" --p2p-endpoint=${BITSHARESD_P2P_ENDPOINT}\"\nfi\n\nif [[ ! -z \"$BITSHARESD_WITNESS_ID\" ]]; then\n    ARGS+=\" --witness-id=$BITSHARESD_WITNESS_ID\"\nfi\n\nif [[ ! -z \"$BITSHARESD_PRIVATE_KEY\" ]]; then\n    ARGS+=\" --private-key=$BITSHARESD_PRIVATE_KEY\"\nfi\n\nif [[ ! -z \"$BITSHARESD_TRACK_ACCOUNTS\" ]]; then\n    for ACCOUNT in $BITSHARESD_TRACK_ACCOUNTS ; do\n        ARGS+=\" --track-account=$ACCOUNT\"\n    done\nfi\n\nif [[ ! -z \"$BITSHARESD_PARTIAL_OPERATIONS\" ]]; then\n    ARGS+=\" --partial-operations=${BITSHARESD_PARTIAL_OPERATIONS}\"\nfi\n\nif [[ ! -z \"$BITSHARESD_MAX_OPS_PER_ACCOUNT\" ]]; then\n    ARGS+=\" --max-ops-per-account=${BITSHARESD_MAX_OPS_PER_ACCOUNT}\"\nfi\n\nif [[ ! -z \"$BITSHARESD_ES_NODE_URL\" ]]; then\n    ARGS+=\" --elasticsearch-node-url=${BITSHARESD_ES_NODE_URL}\"\nfi\n\nif [[ ! -z \"$BITSHARESD_ES_START_AFTER_BLOCK\" ]]; then\n    ARGS+=\" --elasticsearch-start-es-after-block=${BITSHARESD_ES_START_AFTER_BLOCK}\"\nfi\n\nif [[ ! -z \"$BITSHARESD_TRUSTED_NODE\" ]]; then\n    ARGS+=\" --trusted-node=${BITSHARESD_TRUSTED_NODE}\"\nfi\n\n## Link the bitshares config file into home\n## This link has been created in Dockerfile, already\nln -f -s /etc/bitshares/config.ini /var/lib/bitshares\nln -f -s /etc/bitshares/logging.ini /var/lib/bitshares\n\n# Plugins need to be provided in a space-separated list, which\n# makes it necessary to write it like this\nif [[ ! -z \"$BITSHARESD_PLUGINS\" ]]; then\n   exec \"$BITSHARESD\" --data-dir \"${HOME}\" ${ARGS} ${BITSHARESD_ARGS} --plugins \"${BITSHARESD_PLUGINS}\"\nelse\n   exec \"$BITSHARESD\" --data-dir \"${HOME}\" ${ARGS} ${BITSHARESD_ARGS}\nfi\n"
  },
  {
    "path": "docker/default_config.ini",
    "content": "# Endpoint for P2P node to listen on\np2p-endpoint = 0.0.0.0:1776\n\n# P2P nodes to connect to on startup (may specify multiple times)\n# seed-node = \n\n# JSON array of P2P nodes to connect to on startup\n# seed-nodes = \n\n# Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.\n# checkpoint = \n\n# Endpoint for websocket RPC to listen on\nrpc-endpoint = 0.0.0.0:8090\n\n# Endpoint for TLS websocket RPC to listen on\n# rpc-tls-endpoint = \n\n# The TLS certificate file for this server\n# server-pem = \n\n# Password for this certificate\n# server-pem-password = \n\n# File to read Genesis State from\n# genesis-json = \n\n# Block signing key to use for init witnesses, overrides genesis file\n# dbg-init-key = \n\n# JSON file specifying API permissions\n# api-access = \n\n# Number of IO threads, default to 0 for auto-configuration\n# io-threads =\n\n# Whether allow API clients to subscribe to universal object creation and removal events\n# enable-subscribe-to-all =\n\n# Whether to enable tracking of votes of standby witnesses and committee members. Set it to true to provide accurate data to API clients, set to false for slightly better performance.\n# enable-standby-votes-tracking =\n\n# For history_api::get_account_history_operations to set max limit value\n# api-limit-get-account-history-operations = 100\n\n# For history_api::get_account_history to set max limit value\n# api-limit-get-account-history = 100\n\n# For orders_api::get_grouped_limit_orders to set max limit value\n# api-limit-get-grouped-limit-orders = 101\n\n# For history_api::get_relative_account_history to set max limit value\n# api-limit-get-relative-account-history = 100\n\n# For history_api::get_account_history_by_operations to set max limit value\n# api-limit-get-account-history-by-operations = 100\n\n# For asset_api::get_asset_holders to set max limit value\n# api-limit-get-asset-holders = 100\n\n# For database_api_impl::get_key_references to set max limit value\n# api-limit-get-key-references = 100\n\n# For database_api_impl::get_htlc_by_from and get_htlc_by_to to set max limit value\n# api-limit-get-htlc-by = 100\n\n# For database_api_impl::get_full_accounts to set max accounts to query at once\n# api-limit-get-full-accounts = 50\n\n# For database_api_impl::get_full_accounts to set max items to return in the lists\n# api-limit-get-full-accounts-lists = 500\n\n# For database_api_impl::get_call_orders and get_call_orders_by_account to set max limit value\n# api-limit-get-call-orders = 300\n\n# For database_api_impl::get_settle_orders and get_settle_orders_by_account to set max limit value\n# api-limit-get-settle-orders = 300\n\n# For database_api_impl::list_assets and get_assets_by_issuer to set max limit value\n# api-limit-get-assets = 101\n\n# For database_api_impl::get_limit_orders to set max limit value\n# api-limit-get-limit-orders = 300\n\n# For database_api_impl::get_limit_orders_by_account to set max limit value\n# api-limit-get-limit-orders-by-account = 101\n\n# For database_api_impl::get_order_book to set max limit value\n# api-limit-get-order-book = 50\n\n# For database_api_impl::lookup_accounts to set max limit value\n# api-limit-lookup-accounts = 1000\n\n# For database_api_impl::lookup_witness_accounts to set max limit value\n# api-limit-lookup-witness-accounts = 1000\n\n# For database_api_impl::lookup_committee_member_accounts to set max limit value\n# api-limit-lookup-committee-member-accounts = 1000\n\n# For database_api_impl::lookup_vote_ids to set max limit value\n# api-limit-lookup-vote-ids = 1000\n\n# For database_api_impl::get_account_limit_orders to set max limit value\n# api-limit-get-account-limit-orders = 101\n\n# For database_api_impl::get_collateral_bids to set max limit value\n# api-limit-get-collateral-bids = 100\n\n# For database_api_impl::get_top_markets to set max limit value\n# api-limit-get-top-markets = 100\n\n# For database_api_impl::get_trade_history to set max limit value\n# api-limit-get-trade-history = 100\n\n# For database_api_impl::get_trade_history_by_sequence to set max limit value\n# api-limit-get-trade-history-by-sequence = 100\n\n# For database_api_impl::get_withdraw_permissions_by_giver to set max limit value\n# api-limit-get-withdraw-permissions-by-giver = 101\n\n# For database_api_impl::get_withdraw_permissions_by_recipient to set max limit value\n# api-limit-get-withdraw-permissions-by-recipient = 101\n\n# Space-separated list of plugins to activate\nplugins = witness account_history market_history grouped_orders api_helper_indexes custom_operations\n\n# Do not exit if api_helper_indexes plugin is not enabled.\n# ignore-api-helper-indexes-warning = true\n\n\n# ==============================================================================\n# witness plugin options\n# ==============================================================================\n\n# Enable block production, even if the chain is stale.\nenable-stale-production = false\n\n# Percent of witnesses (0-100) that must be participating in order to produce blocks\n# required-participation = 33\n\n# ID of witness controlled by this node (e.g. \"1.6.5\", quotes are required, may specify multiple times)\n# witness-id = \n\n# Tuple of [PublicKey, WIF private key] (may specify multiple times)\n# private-key = [\"BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\",\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"]\n\n# Path to a file containing tuples of [PublicKey, WIF private key]. The file has to contain exactly one tuple (i.e. private - public key pair) per line. This option may be specified multiple times, thus multiple files can be provided.\n# private-key-file =\n\n\n# ==============================================================================\n# debug_witness plugin options\n# ==============================================================================\n\n# Tuple of [PublicKey, WIF private key] (may specify multiple times)\n# debug-private-key = [\"BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\",\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"]\n\n\n# ==============================================================================\n# account_history plugin options\n# ==============================================================================\n\n# Account ID to track history for (may specify multiple times)\n# track-account = \n\n# Keep only those operations in memory that are related to account history tracking\npartial-operations = true\n\n# Maximum number of operations per account will be kept in memory\nmax-ops-per-account = 100\n\n\n# ==============================================================================\n# elasticsearch plugin options\n# ==============================================================================\n\n# Elastic Search database node url(http://localhost:9200/)\n# elasticsearch-node-url =\n\n# Number of bulk documents to index on replay(10000)\n# elasticsearch-bulk-replay =\n\n# Number of bulk documents to index on a syncronied chain(100)\n# elasticsearch-bulk-sync =\n\n# Use visitor to index additional data(slows down the replay(false))\n# elasticsearch-visitor =\n\n# Pass basic auth to elasticsearch database('')\n# elasticsearch-basic-auth =\n\n# Add a prefix to the index(bitshares-)\n# elasticsearch-index-prefix =\n\n# Save operation as object(false)\n# elasticsearch-operation-object =\n\n# Start doing ES job after block(0)\n# elasticsearch-start-es-after-block =\n\n# Save operation as string. Needed to serve history api calls(true)\n# elasticsearch-operation-string =\n\n# Mode of operation: only_save(0), only_query(1), all(2) - Default: 0\n# elasticsearch-mode =\n\n\n# ==============================================================================\n# market_history plugin options\n# ==============================================================================\n\n# Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers\n# bucket-size = [15,60,300,3600,86400]\nbucket-size = [60,300,900,1800,3600,14400,86400]\n# for 1 min, 5 mins, 30 mins, 1h, 4 hs and 1 day. i think this should be the default.\n# https://github.com/bitshares/bitshares-core/issues/465\n\n# How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)\nhistory-per-size = 1000\n\n# Will only store this amount of matched orders for each market in order history for querying, or those meet the other option, which has more data (default: 1000)\nmax-order-his-records-per-market = 1000\n\n# Will only store matched orders in last X seconds for each market in order history for querying, or those meet the other option, which has more data (default: 259200 (3 days))\nmax-order-his-seconds-per-market = 259200\n\n\n# ==============================================================================\n# delayed_node plugin options\n# ==============================================================================\n\n# RPC endpoint of a trusted validating node (required for delayed_node)\n# trusted-node =\n\n\n# ==============================================================================\n# snapshot plugin options\n# ==============================================================================\n\n# Block number after which to do a snapshot\n# snapshot-at-block =\n\n# Block time (ISO format) after which to do a snapshot\n# snapshot-at-time =\n\n# Pathname of JSON file where to store the snapshot\n# snapshot-to =\n\n\n# ==============================================================================\n# es_objects plugin options\n# ==============================================================================\n\n# Elasticsearch node url(http://localhost:9200/)\n# es-objects-elasticsearch-url =\n\n# Basic auth username:password('')\n# es-objects-auth =\n\n# Number of bulk documents to index on replay(10000)\n# es-objects-bulk-replay =\n\n# Number of bulk documents to index on a synchronized chain(100)\n# es-objects-bulk-sync =\n\n# Store proposal objects(true)\n# es-objects-proposals =\n\n# Store account objects(true)\n# es-objects-accounts =\n\n# Store asset objects(true)\n# es-objects-assets =\n\n# Store balances objects(true)\n# es-objects-balances =\n\n# Store limit order objects(true)\n# es-objects-limit-orders =\n\n# Store feed data(true)\n# es-objects-asset-bitasset =\n\n# Add a prefix to the index(objects-)\n# es-objects-index-prefix =\n\n# Keep only current state of the objects(true)\n# es-objects-keep-only-current =\n\n# Start doing ES job after block(0)\n# es-objects-start-es-after-block =\n\n\n# ==============================================================================\n# grouped_orders plugin options\n# ==============================================================================\n\n# Group orders by percentage increase on price. Specify a JSON array of numbers here, each number is a group, number 1 means 0.01%.\n# tracked-groups = [10,100]\n\n\n# ==============================================================================\n# logging options\n# ==============================================================================\n#\n# Logging configuration is loaded from logging.ini by default.\n# If logging.ini exists, logging configuration added in this file will be ignored.\n"
  },
  {
    "path": "docker/default_logging.ini",
    "content": "# declare an appender named \"stderr\" that writes messages to the console\n[log.console_appender.stderr]\nstream=std_error\n\n# declare an appender named \"default\" that writes messages to default.log\n[log.file_appender.default]\n# filename can be absolute or relative to this config file\nfilename=logs/default/default.log\n# Rotate log every ? minutes, if leave out default to 60\nrotation_interval=60\n# how long will logs be kept (in days), if leave out default to 1\nrotation_limit=7\n\n# declare an appender named \"p2p\" that writes messages to p2p.log\n[log.file_appender.p2p]\n# filename can be absolute or relative to this config file\nfilename=logs/p2p/p2p.log\n# Rotate log every ? minutes, if leave out default to 60\nrotation_interval=60\n# how long will logs be kept (in days), if leave out default to 1\nrotation_limit=7\n\n# declare an appender named \"rpc\" that writes messages to rpc.log\n[log.file_appender.rpc]\n# filename can be absolute or relative to this config file\nfilename=logs/rpc/rpc.log\n# Rotate log every ? minutes, if leave out default to 60\nrotation_interval=60\n# how long will logs be kept (in days), if leave out default to 1\nrotation_limit=7\n\n# route any messages logged to the default logger to the \"stderr\" appender and\n# \"default\" appender we declared above, if they are info level or higher\n[logger.default]\nlevel=info\nappenders=stderr,default\n\n# route messages sent to the \"p2p\" logger to the \"p2p\" appender declared above\n[logger.p2p]\nlevel=warn\nappenders=p2p\n\n# route messages sent to the \"rpc\" logger to the \"rpc\" appender declared above\n[logger.rpc]\nlevel=error\nappenders=rpc\n\n"
  },
  {
    "path": "libraries/CMakeLists.txt",
    "content": "add_subdirectory( fc )\nadd_subdirectory( db )\nadd_subdirectory( chain )\nadd_subdirectory( egenesis )\nadd_subdirectory( net )\nadd_subdirectory( utilities )\nadd_subdirectory( app )\nadd_subdirectory( plugins )\nadd_subdirectory( wallet )\nadd_subdirectory( protocol )\n"
  },
  {
    "path": "libraries/README.md",
    "content": "# BitShares Libraries\n\nThe libraries are the core of the project and defines everything where applications can build on top.\n\nA **graphene** blockchain software will use the `app` library to define what the application will do, what services it will offer. The blockchain is defined by the `chain` library and include all the objects, types, operations, protocols that builds current consensus blockchain. The lowest level in memory database of Bitshares is developed at the `db` library. The `fc` is a helper module broadly used in the libraries code, `egenesis` will help with the genesis file, `plugins` will be loaded optionally to the application. Wallet software like the cli_wallet will benefit from the `wallet` library.\n\nCode in libraries is the most important part of **bitshares-core** project and it is maintained by the Bitshares Core Team and contributors.\n# Available Libraries\n\nFolder | Name | Description | Status\n---|---|---|---\n[app](app) | Application | Bundles component libraries (chain, network, plugins) into a useful application. Also provides API access. | Active \n[chain](chain) | Blockchain | Blockchain implementation and business logic. Database structure in the form of objects and updates to the blockchain in the form of evaluators are implemented here. | Active \n[db](db) | Database | Defines the internal database graphene uses. | Active \n[egenesis](egenesis) | Genesis | Hardcodes the `genesis.json` file into the `witness_node` executable.| Active\n[fc](fc) | Fast-compiling C++ library | https://github.com/bitshares/bitshares-fc | Active \n[net](net) | Network | The graphene p2p layer. | Active \n[plugins](plugins) | Plugins | Collection of singleton designed modules used for extending the bitshares-core.  | Active \n[protocol](protocol) | Protocol | Fundamental structure of the data that will be transmitted on the wire. Operations are defined and basic data integrity checks are done for each.  | Active \n[utilities](utilities) | Utilities | Common utility calls used in applications or other libraries. | Active \n[wallet](wallet) | Wallet | Wallet definition for the `cli_wallet` software. | Active\n"
  },
  {
    "path": "libraries/app/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/app/*.hpp\")\nfile(GLOB EGENESIS_HEADERS \"../egenesis/include/graphene/app/*.hpp\")\n\nadd_library( graphene_app \n             api.cpp\n             api_objects.cpp\n             application.cpp\n             util.cpp\n             database_api.cpp\n             plugin.cpp\n             config_util.cpp\n             ${HEADERS}\n             ${EGENESIS_HEADERS}\n           )\n\n# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246\ntarget_link_libraries( graphene_app\n                       graphene_market_history graphene_account_history graphene_elasticsearch graphene_grouped_orders\n                       graphene_api_helper_indexes graphene_custom_operations\n                       graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness )\ntarget_include_directories( graphene_app\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\"\n                            \"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include\" )\n\nif(MSVC)\n  set_source_files_properties( application.cpp api.cpp database_api.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\nINSTALL( TARGETS\n   graphene_app\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/app\" )\n"
  },
  {
    "path": "libraries/app/api.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <cctype>\n\n#include <graphene/app/api.hpp>\n#include <graphene/app/api_access.hpp>\n#include <graphene/app/application.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/get_config.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/transaction_history_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n\n#include <fc/crypto/base64.hpp>\n#include <fc/crypto/hex.hpp>\n#include <fc/rpc/api_connection.hpp>\n#include <fc/thread/future.hpp>\n\ntemplate class fc::api<graphene::app::block_api>;\ntemplate class fc::api<graphene::app::network_broadcast_api>;\ntemplate class fc::api<graphene::app::network_node_api>;\ntemplate class fc::api<graphene::app::history_api>;\ntemplate class fc::api<graphene::app::crypto_api>;\ntemplate class fc::api<graphene::app::asset_api>;\ntemplate class fc::api<graphene::app::orders_api>;\ntemplate class fc::api<graphene::app::custom_operations_api>;\ntemplate class fc::api<graphene::debug_witness::debug_api>;\ntemplate class fc::api<graphene::app::login_api>;\n\n\nnamespace graphene { namespace app {\n\n    login_api::login_api(application& a)\n    :_app(a)\n    {\n    }\n\n    login_api::~login_api()\n    {\n    }\n\n    bool login_api::login(const string& user, const string& password)\n    {\n       optional< api_access_info > acc = _app.get_api_access_info( user );\n       if( !acc.valid() )\n          return false;\n       if( acc->password_hash_b64 != \"*\" )\n       {\n          std::string password_salt = fc::base64_decode( acc->password_salt_b64 );\n          std::string acc_password_hash = fc::base64_decode( acc->password_hash_b64 );\n\n          fc::sha256 hash_obj = fc::sha256::hash( password + password_salt );\n          if( hash_obj.data_size() != acc_password_hash.length() )\n             return false;\n          if( memcmp( hash_obj.data(), acc_password_hash.c_str(), hash_obj.data_size() ) != 0 )\n             return false;\n       }\n\n       for( const std::string& api_name : acc->allowed_apis )\n          enable_api( api_name );\n       return true;\n    }\n\n    void login_api::enable_api( const std::string& api_name )\n    {\n       if( api_name == \"database_api\" )\n       {\n          _database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ), &( _app.get_options() ) );\n       }\n       else if( api_name == \"block_api\" )\n       {\n          _block_api = std::make_shared< block_api >( std::ref( *_app.chain_database() ) );\n       }\n       else if( api_name == \"network_broadcast_api\" )\n       {\n          _network_broadcast_api = std::make_shared< network_broadcast_api >( std::ref( _app ) );\n       }\n       else if( api_name == \"history_api\" )\n       {\n          _history_api = std::make_shared< history_api >( _app );\n       }\n       else if( api_name == \"network_node_api\" )\n       {\n          _network_node_api = std::make_shared< network_node_api >( std::ref(_app) );\n       }\n       else if( api_name == \"crypto_api\" )\n       {\n          _crypto_api = std::make_shared< crypto_api >();\n       }\n       else if( api_name == \"asset_api\" )\n       {\n          _asset_api = std::make_shared< asset_api >( _app );\n       }\n       else if( api_name == \"orders_api\" )\n       {\n          _orders_api = std::make_shared< orders_api >( std::ref( _app ) );\n       }\n       else if( api_name == \"custom_operations_api\" )\n       {\n          if( _app.get_plugin( \"custom_operations\" ) )\n             _custom_operations_api = std::make_shared< custom_operations_api >( std::ref( _app ) );\n       }\n       else if( api_name == \"debug_api\" )\n       {\n          // can only enable this API if the plugin was loaded\n          if( _app.get_plugin( \"debug_witness\" ) )\n             _debug_api = std::make_shared< graphene::debug_witness::debug_api >( std::ref(_app) );\n       }\n       return;\n    }\n\n    // block_api\n    block_api::block_api(graphene::chain::database& db) : _db(db) { }\n    block_api::~block_api() { }\n\n    vector<optional<signed_block>> block_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to)const\n    {\n       FC_ASSERT( block_num_to >= block_num_from );\n       vector<optional<signed_block>> res;\n       for(uint32_t block_num=block_num_from; block_num<=block_num_to; block_num++) {\n          res.push_back(_db.fetch_block_by_number(block_num));\n       }\n       return res;\n    }\n\n    network_broadcast_api::network_broadcast_api(application& a):_app(a)\n    {\n       _applied_block_connection = _app.chain_database()->applied_block.connect([this](const signed_block& b){ on_applied_block(b); });\n    }\n\n    void network_broadcast_api::on_applied_block( const signed_block& b )\n    {\n       if( _callbacks.size() )\n       {\n          /// we need to ensure the database_api is not deleted for the life of the async operation\n          auto capture_this = shared_from_this();\n          for( uint32_t trx_num = 0; trx_num < b.transactions.size(); ++trx_num )\n          {\n             const auto& trx = b.transactions[trx_num];\n             auto id = trx.id();\n             auto itr = _callbacks.find(id);\n             if( itr != _callbacks.end() )\n             {\n                auto block_num = b.block_num();\n                auto& callback = _callbacks.find(id)->second;\n                auto v = fc::variant( transaction_confirmation{ id, block_num, trx_num, trx }, GRAPHENE_MAX_NESTED_OBJECTS );\n                fc::async( [capture_this,v,callback]() {\n                   callback(v);\n                } );\n             }\n          }\n       }\n    }\n\n    void network_broadcast_api::broadcast_transaction(const precomputable_transaction& trx)\n    {\n       _app.chain_database()->precompute_parallel( trx ).wait();\n       _app.chain_database()->push_transaction(trx);\n       if( _app.p2p_node() != nullptr )\n          _app.p2p_node()->broadcast_transaction(trx);\n    }\n\n    fc::variant network_broadcast_api::broadcast_transaction_synchronous(const precomputable_transaction& trx)\n    {\n       fc::promise<fc::variant>::ptr prom = fc::promise<fc::variant>::create();\n       broadcast_transaction_with_callback( [prom]( const fc::variant& v ){\n        prom->set_value(v);\n       }, trx );\n\n       return fc::future<fc::variant>(prom).wait();\n    }\n\n    void network_broadcast_api::broadcast_block( const signed_block& b )\n    {\n       _app.chain_database()->precompute_parallel( b ).wait();\n       _app.chain_database()->push_block(b);\n       if( _app.p2p_node() != nullptr )\n          _app.p2p_node()->broadcast( net::block_message( b ));\n    }\n\n    void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const precomputable_transaction& trx)\n    {\n       _app.chain_database()->precompute_parallel( trx ).wait();\n       _callbacks[trx.id()] = cb;\n       _app.chain_database()->push_transaction(trx);\n       if( _app.p2p_node() != nullptr )\n          _app.p2p_node()->broadcast_transaction(trx);\n    }\n\n    network_node_api::network_node_api( application& a ) : _app( a )\n    {\n    }\n\n    fc::variant_object network_node_api::get_info() const\n    {\n       fc::mutable_variant_object result = _app.p2p_node()->network_get_info();\n       result[\"connection_count\"] = _app.p2p_node()->get_connection_count();\n       return result;\n    }\n\n    void network_node_api::add_node(const fc::ip::endpoint& ep)\n    {\n       _app.p2p_node()->add_node(ep);\n    }\n\n    std::vector<net::peer_status> network_node_api::get_connected_peers() const\n    {\n       return _app.p2p_node()->get_connected_peers();\n    }\n\n    std::vector<net::potential_peer_record> network_node_api::get_potential_peers() const\n    {\n       return _app.p2p_node()->get_potential_peers();\n    }\n\n    fc::variant_object network_node_api::get_advanced_node_parameters() const\n    {\n       return _app.p2p_node()->get_advanced_node_parameters();\n    }\n\n    void network_node_api::set_advanced_node_parameters(const fc::variant_object& params)\n    {\n       return _app.p2p_node()->set_advanced_node_parameters(params);\n    }\n\n    fc::api<network_broadcast_api> login_api::network_broadcast()const\n    {\n       FC_ASSERT(_network_broadcast_api);\n       return *_network_broadcast_api;\n    }\n\n    fc::api<block_api> login_api::block()const\n    {\n       FC_ASSERT(_block_api);\n       return *_block_api;\n    }\n\n    fc::api<network_node_api> login_api::network_node()const\n    {\n       FC_ASSERT(_network_node_api);\n       return *_network_node_api;\n    }\n\n    fc::api<database_api> login_api::database()const\n    {\n       FC_ASSERT(_database_api);\n       return *_database_api;\n    }\n\n    fc::api<history_api> login_api::history() const\n    {\n       FC_ASSERT(_history_api);\n       return *_history_api;\n    }\n\n    fc::api<crypto_api> login_api::crypto() const\n    {\n       FC_ASSERT(_crypto_api);\n       return *_crypto_api;\n    }\n\n    fc::api<asset_api> login_api::asset() const\n    {\n       FC_ASSERT(_asset_api);\n       return *_asset_api;\n    }\n\n    fc::api<orders_api> login_api::orders() const\n    {\n       FC_ASSERT(_orders_api);\n       return *_orders_api;\n    }\n\n    fc::api<graphene::debug_witness::debug_api> login_api::debug() const\n    {\n       FC_ASSERT(_debug_api);\n       return *_debug_api;\n    }\n\n    fc::api<custom_operations_api> login_api::custom_operations() const\n    {\n       FC_ASSERT(_custom_operations_api);\n       return *_custom_operations_api;\n    }\n\n    vector<order_history_object> history_api::get_fill_order_history( std::string asset_a, std::string asset_b, uint32_t limit  )const\n    {\n       FC_ASSERT(_app.chain_database());\n       const auto& db = *_app.chain_database();\n       asset_id_type a = database_api.get_asset_id_from_string( asset_a );\n       asset_id_type b = database_api.get_asset_id_from_string( asset_b );\n       if( a > b ) std::swap(a,b);\n       const auto& history_idx = db.get_index_type<graphene::market_history::history_index>().indices().get<by_key>();\n       history_key hkey;\n       hkey.base = a;\n       hkey.quote = b;\n       hkey.sequence = std::numeric_limits<int64_t>::min();\n\n       uint32_t count = 0;\n       auto itr = history_idx.lower_bound( hkey );\n       vector<order_history_object> result;\n       while( itr != history_idx.end() && count < limit)\n       {\n          if( itr->key.base != a || itr->key.quote != b ) break;\n          result.push_back( *itr );\n          ++itr;\n          ++count;\n       }\n\n       return result;\n    }\n\n    vector<operation_history_object> history_api::get_account_history( const std::string account_id_or_name,\n                                                                       operation_history_id_type stop,\n                                                                       unsigned limit,\n                                                                       operation_history_id_type start ) const\n    {\n       FC_ASSERT( _app.chain_database() );\n       const auto& db = *_app.chain_database();\n\n       const auto configured_limit = _app.get_options().api_limit_get_account_history;\n       FC_ASSERT( limit <= configured_limit,\n                  \"limit can not be greater than ${configured_limit}\",\n                  (\"configured_limit\", configured_limit) );\n\n       vector<operation_history_object> result;\n       account_id_type account;\n       try {\n          account = database_api.get_account_id_from_string(account_id_or_name);\n          const account_transaction_history_object& node = account(db).statistics(db).most_recent_op(db);\n          if(start == operation_history_id_type() || start.instance.value > node.operation_id.instance.value)\n             start = node.operation_id;\n       } catch(...) { return result; }\n\n       if(_app.is_plugin_enabled(\"elasticsearch\")) {\n          auto es = _app.get_plugin<elasticsearch::elasticsearch_plugin>(\"elasticsearch\");\n          if(es.get()->get_running_mode() != elasticsearch::mode::only_save) {\n             if(!_app.elasticsearch_thread)\n                _app.elasticsearch_thread= std::make_shared<fc::thread>(\"elasticsearch\");\n\n             return _app.elasticsearch_thread->async([&es, &account, &stop, &limit, &start]() {\n                return es->get_account_history(account, stop, limit, start);\n             }, \"thread invoke for method \" BOOST_PP_STRINGIZE(method_name)).wait();\n          }\n       }\n\n       const auto& hist_idx = db.get_index_type<account_transaction_history_index>();\n       const auto& by_op_idx = hist_idx.indices().get<by_op>();\n       auto index_start = by_op_idx.begin();\n       auto itr = by_op_idx.lower_bound(boost::make_tuple(account, start));\n\n       while(itr != index_start && itr->account == account && itr->operation_id.instance.value > stop.instance.value && result.size() < limit)\n       {\n          if(itr->operation_id.instance.value <= start.instance.value)\n             result.push_back(itr->operation_id(db));\n          --itr;\n       }\n       if(stop.instance.value == 0 && result.size() < limit && itr->account == account) {\n         result.push_back(itr->operation_id(db));\n       }\n\n       return result;\n    }\n\n    vector<operation_history_object> history_api::get_account_history_operations( const std::string account_id_or_name,\n                                                                       int operation_type,\n                                                                       operation_history_id_type start,\n                                                                       operation_history_id_type stop,\n                                                                       unsigned limit ) const\n    {\n       FC_ASSERT( _app.chain_database() );\n       const auto& db = *_app.chain_database();\n\n       const auto configured_limit = _app.get_options().api_limit_get_account_history_operations;\n       FC_ASSERT( limit <= configured_limit,\n                  \"limit can not be greater than ${configured_limit}\",\n                  (\"configured_limit\", configured_limit) );\n\n       vector<operation_history_object> result;\n       account_id_type account;\n       try {\n          account = database_api.get_account_id_from_string(account_id_or_name);\n       } catch(...) { return result; }\n       const auto& stats = account(db).statistics(db);\n       if( stats.most_recent_op == account_transaction_history_id_type() ) return result;\n       const account_transaction_history_object* node = &stats.most_recent_op(db);\n       if( start == operation_history_id_type() )\n          start = node->operation_id;\n\n       while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)\n       {\n          if( node->operation_id.instance.value <= start.instance.value ) {\n\n             if(node->operation_id(db).op.which() == operation_type)\n               result.push_back( node->operation_id(db) );\n          }\n          if( node->next == account_transaction_history_id_type() )\n             node = nullptr;\n          else node = &node->next(db);\n       }\n       if( stop.instance.value == 0 && result.size() < limit ) {\n          auto head = db.find(account_transaction_history_id_type());\n          if (head != nullptr && head->account == account && head->operation_id(db).op.which() == operation_type)\n            result.push_back(head->operation_id(db));\n       }\n       return result;\n    }\n\n\n    vector<operation_history_object> history_api::get_relative_account_history( const std::string account_id_or_name,\n                                                                                uint64_t stop,\n                                                                                unsigned limit,\n                                                                                uint64_t start ) const\n    {\n       FC_ASSERT( _app.chain_database() );\n       const auto& db = *_app.chain_database();\n\n       const auto configured_limit = _app.get_options().api_limit_get_relative_account_history;\n       FC_ASSERT( limit <= configured_limit,\n                  \"limit can not be greater than ${configured_limit}\",\n                  (\"configured_limit\", configured_limit) );\n\n       vector<operation_history_object> result;\n       account_id_type account;\n       try {\n          account = database_api.get_account_id_from_string(account_id_or_name);\n       } catch(...) { return result; }\n       const auto& stats = account(db).statistics(db);\n       if( start == 0 )\n          start = stats.total_ops;\n       else\n          start = std::min( stats.total_ops, start );\n\n       if( start >= stop && start > stats.removed_ops && limit > 0 )\n       {\n          const auto& hist_idx = db.get_index_type<account_transaction_history_index>();\n          const auto& by_seq_idx = hist_idx.indices().get<by_seq>();\n\n          auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) );\n          auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) );\n\n          do\n          {\n             --itr;\n             result.push_back( itr->operation_id(db) );\n          }\n          while ( itr != itr_stop && result.size() < limit );\n       }\n       return result;\n    }\n\n    flat_set<uint32_t> history_api::get_market_history_buckets()const\n    {\n       auto hist = _app.get_plugin<market_history_plugin>( \"market_history\" );\n       FC_ASSERT( hist );\n       return hist->tracked_buckets();\n    }\n\n    history_operation_detail history_api::get_account_history_by_operations( const std::string account_id_or_name,\n                                                                             flat_set<uint16_t> operation_types,\n                                                                             uint32_t start, unsigned limit )const\n    {\n       const auto configured_limit = _app.get_options().api_limit_get_account_history_by_operations;\n       FC_ASSERT( limit <= configured_limit,\n                  \"limit can not be greater than ${configured_limit}\",\n                  (\"configured_limit\", configured_limit) );\n\n       history_operation_detail result;\n       vector<operation_history_object> objs = get_relative_account_history( account_id_or_name, start, limit,\n                                                                             limit + start - 1 );\n       result.total_count = objs.size();\n\n       if( operation_types.empty() )\n          result.operation_history_objs = std::move(objs);\n       else\n       {\n          for( const operation_history_object &o : objs )\n          {\n             if( operation_types.find(o.op.which()) != operation_types.end() ) {\n                result.operation_history_objs.push_back(o);\n             }\n          }\n       }\n\n       return result;\n    }\n\n    vector<bucket_object> history_api::get_market_history( std::string asset_a, std::string asset_b,\n                                                           uint32_t bucket_seconds,\n                                                           fc::time_point_sec start, fc::time_point_sec end )const\n    { try {\n       FC_ASSERT(_app.chain_database());\n       const auto& db = *_app.chain_database();\n       asset_id_type a = database_api.get_asset_id_from_string( asset_a );\n       asset_id_type b = database_api.get_asset_id_from_string( asset_b );\n       vector<bucket_object> result;\n       result.reserve(200);\n\n       if( a > b ) std::swap(a,b);\n\n       const auto& bidx = db.get_index_type<bucket_index>();\n       const auto& by_key_idx = bidx.indices().get<by_key>();\n\n       auto itr = by_key_idx.lower_bound( bucket_key( a, b, bucket_seconds, start ) );\n       while( itr != by_key_idx.end() && itr->key.open <= end && result.size() < 200 )\n       {\n          if( !(itr->key.base == a && itr->key.quote == b && itr->key.seconds == bucket_seconds) )\n          {\n            return result;\n          }\n          result.push_back(*itr);\n          ++itr;\n       }\n       return result;\n    } FC_CAPTURE_AND_RETHROW( (asset_a)(asset_b)(bucket_seconds)(start)(end) ) }\n\n    crypto_api::crypto_api(){};\n\n    commitment_type crypto_api::blind( const blind_factor_type& blind, uint64_t value )\n    {\n       return fc::ecc::blind( blind, value );\n    }\n\n    blind_factor_type crypto_api::blind_sum( const std::vector<blind_factor_type>& blinds_in, uint32_t non_neg )\n    {\n       return fc::ecc::blind_sum( blinds_in, non_neg );\n    }\n\n    bool crypto_api::verify_sum( const std::vector<commitment_type>& commits_in, const std::vector<commitment_type>& neg_commits_in, int64_t excess )\n    {\n       return fc::ecc::verify_sum( commits_in, neg_commits_in, excess );\n    }\n\n    verify_range_result crypto_api::verify_range( const commitment_type& commit, const std::vector<char>& proof )\n    {\n       verify_range_result result;\n       result.success = fc::ecc::verify_range( result.min_val, result.max_val, commit, proof );\n       return result;\n    }\n\n    std::vector<char> crypto_api::range_proof_sign( uint64_t min_value,\n                                                    const commitment_type& commit,\n                                                    const blind_factor_type& commit_blind,\n                                                    const blind_factor_type& nonce,\n                                                    int8_t base10_exp,\n                                                    uint8_t min_bits,\n                                                    uint64_t actual_value )\n    {\n       return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value );\n    }\n\n    verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind( const blind_factor_type& nonce,\n                                                                            const commitment_type& commit,\n                                                                            const std::vector<char>& proof )\n    {\n       verify_range_proof_rewind_result result;\n       result.success = fc::ecc::verify_range_proof_rewind( result.blind_out,\n                                                            result.value_out,\n                                                            result.message_out,\n                                                            nonce,\n                                                            result.min_val,\n                                                            result.max_val,\n                                                            const_cast< commitment_type& >( commit ),\n                                                            proof );\n       return result;\n    }\n\n    range_proof_info crypto_api::range_get_info( const std::vector<char>& proof )\n    {\n       return fc::ecc::range_get_info( proof );\n    }\n\n    // asset_api\n    asset_api::asset_api(graphene::app::application& app) :\n          _app(app),\n          _db( *app.chain_database()),\n          database_api( std::ref(*app.chain_database()), &(app.get_options())\n          ) { }\n    asset_api::~asset_api() { }\n\n    vector<account_asset_balance> asset_api::get_asset_holders( std::string asset, uint32_t start, uint32_t limit ) const\n    {\n       const auto configured_limit = _app.get_options().api_limit_get_asset_holders;\n       FC_ASSERT( limit <= configured_limit,\n                  \"limit can not be greater than ${configured_limit}\",\n                  (\"configured_limit\", configured_limit) );\n\n       asset_id_type asset_id = database_api.get_asset_id_from_string( asset );\n       const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();\n       auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );\n\n       vector<account_asset_balance> result;\n\n       uint32_t index = 0;\n       for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )\n       {\n          if( result.size() >= limit )\n             break;\n\n          if( bal.balance.value == 0 )\n             continue;\n\n          if( index++ < start )\n             continue;\n\n          const auto account = _db.find(bal.owner);\n\n          account_asset_balance aab;\n          aab.name       = account->name;\n          aab.account_id = account->id;\n          aab.amount     = bal.balance.value;\n\n          result.push_back(aab);\n       }\n\n       return result;\n    }\n    // get number of asset holders.\n    int asset_api::get_asset_holders_count( std::string asset ) const {\n       const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();\n       asset_id_type asset_id = database_api.get_asset_id_from_string( asset );\n       auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );\n\n       int count = boost::distance(range) - 1;\n\n       return count;\n    }\n    // function to get vector of system assets with holders count.\n    vector<asset_holders> asset_api::get_all_asset_holders() const {\n       vector<asset_holders> result;\n       vector<asset_id_type> total_assets;\n       for( const asset_object& asset_obj : _db.get_index_type<asset_index>().indices() )\n       {\n          const auto& dasset_obj = asset_obj.dynamic_asset_data_id(_db);\n\n          asset_id_type asset_id;\n          asset_id = dasset_obj.id;\n\n          const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();\n          auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );\n\n          int count = boost::distance(range) - 1;\n\n          asset_holders ah;\n          ah.asset_id       = asset_id;\n          ah.count     = count;\n\n          result.push_back(ah);\n       }\n\n       return result;\n    }\n\n   // orders_api\n   flat_set<uint16_t> orders_api::get_tracked_groups()const\n   {\n      auto plugin = _app.get_plugin<grouped_orders_plugin>( \"grouped_orders\" );\n      FC_ASSERT( plugin );\n      return plugin->tracked_groups();\n   }\n\n   vector< limit_order_group > orders_api::get_grouped_limit_orders( std::string base_asset,\n                                                               std::string quote_asset,\n                                                               uint16_t group,\n                                                               optional<price> start,\n                                                               uint32_t limit )const\n   {\n      const auto configured_limit = _app.get_options().api_limit_get_grouped_limit_orders;\n      FC_ASSERT( limit <= configured_limit,\n                 \"limit can not be greater than ${configured_limit}\",\n                 (\"configured_limit\", configured_limit) );\n\n      auto plugin = _app.get_plugin<graphene::grouped_orders::grouped_orders_plugin>( \"grouped_orders\" );\n      FC_ASSERT( plugin );\n      const auto& limit_groups = plugin->limit_order_groups();\n      vector< limit_order_group > result;\n\n      asset_id_type base_asset_id = database_api.get_asset_id_from_string( base_asset );\n      asset_id_type quote_asset_id = database_api.get_asset_id_from_string( quote_asset );\n\n      price max_price = price::max( base_asset_id, quote_asset_id );\n      price min_price = price::min( base_asset_id, quote_asset_id );\n      if( start.valid() && !start->is_null() )\n         max_price = std::max( std::min( max_price, *start ), min_price );\n\n      auto itr = limit_groups.lower_bound( limit_order_group_key( group, max_price ) );\n      // use an end iterator to try to avoid expensive price comparison\n      auto end = limit_groups.upper_bound( limit_order_group_key( group, min_price ) );\n      while( itr != end && result.size() < limit )\n      {\n         result.emplace_back( *itr );\n         ++itr;\n      }\n      return result;\n   }\n\n   // custom operations api\n   vector<account_storage_object> custom_operations_api::get_storage_info(std::string account_id_or_name,\n         std::string catalog)const\n   {\n      auto plugin = _app.get_plugin<graphene::custom_operations::custom_operations_plugin>(\"custom_operations\");\n      FC_ASSERT( plugin );\n\n      const auto account_id = database_api.get_account_id_from_string(account_id_or_name);\n      vector<account_storage_object> results;\n      const auto& storage_index = _app.chain_database()->get_index_type<account_storage_index>();\n      const auto& by_account_catalog_idx = storage_index.indices().get<by_account_catalog_key>();\n      auto range = by_account_catalog_idx.equal_range(make_tuple(account_id, catalog));\n      for( const account_storage_object& aso : boost::make_iterator_range( range.first, range.second ) )\n         results.push_back(aso);\n      return results;\n   }\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/app/api_objects.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/app/api_objects.hpp>\n#include <graphene/app/util.hpp>\n\nnamespace graphene { namespace app {\n\nmarket_ticker::market_ticker(const market_ticker_object& mto,\n                             const fc::time_point_sec& now,\n                             const asset_object& asset_base,\n                             const asset_object& asset_quote,\n                             const order_book& orders)\n{\n   time = now;\n   base = asset_base.symbol;\n   quote = asset_quote.symbol;\n   percent_change = \"0\";\n   lowest_ask = \"0\";\n   lowest_ask_base_size = \"0\";\n   lowest_ask_quote_size = \"0\";\n   highest_bid = \"0\";\n   highest_bid_base_size = \"0\";\n   highest_bid_quote_size = \"0\";\n   fc::uint128_t bv;\n   fc::uint128_t qv;\n   price latest_price = asset( mto.latest_base, mto.base ) / asset( mto.latest_quote, mto.quote );\n   if( mto.base != asset_base.id )\n      latest_price = ~latest_price;\n   latest = price_to_string( latest_price, asset_base, asset_quote );\n   if( mto.last_day_base != 0 && mto.last_day_quote != 0 // has trade data before 24 hours\n       && ( mto.last_day_base != mto.latest_base || mto.last_day_quote != mto.latest_quote ) ) // price changed\n   {\n      price last_day_price = asset( mto.last_day_base, mto.base ) / asset( mto.last_day_quote, mto.quote );\n      if( mto.base != asset_base.id )\n         last_day_price = ~last_day_price;\n      percent_change = price_diff_percent_string( last_day_price, latest_price );\n   }\n   if( asset_base.id == mto.base )\n   {\n      bv = mto.base_volume;\n      qv = mto.quote_volume;\n   }\n   else\n   {\n      bv = mto.quote_volume;\n      qv = mto.base_volume;\n   }\n   base_volume = uint128_amount_to_string( bv, asset_base.precision );\n   quote_volume = uint128_amount_to_string( qv, asset_quote.precision );\n\n   if(!orders.asks.empty())\n   {\n       lowest_ask = orders.asks[0].price;\n       lowest_ask_base_size = orders.asks[0].base;\n       lowest_ask_quote_size = orders.asks[0].quote;\n   }\n\n   if(!orders.bids.empty())\n   {\n       highest_bid = orders.bids[0].price;\n       highest_bid_base_size = orders.bids[0].base;\n       highest_bid_quote_size = orders.bids[0].quote;\n   }\n\n}\n\nmarket_ticker::market_ticker(const fc::time_point_sec& now,\n                             const asset_object& asset_base,\n                             const asset_object& asset_quote)\n{\n   time = now;\n   base = asset_base.symbol;\n   quote = asset_quote.symbol;\n   latest = \"0\";\n   lowest_ask = \"0\";\n   lowest_ask_base_size = \"0\";\n   lowest_ask_quote_size = \"0\";\n   highest_bid = \"0\";\n   highest_bid_base_size = \"0\";\n   highest_bid_quote_size = \"0\";\n   percent_change = \"0\";\n   base_volume = \"0\";\n   quote_volume = \"0\";\n}\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/app/application.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/app/api.hpp>\n#include <graphene/app/api_access.hpp>\n#include <graphene/app/application.hpp>\n#include <graphene/app/plugin.hpp>\n\n#include <graphene/chain/db_with.hpp>\n#include <graphene/chain/genesis_state.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/protocol/types.hpp>\n\n#include <graphene/egenesis/egenesis.hpp>\n\n#include <graphene/net/core_messages.hpp>\n#include <graphene/net/exceptions.hpp>\n\n#include <graphene/utilities/key_conversion.hpp>\n#include <graphene/chain/worker_evaluator.hpp>\n\n#include <fc/asio.hpp>\n#include <fc/io/fstream.hpp>\n#include <fc/rpc/api_connection.hpp>\n#include <fc/rpc/websocket_api.hpp>\n#include <fc/crypto/base64.hpp>\n\n#include <boost/filesystem/path.hpp>\n#include <boost/signals2.hpp>\n#include <boost/range/algorithm/reverse.hpp>\n#include <boost/algorithm/string.hpp>\n\n#include <iostream>\n\n#include <fc/log/file_appender.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/log/logger_config.hpp>\n\n#include <boost/range/adaptor/reversed.hpp>\n\nnamespace graphene { namespace app {\nusing net::item_hash_t;\nusing net::item_id;\nusing net::message;\nusing net::block_message;\nusing net::trx_message;\n\nusing chain::block_header;\nusing chain::signed_block_header;\nusing chain::signed_block;\nusing chain::block_id_type;\n\nusing std::vector;\n\nnamespace bpo = boost::program_options;\n\nnamespace detail {\n\n   graphene::chain::genesis_state_type create_example_genesis() {\n      auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n      dlog(\"Allocating all stake to ${key}\", (\"key\", utilities::key_to_wif(nathan_key)));\n      graphene::chain::genesis_state_type initial_state;\n      initial_state.initial_parameters.get_mutable_fees() = fee_schedule::get_default();\n      initial_state.initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;\n      initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() /\n            initial_state.initial_parameters.block_interval *\n            initial_state.initial_parameters.block_interval);\n      for( uint64_t i = 0; i < initial_state.initial_active_witnesses; ++i )\n      {\n         auto name = \"init\"+fc::to_string(i);\n         initial_state.initial_accounts.emplace_back(name,\n                                                     nathan_key.get_public_key(),\n                                                     nathan_key.get_public_key(),\n                                                     true);\n         initial_state.initial_committee_candidates.push_back({name});\n         initial_state.initial_witness_candidates.push_back({name, nathan_key.get_public_key()});\n      }\n\n      initial_state.initial_accounts.emplace_back(\"nathan\", nathan_key.get_public_key());\n      initial_state.initial_balances.push_back({nathan_key.get_public_key(),\n                                                GRAPHENE_SYMBOL,\n                                                GRAPHENE_MAX_SHARE_SUPPLY});\n      initial_state.initial_chain_id = fc::sha256::hash( \"BOGUS\" );\n\n      return initial_state;\n   }\n\n\n}\n\n}}\n\n#include \"application_impl.hxx\"\n\nnamespace graphene { namespace app { namespace detail {\n\nvoid application_impl::reset_p2p_node(const fc::path& data_dir)\n{ try {\n   _p2p_network = std::make_shared<net::node>(\"BitShares Reference Implementation\");\n\n   _p2p_network->load_configuration(data_dir / \"p2p\");\n   _p2p_network->set_node_delegate(this);\n\n   if( _options->count(\"seed-node\") )\n   {\n      auto seeds = _options->at(\"seed-node\").as<vector<string>>();\n      _p2p_network->add_seed_nodes(seeds);\n   }\n\n   if( _options->count(\"seed-nodes\") )\n   {\n      auto seeds_str = _options->at(\"seed-nodes\").as<string>();\n      auto seeds = fc::json::from_string(seeds_str).as<vector<string>>(2);\n      _p2p_network->add_seed_nodes(seeds);\n   }\n   else\n   {\n      // https://bitsharestalk.org/index.php/topic,23715.0.html\n      vector<string> seeds = {\n         #include \"../egenesis/seed-nodes.txt\"\n      };\n      _p2p_network->add_seed_nodes(seeds);\n   }\n\n   if( _options->count(\"p2p-endpoint\") )\n      _p2p_network->listen_on_endpoint(fc::ip::endpoint::from_string(_options->at(\"p2p-endpoint\").as<string>()), true);\n   else\n      _p2p_network->listen_on_port(0, false);\n   _p2p_network->listen_to_p2p_network();\n   ilog(\"Configured p2p node to listen on ${ip}\", (\"ip\", _p2p_network->get_actual_listening_endpoint()));\n\n   _p2p_network->connect_to_p2p_network();\n   _p2p_network->sync_from(net::item_id(net::core_message_type_enum::block_message_type,\n                                        _chain_db->head_block_id()),\n                           std::vector<uint32_t>());\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid application_impl::new_connection( const fc::http::websocket_connection_ptr& c )\n{\n   auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_NET_MAX_NESTED_OBJECTS);\n   auto login = std::make_shared<graphene::app::login_api>( std::ref(*_self) );\n   login->enable_api(\"database_api\");\n\n   wsc->register_api(login->database());\n   wsc->register_api(fc::api<graphene::app::login_api>(login));\n   c->set_session_data( wsc );\n\n   std::string username = \"*\";\n   std::string password = \"*\";\n\n    // Try to extract login information from \"Authorization\" header if present\n   std::string auth = c->get_request_header(\"Authorization\");\n   if( boost::starts_with(auth, \"Basic \") ) {\n\n      FC_ASSERT( auth.size() > 6 );\n      auto user_pass = fc::base64_decode(auth.substr(6));\n\n      std::vector<std::string> parts;\n      boost::split( parts, user_pass, boost::is_any_of(\":\") );\n\n      FC_ASSERT(parts.size() == 2);\n\n      username = parts[0];\n      password = parts[1];\n   }\n\n   login->login(username, password);\n}\n\nvoid application_impl::reset_websocket_server()\n{ try {\n   if( !_options->count(\"rpc-endpoint\") )\n      return;\n\n   _websocket_server = std::make_shared<fc::http::websocket_server>();\n   _websocket_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );\n\n   ilog(\"Configured websocket rpc to listen on ${ip}\", (\"ip\",_options->at(\"rpc-endpoint\").as<string>()));\n   _websocket_server->listen( fc::ip::endpoint::from_string(_options->at(\"rpc-endpoint\").as<string>()) );\n   _websocket_server->start_accept();\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid application_impl::reset_websocket_tls_server()\n{ try {\n   if( !_options->count(\"rpc-tls-endpoint\") )\n      return;\n   if( !_options->count(\"server-pem\") )\n   {\n      wlog( \"Please specify a server-pem to use rpc-tls-endpoint\" );\n      return;\n   }\n\n   string password = _options->count(\"server-pem-password\") ? _options->at(\"server-pem-password\").as<string>() : \"\";\n   _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at(\"server-pem\").as<string>(), password );\n   _websocket_tls_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );\n\n   ilog(\"Configured websocket TLS rpc to listen on ${ip}\", (\"ip\",_options->at(\"rpc-tls-endpoint\").as<string>()));\n   _websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at(\"rpc-tls-endpoint\").as<string>()) );\n   _websocket_tls_server->start_accept();\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid application_impl::set_dbg_init_key( graphene::chain::genesis_state_type& genesis, const std::string& init_key )\n{\n   flat_set< std::string > initial_witness_names;\n   public_key_type init_pubkey( init_key );\n   for( uint64_t i=0; i<genesis.initial_active_witnesses; i++ )\n      genesis.initial_witness_candidates[i].block_signing_key = init_pubkey;\n}\n\n\n\nvoid application_impl::set_api_limit() {\n   if (_options->count(\"api-limit-get-account-history-operations\")) {\n      _app_options.api_limit_get_account_history_operations = _options->at(\"api-limit-get-account-history-operations\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-account-history\")){\n      _app_options.api_limit_get_account_history = _options->at(\"api-limit-get-account-history\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-grouped-limit-orders\")){\n      _app_options.api_limit_get_grouped_limit_orders = _options->at(\"api-limit-get-grouped-limit-orders\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-relative-account-history\")){\n       _app_options.api_limit_get_relative_account_history = _options->at(\"api-limit-get-relative-account-history\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-account-history-by-operations\")){\n       _app_options.api_limit_get_account_history_by_operations = _options->at(\"api-limit-get-account-history-by-operations\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-asset-holders\")){\n       _app_options.api_limit_get_asset_holders = _options->at(\"api-limit-get-asset-holders\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-key-references\")){\n       _app_options.api_limit_get_key_references = _options->at(\"api-limit-get-key-references\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-htlc-by\")) {\n      _app_options.api_limit_get_htlc_by = _options->at(\"api-limit-get-htlc-by\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-full-accounts\")) {\n      _app_options.api_limit_get_full_accounts = _options->at(\"api-limit-get-full-accounts\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-full-accounts-lists\")) {\n      _app_options.api_limit_get_full_accounts_lists = _options->at(\"api-limit-get-full-accounts-lists\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-call-orders\")) {\n      _app_options.api_limit_get_call_orders = _options->at(\"api-limit-get-call-orders\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-settle-orders\")) {\n      _app_options.api_limit_get_settle_orders = _options->at(\"api-limit-get-settle-orders\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-assets\")) {\n      _app_options.api_limit_get_assets = _options->at(\"api-limit-get-assets\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-limit-orders\")){\n      _app_options.api_limit_get_limit_orders = _options->at(\"api-limit-get-limit-orders\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-limit-orders-by-account\")){\n      _app_options.api_limit_get_limit_orders_by_account = _options->at(\"api-limit-get-limit-orders-by-account\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-order-book\")){\n      _app_options.api_limit_get_order_book = _options->at(\"api-limit-get-order-book\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-list-htlcs\")){\n      _app_options.api_limit_list_htlcs = _options->at(\"api-limit-list-htlcs\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-lookup-accounts\")) {\n      _app_options.api_limit_lookup_accounts = _options->at(\"api-limit-lookup-accounts\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-lookup-witness-accounts\")) {\n      _app_options.api_limit_lookup_witness_accounts = _options->at(\"api-limit-lookup-witness-accounts\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-lookup-committee-member-accounts\")) {\n      _app_options.api_limit_lookup_committee_member_accounts = _options->at(\"api-limit-lookup-committee-member-accounts\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-lookup-vote-ids\")) {\n      _app_options.api_limit_lookup_vote_ids = _options->at(\"api-limit-lookup-vote-ids\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-account-limit-orders\")) {\n      _app_options.api_limit_get_account_limit_orders = _options->at(\"api-limit-get-account-limit-orders\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-collateral-bids\")) {\n      _app_options.api_limit_get_collateral_bids = _options->at(\"api-limit-get-collateral-bids\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-top-markets\")) {\n      _app_options.api_limit_get_top_markets = _options->at(\"api-limit-get-top-markets\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-trade-history\")) {\n      _app_options.api_limit_get_trade_history = _options->at(\"api-limit-get-trade-history\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-trade-history-by-sequence\")) {\n      _app_options.api_limit_get_trade_history_by_sequence = _options->at(\"api-limit-get-trade-history-by-sequence\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-withdraw-permissions-by-giver\")) {\n      _app_options.api_limit_get_withdraw_permissions_by_giver = _options->at(\"api-limit-get-withdraw-permissions-by-giver\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-withdraw-permissions-by-recipient\")) {\n      _app_options.api_limit_get_withdraw_permissions_by_recipient = _options->at(\"api-limit-get-withdraw-permissions-by-recipient\").as<uint64_t>();\n   }\n   if(_options->count(\"api-limit-get-liquidity-pools\")) {\n      _app_options.api_limit_get_liquidity_pools = _options->at(\"api-limit-get-liquidity-pools\").as<uint64_t>();\n   }\n}\n\nvoid application_impl::startup()\n{ try {\n   fc::create_directories(_data_dir / \"blockchain\");\n\n   auto initial_state = [this] {\n      ilog(\"Initializing database...\");\n      if( _options->count(\"genesis-json\") )\n      {\n         std::string genesis_str;\n         fc::read_file_contents( _options->at(\"genesis-json\").as<boost::filesystem::path>(), genesis_str );\n         graphene::chain::genesis_state_type genesis = fc::json::from_string( genesis_str ).as<graphene::chain::genesis_state_type>( 20 );\n         bool modified_genesis = false;\n         if( _options->count(\"genesis-timestamp\") )\n         {\n            genesis.initial_timestamp = fc::time_point_sec( fc::time_point::now() )\n                                      + genesis.initial_parameters.block_interval\n                                      + _options->at(\"genesis-timestamp\").as<uint32_t>();\n            genesis.initial_timestamp -= ( genesis.initial_timestamp.sec_since_epoch()\n                                           % genesis.initial_parameters.block_interval );\n            modified_genesis = true;\n\n            ilog(\n               \"Used genesis timestamp:  ${timestamp} (PLEASE RECORD THIS)\", \n               (\"timestamp\", genesis.initial_timestamp.to_iso_string())\n            );\n         }\n         if( _options->count(\"dbg-init-key\") )\n         {\n            std::string init_key = _options->at( \"dbg-init-key\" ).as<string>();\n            FC_ASSERT( genesis.initial_witness_candidates.size() >= genesis.initial_active_witnesses );\n            set_dbg_init_key( genesis, init_key );\n            modified_genesis = true;\n            ilog(\"Set init witness key to ${init_key}\", (\"init_key\", init_key));\n         }\n         if( modified_genesis )\n         {\n            wlog(\"WARNING:  GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT\");\n            genesis_str += \"BOGUS\";\n            genesis.initial_chain_id = fc::sha256::hash( genesis_str );\n         }\n         else\n            genesis.initial_chain_id = fc::sha256::hash( genesis_str );\n         return genesis;\n      }\n      else\n      {\n         std::string egenesis_json;\n         graphene::egenesis::compute_egenesis_json( egenesis_json );\n         FC_ASSERT( egenesis_json != \"\" );\n         FC_ASSERT( graphene::egenesis::get_egenesis_json_hash() == fc::sha256::hash( egenesis_json ) );\n         auto genesis = fc::json::from_string( egenesis_json ).as<graphene::chain::genesis_state_type>( 20 );\n         genesis.initial_chain_id = fc::sha256::hash( egenesis_json );\n         return genesis;\n      }\n   };\n\n   if( _options->count(\"resync-blockchain\") )\n      _chain_db->wipe(_data_dir / \"blockchain\", true);\n\n   flat_map<uint32_t,block_id_type> loaded_checkpoints;\n   if( _options->count(\"checkpoint\") )\n   {\n      auto cps = _options->at(\"checkpoint\").as<vector<string>>();\n      loaded_checkpoints.reserve( cps.size() );\n      for( auto cp : cps )\n      {\n         auto item = fc::json::from_string(cp).as<std::pair<uint32_t,block_id_type> >( 2 );\n         loaded_checkpoints[item.first] = item.second;\n      }\n   }\n   _chain_db->add_checkpoints( loaded_checkpoints );\n\n   if( _options->count(\"enable-standby-votes-tracking\") )\n   {\n      _chain_db->enable_standby_votes_tracking( _options->at(\"enable-standby-votes-tracking\").as<bool>() );\n   }\n\n   if( _options->count(\"replay-blockchain\") || _options->count(\"revalidate-blockchain\") )\n      _chain_db->wipe( _data_dir / \"blockchain\", false );\n\n   try\n   {\n      // these flags are used in open() only, i. e. during replay\n      uint32_t skip;\n      if( _options->count(\"revalidate-blockchain\") ) // see also handle_block()\n      {\n         if( !loaded_checkpoints.empty() )\n            wlog( \"Warning - revalidate will not validate before last checkpoint\" );\n         if( _options->count(\"force-validate\") )\n            skip = graphene::chain::database::skip_nothing;\n         else\n            skip = graphene::chain::database::skip_transaction_signatures;\n      }\n      else // no revalidate, skip most checks\n         skip = graphene::chain::database::skip_witness_signature |\n                graphene::chain::database::skip_block_size_check |\n                graphene::chain::database::skip_merkle_check |\n                graphene::chain::database::skip_transaction_signatures |\n                graphene::chain::database::skip_transaction_dupe_check |\n                graphene::chain::database::skip_tapos_check |\n                graphene::chain::database::skip_witness_schedule_check;\n\n      graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () {\n         _chain_db->open( _data_dir / \"blockchain\", initial_state, GRAPHENE_CURRENT_DB_VERSION );\n      });\n   }\n   catch( const fc::exception& e )\n   {\n      elog( \"Caught exception ${e} in open(), you might want to force a replay\", (\"e\", e.to_detail_string()) );\n      throw;\n   }\n\n   if( _options->count(\"force-validate\") )\n   {\n      ilog( \"All transaction signatures will be validated\" );\n      _force_validate = true;\n   }\n\n   if ( _options->count(\"enable-subscribe-to-all\") )\n      _app_options.enable_subscribe_to_all = _options->at( \"enable-subscribe-to-all\" ).as<bool>();\n\n   set_api_limit();\n\n   if( _active_plugins.find( \"market_history\" ) != _active_plugins.end() )\n      _app_options.has_market_history_plugin = true;\n\n   if( _active_plugins.find( \"api_helper_indexes\" ) != _active_plugins.end() )\n      _app_options.has_api_helper_indexes_plugin = true;\n\n   if( _options->count(\"api-access\") ) {\n\n      fc::path api_access_file = _options->at(\"api-access\").as<boost::filesystem::path>();\n\n      FC_ASSERT( fc::exists(api_access_file), \n            \"Failed to load file from ${path}\", (\"path\", api_access_file) );\n\n      _apiaccess = fc::json::from_file( api_access_file ).as<api_access>( 20 );\n      ilog( \"Using api access file from ${path}\",\n            (\"path\", api_access_file) );\n   }\n   else\n   {\n      // TODO:  Remove this generous default access policy\n      // when the UI logs in properly\n      _apiaccess = api_access();\n      api_access_info wild_access;\n      wild_access.password_hash_b64 = \"*\";\n      wild_access.password_salt_b64 = \"*\";\n      wild_access.allowed_apis.push_back( \"database_api\" );\n      wild_access.allowed_apis.push_back( \"network_broadcast_api\" );\n      wild_access.allowed_apis.push_back( \"history_api\" );\n      wild_access.allowed_apis.push_back( \"orders_api\" );\n      wild_access.allowed_apis.push_back( \"custom_operations_api\" );\n      _apiaccess.permission_map[\"*\"] = wild_access;\n   }\n\n   reset_p2p_node(_data_dir);\n   reset_websocket_server();\n   reset_websocket_tls_server();\n} FC_LOG_AND_RETHROW() }\n\noptional< api_access_info > application_impl::get_api_access_info(const string& username)const\n{\n   optional< api_access_info > result;\n   auto it = _apiaccess.permission_map.find(username);\n   if( it == _apiaccess.permission_map.end() )\n   {\n      it = _apiaccess.permission_map.find(\"*\");\n      if( it == _apiaccess.permission_map.end() )\n         return result;\n   }\n   return it->second;\n}\n\nvoid application_impl::set_api_access_info(const string& username, api_access_info&& permissions)\n{\n   _apiaccess.permission_map.insert(std::make_pair(username, std::move(permissions)));\n}\n\n/**\n * If delegate has the item, the network has no need to fetch it.\n */\nbool application_impl::has_item(const net::item_id& id)\n{\n   try\n   {\n      if( id.item_type == graphene::net::block_message_type )\n         return _chain_db->is_known_block(id.item_hash);\n      else\n         return _chain_db->is_known_transaction(id.item_hash);\n   }\n   FC_CAPTURE_AND_RETHROW( (id) )\n}\n\n/**\n * @brief allows the application to validate an item prior to broadcasting to peers.\n *\n * @param sync_mode true if the message was fetched through the sync process, false during normal operation\n * @returns true if this message caused the blockchain to switch forks, false if it did not\n *\n * @throws exception if error validating the item, otherwise the item is safe to broadcast on.\n */\nbool application_impl::handle_block(const graphene::net::block_message& blk_msg, bool sync_mode,\n                          std::vector<fc::uint160_t>& contained_transaction_message_ids)\n{ try {\n\n   auto latency = fc::time_point::now() - blk_msg.block.timestamp;\n   if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)\n   {\n      const auto& witness = blk_msg.block.witness(*_chain_db);\n      const auto& witness_account = witness.witness_account(*_chain_db);\n      auto last_irr = _chain_db->get_dynamic_global_properties().last_irreversible_block_num;\n      ilog(\"Got block: #${n} ${bid} time: ${t} transaction(s): ${x} latency: ${l} ms from: ${w}  irreversible: ${i} (-${d})\",\n           (\"t\",blk_msg.block.timestamp)\n           (\"n\", blk_msg.block.block_num())\n           (\"bid\", blk_msg.block.id())\n           (\"x\", blk_msg.block.transactions.size())\n           (\"l\", (latency.count()/1000))\n           (\"w\",witness_account.name)\n           (\"i\",last_irr)(\"d\",blk_msg.block.block_num()-last_irr) );\n   }\n   GRAPHENE_ASSERT( latency.count()/1000 > -5000,\n                    graphene::net::block_timestamp_in_future_exception,\n                    \"Rejecting block with timestamp in the future\", );\n\n   try {\n      const uint32_t skip = (_is_block_producer | _force_validate) ?\n                               database::skip_nothing : database::skip_transaction_signatures;\n      bool result = valve.do_serial( [this,&blk_msg,skip] () {\n         _chain_db->precompute_parallel( blk_msg.block, skip ).wait();\n      }, [this,&blk_msg,skip] () {\n         // TODO: in the case where this block is valid but on a fork that's too old for us to switch to,\n         // you can help the network code out by throwing a block_older_than_undo_history exception.\n         // when the net code sees that, it will stop trying to push blocks from that chain, but\n         // leave that peer connected so that they can get sync blocks from us\n         return _chain_db->push_block( blk_msg.block, skip );\n      });\n\n      // the block was accepted, so we now know all of the transactions contained in the block\n      if (!sync_mode)\n      {\n         // if we're not in sync mode, there's a chance we will be seeing some transactions\n         // included in blocks before we see the free-floating transaction itself.  If that\n         // happens, there's no reason to fetch the transactions, so  construct a list of the\n         // transaction message ids we no longer need.\n         // during sync, it is unlikely that we'll see any old\n         contained_transaction_message_ids.reserve( contained_transaction_message_ids.size()\n                                                    + blk_msg.block.transactions.size() );\n         for (const processed_transaction& transaction : blk_msg.block.transactions)\n         {\n            graphene::net::trx_message transaction_message(transaction);\n            contained_transaction_message_ids.emplace_back(graphene::net::message(transaction_message).id());\n         }\n      }\n\n      return result;\n   } catch ( const graphene::chain::unlinkable_block_exception& e ) {\n      // translate to a graphene::net exception\n      elog(\"Error when pushing block:\\n${e}\", (\"e\", e.to_detail_string()));\n      FC_THROW_EXCEPTION( graphene::net::unlinkable_block_exception,\n                          \"Error when pushing block:\\n${e}\",\n                          (\"e\", e.to_detail_string()) );\n   } catch( const fc::exception& e ) {\n      elog(\"Error when pushing block:\\n${e}\", (\"e\", e.to_detail_string()));\n      throw;\n   }\n\n   if( !_is_finished_syncing && !sync_mode )\n   {\n      _is_finished_syncing = true;\n      _self->syncing_finished();\n   }\n} FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) return false; }\n\nvoid application_impl::handle_transaction(const graphene::net::trx_message& transaction_message)\n{ try {\n   static fc::time_point last_call;\n   static int trx_count = 0;\n   ++trx_count;\n   auto now = fc::time_point::now();\n   if( now - last_call > fc::seconds(1) ) {\n      ilog(\"Got ${c} transactions from network\", (\"c\",trx_count) );\n      last_call = now;\n      trx_count = 0;\n   }\n\n   _chain_db->precompute_parallel( transaction_message.trx ).wait();\n   _chain_db->push_transaction( transaction_message.trx );\n} FC_CAPTURE_AND_RETHROW( (transaction_message) ) }\n\nvoid application_impl::handle_message(const message& message_to_process)\n{\n   // not a transaction, not a block\n   FC_THROW( \"Invalid Message Type\" );\n}\n\nbool application_impl::is_included_block(const block_id_type& block_id)\n{\n  uint32_t block_num = block_header::num_from_id(block_id);\n  block_id_type block_id_in_preferred_chain = _chain_db->get_block_id_for_num(block_num);\n  return block_id == block_id_in_preferred_chain;\n}\n\n/**\n * Assuming all data elements are ordered in some way, this method should\n * return up to limit ids that occur *after* the last ID in synopsis that\n * we recognize.\n *\n * On return, remaining_item_count will be set to the number of items\n * in our blockchain after the last item returned in the result,\n * or 0 if the result contains the last item in the blockchain\n */\nstd::vector<item_hash_t> application_impl::get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,\n                                               uint32_t& remaining_item_count,\n                                               uint32_t limit)\n{ try {\n   vector<block_id_type> result;\n   remaining_item_count = 0;\n   if( _chain_db->head_block_num() == 0 )\n      return result;\n\n   result.reserve(limit);\n   block_id_type last_known_block_id;\n\n   if (blockchain_synopsis.empty() ||\n       (blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))\n   {\n     // peer has sent us an empty synopsis meaning they have no blocks.\n     // A bug in old versions would cause them to send a synopsis containing block 000000000\n     // when they had an empty blockchain, so pretend they sent the right thing here.\n\n     // do nothing, leave last_known_block_id set to zero\n   }\n   else\n   {\n     bool found_a_block_in_synopsis = false;\n     for (const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis))\n       if (block_id_in_synopsis == block_id_type() ||\n           (_chain_db->is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis)))\n       {\n         last_known_block_id = block_id_in_synopsis;\n         found_a_block_in_synopsis = true;\n         break;\n       }\n     if (!found_a_block_in_synopsis)\n       FC_THROW_EXCEPTION( graphene::net::peer_is_on_an_unreachable_fork,\n                           \"Unable to provide a list of blocks starting at any of the blocks in peer's synopsis\" );\n   }\n   for( uint32_t num = block_header::num_from_id(last_known_block_id);\n        num <= _chain_db->head_block_num() && result.size() < limit;\n        ++num )\n      if( num > 0 )\n         result.push_back(_chain_db->get_block_id_for_num(num));\n\n   if( !result.empty() && block_header::num_from_id(result.back()) < _chain_db->head_block_num() )\n      remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) }\n\n/**\n * Given the hash of the requested data, fetch the body.\n */\nmessage application_impl::get_item(const item_id& id)\n{ try {\n  // ilog(\"Request for item ${id}\", (\"id\", id));\n   if( id.item_type == graphene::net::block_message_type )\n   {\n      auto opt_block = _chain_db->fetch_block_by_id(id.item_hash);\n      if( !opt_block )\n         elog(\"Couldn't find block ${id} -- corresponding ID in our chain is ${id2}\",\n              (\"id\", id.item_hash)(\"id2\", _chain_db->get_block_id_for_num(block_header::num_from_id(id.item_hash))));\n      FC_ASSERT( opt_block.valid() );\n      // ilog(\"Serving up block #${num}\", (\"num\", opt_block->block_num()));\n      return block_message(std::move(*opt_block));\n   }\n   return trx_message( _chain_db->get_recent_transaction( id.item_hash ) );\n} FC_CAPTURE_AND_RETHROW( (id) ) }\n\nchain_id_type application_impl::get_chain_id() const\n{\n   return _chain_db->get_chain_id();\n}\n\n/**\n * Returns a synopsis of the blockchain used for syncing.  This consists of a list of\n * block hashes at intervals exponentially increasing towards the genesis block.\n * When syncing to a peer, the peer uses this data to determine if we're on the same\n * fork as they are, and if not, what blocks they need to send us to get us on their\n * fork.\n *\n * In the over-simplified case, this is a straighforward synopsis of our current\n * preferred blockchain; when we first connect up to a peer, this is what we will be sending.\n * It looks like this:\n *   If the blockchain is empty, it will return the empty list.\n *   If the blockchain has one block, it will return a list containing just that block.\n *   If it contains more than one block:\n *     the first element in the list will be the hash of the highest numbered block that\n *         we cannot undo\n *     the second element will be the hash of an item at the half way point in the undoable\n *         segment of the blockchain\n *     the third will be ~3/4 of the way through the undoable segment of the block chain\n *     the fourth will be at ~7/8...\n *       &c.\n *     the last item in the list will be the hash of the most recent block on our preferred chain\n * so if the blockchain had 26 blocks labeled a - z, the synopsis would be:\n *    a n u x z\n * the idea being that by sending a small (<30) number of block ids, we can summarize a huge\n * blockchain.  The block ids are more dense near the end of the chain where because we are\n * more likely to be almost in sync when we first connect, and forks are likely to be short.\n * If the peer we're syncing with in our example is on a fork that started at block 'v',\n * then they will reply to our synopsis with a list of all blocks starting from block 'u',\n * the last block they know that we had in common.\n *\n * In the real code, there are several complications.\n *\n * First, as an optimization, we don't usually send a synopsis of the entire blockchain, we\n * send a synopsis of only the segment of the blockchain that we have undo data for.  If their\n * fork doesn't build off of something in our undo history, we would be unable to switch, so there's\n * no reason to fetch the blocks.\n *\n * Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think\n * we are missing, they only send a chunk of a few thousand blocks at once.  After we get those\n * block ids, we need to request more blocks by sending another synopsis (we can't just say \"send me\n * the next 2000 ids\" because they may have switched forks themselves and they don't track what\n * they've sent us).  For faster performance, we want to get a fairly long list of block ids first,\n * then start downloading the blocks.\n * The peer doesn't handle these follow-up block id requests any different from the initial request;\n * it treats the synopsis we send as our blockchain and bases its response entirely off that.  So to\n * get the response we want (the next chunk of block ids following the last one they sent us, or,\n * failing that, the shortest fork off of the last list of block ids they sent), we need to construct\n * a synopsis as if our blockchain was made up of:\n *    1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)\n *    2. the blocks we've already pushed from their fork (if there's a fork)\n *    3. the block ids they've previously sent us\n * Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in\n * number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.\n * We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and\n * fork database.  The reference_point parameter is the last block from that peer that has been\n * successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on\n * the main chain.\n */\nstd::vector<item_hash_t> application_impl::get_blockchain_synopsis(const item_hash_t& reference_point,\n                                                         uint32_t number_of_blocks_after_reference_point)\n{ try {\n    std::vector<item_hash_t> synopsis;\n    synopsis.reserve(30);\n    uint32_t high_block_num;\n    uint32_t non_fork_high_block_num;\n    uint32_t low_block_num = _chain_db->last_non_undoable_block_num();\n    std::vector<block_id_type> fork_history;\n\n    if (reference_point != item_hash_t())\n    {\n      // the node is asking for a summary of the block chain up to a specified\n      // block, which may or may not be on a fork\n      // for now, assume it's not on a fork\n      if (is_included_block(reference_point))\n      {\n        // reference_point is a block we know about and is on the main chain\n        uint32_t reference_point_block_num = block_header::num_from_id(reference_point);\n        assert(reference_point_block_num > 0);\n        high_block_num = reference_point_block_num;\n        non_fork_high_block_num = high_block_num;\n\n        if (reference_point_block_num < low_block_num)\n        {\n          // we're on the same fork (at least as far as reference_point) but we've passed\n          // reference point and could no longer undo that far if we diverged after that\n          // block.  This should probably only happen due to a race condition where\n          // the network thread calls this function, and then immediately pushes a bunch of blocks,\n          // then the main thread finally processes this function.\n          // with the current framework, there's not much we can do to tell the network\n          // thread what our current head block is, so we'll just pretend that\n          // our head is actually the reference point.\n          // this *may* enable us to fetch blocks that we're unable to push, but that should\n          // be a rare case (and correctly handled)\n          low_block_num = reference_point_block_num;\n        }\n      }\n      else\n      {\n        // block is a block we know about, but it is on a fork\n        try\n        {\n          fork_history = _chain_db->get_block_ids_on_fork(reference_point);\n          // returns a vector where the last element is the common ancestor with the preferred chain,\n          // and the first element is the reference point you passed in\n          assert(fork_history.size() >= 2);\n\n          if( fork_history.front() != reference_point )\n          {\n             edump( (fork_history)(reference_point) );\n             assert(fork_history.front() == reference_point);\n          }\n          block_id_type last_non_fork_block = fork_history.back();\n          fork_history.pop_back();  // remove the common ancestor\n          boost::reverse(fork_history);\n\n          if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?)\n            non_fork_high_block_num = 0;\n          else\n            non_fork_high_block_num = block_header::num_from_id(last_non_fork_block);\n\n          high_block_num = non_fork_high_block_num + fork_history.size();\n          assert(high_block_num == block_header::num_from_id(fork_history.back()));\n        }\n        catch (const fc::exception& e)\n        {\n          // unable to get fork history for some reason.  maybe not linked?\n          // we can't return a synopsis of its chain\n          elog( \"Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}\",\n                (\"hash\", reference_point)(\"exception\", e) );\n          throw;\n        }\n        if (non_fork_high_block_num < low_block_num)\n        {\n          wlog(\"Unable to generate a usable synopsis because the peer we're generating it for forked too long ago \"\n               \"(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})\",\n               (\"low_block_num\", low_block_num)\n               (\"non_fork_high_block_num\", non_fork_high_block_num));\n          FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, \"Peer is are on a fork I'm unable to switch to\");\n        }\n      }\n    }\n    else\n    {\n      // no reference point specified, summarize the whole block chain\n      high_block_num = _chain_db->head_block_num();\n      non_fork_high_block_num = high_block_num;\n      if (high_block_num == 0)\n        return synopsis; // we have no blocks\n    }\n\n    if( low_block_num == 0)\n       low_block_num = 1;\n\n    // at this point:\n    // low_block_num is the block before the first block we can undo,\n    // non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)\n    // high_block_num is the block number of the reference block, or the end of the chain if no reference provided\n\n    // true_high_block_num is the ending block number after the network code appends any item ids it\n    // knows about that we don't\n    uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;\n    do\n    {\n      // for each block in the synopsis, figure out where to pull the block id from.\n      // if it's <= non_fork_high_block_num, we grab it from the main blockchain;\n      // if it's not, we pull it from the fork history\n      if (low_block_num <= non_fork_high_block_num)\n        synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num));\n      else\n        synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]);\n      low_block_num += (true_high_block_num - low_block_num + 2) / 2;\n    }\n    while (low_block_num <= high_block_num);\n\n    //idump((synopsis));\n    return synopsis;\n} FC_CAPTURE_AND_RETHROW() }\n\n/**\n * Call this after the call to handle_message succeeds.\n *\n * @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call\n * @param item_count the number of items known to the node that haven't been sent to handle_item() yet.\n *                   After `item_count` more calls to handle_item(), the node will be in sync\n */\nvoid application_impl::sync_status(uint32_t item_type, uint32_t item_count)\n{\n   // any status reports to GUI go here\n}\n\n/**\n * Call any time the number of connected peers changes.\n */\nvoid application_impl::connection_count_changed(uint32_t c)\n{\n  // any status reports to GUI go here\n}\n\nuint32_t application_impl::get_block_number(const item_hash_t& block_id)\n{ try {\n   return block_header::num_from_id(block_id);\n} FC_CAPTURE_AND_RETHROW( (block_id) ) }\n\n/**\n * Returns the time a block was produced (if block_id = 0, returns genesis time).\n * If we don't know about the block, returns time_point_sec::min()\n */\nfc::time_point_sec application_impl::get_block_time(const item_hash_t& block_id)\n{ try {\n   auto opt_block = _chain_db->fetch_block_by_id( block_id );\n   if( opt_block.valid() ) return opt_block->timestamp;\n   return fc::time_point_sec::min();\n} FC_CAPTURE_AND_RETHROW( (block_id) ) }\n\nitem_hash_t application_impl::get_head_block_id() const\n{\n   return _chain_db->head_block_id();\n}\n\nuint32_t application_impl::estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const\n{\n   return 0; // there are no forks in graphene\n}\n\nvoid application_impl::error_encountered(const std::string& message, const fc::oexception& error)\n{\n   // notify GUI or something cool\n}\n\nuint8_t application_impl::get_current_block_interval_in_seconds() const\n{\n   return _chain_db->get_global_properties().parameters.block_interval;\n}\n\n\n\n} } } // namespace graphene namespace app namespace detail\n\nnamespace graphene { namespace app {\n\napplication::application()\n   : my(new detail::application_impl(this))\n{}\n\napplication::~application()\n{\n   if( my->_p2p_network )\n   {\n      my->_p2p_network->close();\n      my->_p2p_network.reset();\n   }\n   if( my->_chain_db )\n   {\n      my->_chain_db->close();\n   }\n}\n\nvoid application::set_program_options(boost::program_options::options_description& command_line_options,\n                                      boost::program_options::options_description& configuration_file_options) const\n{\n   configuration_file_options.add_options()\n         (\"p2p-endpoint\", bpo::value<string>(), \"Endpoint for P2P node to listen on\")\n         (\"seed-node,s\", bpo::value<vector<string>>()->composing(),\n          \"P2P nodes to connect to on startup (may specify multiple times)\")\n         (\"seed-nodes\", bpo::value<string>()->composing(),\n          \"JSON array of P2P nodes to connect to on startup\")\n         (\"checkpoint,c\", bpo::value<vector<string>>()->composing(),\n          \"Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.\")\n         (\"rpc-endpoint\", bpo::value<string>()->implicit_value(\"127.0.0.1:8090\"),\n          \"Endpoint for websocket RPC to listen on\")\n         (\"rpc-tls-endpoint\", bpo::value<string>()->implicit_value(\"127.0.0.1:8089\"),\n          \"Endpoint for TLS websocket RPC to listen on\")\n         (\"server-pem,p\", bpo::value<string>()->implicit_value(\"server.pem\"), \"The TLS certificate file for this server\")\n         (\"server-pem-password,P\", bpo::value<string>()->implicit_value(\"\"), \"Password for this certificate\")\n         (\"genesis-json\", bpo::value<boost::filesystem::path>(), \"File to read Genesis State from\")\n         (\"dbg-init-key\", bpo::value<string>(), \"Block signing key to use for init witnesses, overrides genesis file\")\n         (\"api-access\", bpo::value<boost::filesystem::path>(), \"JSON file specifying API permissions\")\n         (\"io-threads\", bpo::value<uint16_t>()->implicit_value(0), \"Number of IO threads, default to 0 for auto-configuration\")\n         (\"enable-subscribe-to-all\", bpo::value<bool>()->implicit_value(true),\n          \"Whether allow API clients to subscribe to universal object creation and removal events\")\n         (\"enable-standby-votes-tracking\", bpo::value<bool>()->implicit_value(true),\n          \"Whether to enable tracking of votes of standby witnesses and committee members. \"\n          \"Set it to true to provide accurate data to API clients, set to false for slightly better performance.\")\n         (\"api-limit-get-account-history-operations\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For history_api::get_account_history_operations to set max limit value\")\n         (\"api-limit-get-account-history\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For history_api::get_account_history to set max limit value\")\n         (\"api-limit-get-grouped-limit-orders\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For orders_api::get_grouped_limit_orders to set max limit value\")\n         (\"api-limit-get-relative-account-history\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For history_api::get_relative_account_history to set max limit value\")\n         (\"api-limit-get-account-history-by-operations\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For history_api::get_account_history_by_operations to set max limit value\")\n         (\"api-limit-get-asset-holders\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For asset_api::get_asset_holders to set max limit value\")\n         (\"api-limit-get-key-references\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For database_api_impl::get_key_references to set max limit value\")\n         (\"api-limit-get-htlc-by\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For database_api_impl::get_htlc_by_from and get_htlc_by_to to set max limit value\")\n         (\"api-limit-get-full-accounts\",boost::program_options::value<uint64_t>()->default_value(50),\n          \"For database_api_impl::get_full_accounts to set max accounts to query at once\")\n         (\"api-limit-get-full-accounts-lists\",boost::program_options::value<uint64_t>()->default_value(500),\n          \"For database_api_impl::get_full_accounts to set max items to return in the lists\")\n         (\"api-limit-get-call-orders\",boost::program_options::value<uint64_t>()->default_value(300),\n          \"For database_api_impl::get_call_orders and get_call_orders_by_account to set max limit value\")\n         (\"api-limit-get-settle-orders\",boost::program_options::value<uint64_t>()->default_value(300),\n          \"For database_api_impl::get_settle_orders and get_settle_orders_by_account to set max limit value\")\n         (\"api-limit-get-assets\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For database_api_impl::list_assets and get_assets_by_issuer to set max limit value\")\n         (\"api-limit-get-limit-orders\",boost::program_options::value<uint64_t>()->default_value(300),\n          \"For database_api_impl::get_limit_orders to set max limit value\")\n         (\"api-limit-get-limit-orders-by-account\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For database_api_impl::get_limit_orders_by_account to set max limit value\")\n         (\"api-limit-get-order-book\",boost::program_options::value<uint64_t>()->default_value(50),\n          \"For database_api_impl::get_order_book to set max limit value\")\n         (\"api-limit-lookup-accounts\",boost::program_options::value<uint64_t>()->default_value(1000),\n          \"For database_api_impl::lookup_accounts to set max limit value\")\n         (\"api-limit-lookup-witness-accounts\",boost::program_options::value<uint64_t>()->default_value(1000),\n          \"For database_api_impl::lookup_witness_accounts to set max limit value\")\n         (\"api-limit-lookup-committee-member-accounts\",boost::program_options::value<uint64_t>()->default_value(1000),\n          \"For database_api_impl::lookup_committee_member_accounts to set max limit value\")\n         (\"api-limit-lookup-vote-ids\",boost::program_options::value<uint64_t>()->default_value(1000),\n          \"For database_api_impl::lookup_vote_ids to set max limit value\")\n         (\"api-limit-get-account-limit-orders\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For database_api_impl::get_account_limit_orders to set max limit value\")\n         (\"api-limit-get-collateral-bids\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For database_api_impl::get_collateral_bids to set max limit value\")\n         (\"api-limit-get-top-markets\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For database_api_impl::get_top_markets to set max limit value\")\n         (\"api-limit-get-trade-history\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For database_api_impl::get_trade_history to set max limit value\")\n         (\"api-limit-get-trade-history-by-sequence\",boost::program_options::value<uint64_t>()->default_value(100),\n          \"For database_api_impl::get_trade_history_by_sequence to set max limit value\")\n         (\"api-limit-get-withdraw-permissions-by-giver\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For database_api_impl::get_withdraw_permissions_by_giver to set max limit value\")\n         (\"api-limit-get-withdraw-permissions-by-recipient\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For database_api_impl::get_withdraw_permissions_by_recipient to set max limit value\")\n         (\"api-limit-get-liquidity-pools\",boost::program_options::value<uint64_t>()->default_value(101),\n          \"For database_api_impl::get_liquidity_pools_* to set max limit value\")\n         ;\n   command_line_options.add(configuration_file_options);\n   command_line_options.add_options()\n         (\"replay-blockchain\", \"Rebuild object graph by replaying all blocks without validation\")\n         (\"revalidate-blockchain\", \"Rebuild object graph by replaying all blocks with full validation\")\n         (\"resync-blockchain\", \"Delete all blocks and re-sync with network from scratch\")\n         (\"force-validate\", \"Force validation of all transactions during normal operation\")\n         (\"genesis-timestamp\", bpo::value<uint32_t>(),\n          \"Replace timestamp from genesis.json with current time plus this many seconds (experts only!)\")\n         ;\n   command_line_options.add(_cli_options);\n   configuration_file_options.add(_cfg_options);\n}\n\nvoid application::initialize(const fc::path& data_dir, const boost::program_options::variables_map& options)\n{\n   my->_data_dir = data_dir;\n   my->_options = &options;\n\n   if ( options.count(\"io-threads\") )\n   {\n      const uint16_t num_threads = options[\"io-threads\"].as<uint16_t>();\n      fc::asio::default_io_service_scope::set_num_threads(num_threads);\n   }\n}\n\nvoid application::startup()\n{\n   try {\n      my->startup();\n   } catch ( const fc::exception& e ) {\n      elog( \"${e}\", (\"e\",e.to_detail_string()) );\n      throw;\n   } catch ( ... ) {\n      elog( \"unexpected exception\" );\n      throw;\n   }\n}\n\nvoid application::set_api_limit()\n{\n   try {\n      my->set_api_limit();\n   } catch ( const fc::exception& e ) {\n      elog( \"${e}\", (\"e\",e.to_detail_string()) );\n      throw;\n   } catch ( ... ) {\n      elog( \"unexpected exception\" );\n      throw;\n   }\n}\nstd::shared_ptr<abstract_plugin> application::get_plugin(const string& name) const\n{\n   return my->_active_plugins[name];\n}\n\nbool application::is_plugin_enabled(const string& name) const\n{\n   return !(my->_active_plugins.find(name) == my->_active_plugins.end());\n}\n\nnet::node_ptr application::p2p_node()\n{\n   return my->_p2p_network;\n}\n\nstd::shared_ptr<chain::database> application::chain_database() const\n{\n   return my->_chain_db;\n}\n\nvoid application::set_block_production(bool producing_blocks)\n{\n   my->_is_block_producer = producing_blocks;\n}\n\noptional< api_access_info > application::get_api_access_info( const string& username )const\n{\n   return my->get_api_access_info( username );\n}\n\nvoid application::set_api_access_info(const string& username, api_access_info&& permissions)\n{\n   my->set_api_access_info(username, std::move(permissions));\n}\n\nbool application::is_finished_syncing() const\n{\n   return my->_is_finished_syncing;\n}\n\nvoid graphene::app::application::enable_plugin(const string& name)\n{\n   FC_ASSERT(my->_available_plugins[name], \"Unknown plugin '\" + name + \"'\");\n   my->_active_plugins[name] = my->_available_plugins[name];\n   my->_active_plugins[name]->plugin_set_app(this);\n}\n\nvoid graphene::app::application::add_available_plugin(std::shared_ptr<graphene::app::abstract_plugin> p)\n{\n   my->_available_plugins[p->plugin_name()] = p;\n}\n\nvoid application::shutdown_plugins()\n{\n   for( auto& entry : my->_active_plugins )\n      entry.second->plugin_shutdown();\n   return;\n}\nvoid application::shutdown()\n{\n   if( my->_p2p_network )\n      my->_p2p_network->close();\n   if( my->_chain_db )\n   {\n      my->_chain_db->close();\n      my->_chain_db = nullptr;\n   }\n}\n\nvoid application::initialize_plugins( const boost::program_options::variables_map& options )\n{\n   for( auto& entry : my->_active_plugins )\n      entry.second->plugin_initialize( options );\n   return;\n}\n\nvoid application::startup_plugins()\n{\n   for( auto& entry : my->_active_plugins )\n   {\n      entry.second->plugin_startup();\n      ilog( \"Plugin ${name} started\", ( \"name\", entry.second->plugin_name() ) );\n   }\n   return;\n}\n\nconst application_options& application::get_options()\n{\n   return my->_app_options;\n}\n\n// namespace detail\n} }\n"
  },
  {
    "path": "libraries/app/application_impl.hxx",
    "content": "#pragma once\n\n#include <fc/network/http/websocket.hpp>\n#include <fc/thread/parallel.hpp>\n\n#include <graphene/app/application.hpp>\n#include <graphene/app/api_access.hpp>\n#include <graphene/chain/genesis_state.hpp>\n#include <graphene/protocol/types.hpp>\n#include <graphene/net/message.hpp>\n\nnamespace graphene { namespace app { namespace detail {\n\n\nclass application_impl : public net::node_delegate\n   {\n   public:\n      fc::optional<fc::temp_file> _lock_file;\n      bool _is_block_producer = false;\n      bool _force_validate = false;\n      application_options _app_options;\n\n      void reset_p2p_node(const fc::path& data_dir);\n\n      void new_connection( const fc::http::websocket_connection_ptr& c );\n\n      void reset_websocket_server();\n\n      void reset_websocket_tls_server();\n\n      explicit application_impl(application* self)\n         : _self(self),\n           _chain_db(std::make_shared<chain::database>())\n      {\n      }\n\n      virtual ~application_impl()\n      {\n      }\n\n      void set_dbg_init_key( graphene::chain::genesis_state_type& genesis, const std::string& init_key );\n      void set_api_limit();\n\n      void startup();\n\n      fc::optional< api_access_info > get_api_access_info(const string& username)const;\n\n      void set_api_access_info(const string& username, api_access_info&& permissions);\n\n      /**\n       * If delegate has the item, the network has no need to fetch it.\n       */\n      virtual bool has_item(const net::item_id& id) override;\n\n      /**\n       * @brief allows the application to validate an item prior to broadcasting to peers.\n       *\n       * @param sync_mode true if the message was fetched through the sync process, false during normal operation\n       * @returns true if this message caused the blockchain to switch forks, false if it did not\n       *\n       * @throws exception if error validating the item, otherwise the item is safe to broadcast on.\n       */\n      virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode,\n                                std::vector<fc::uint160_t>& contained_transaction_message_ids) override;\n\n      virtual void handle_transaction(const graphene::net::trx_message& transaction_message) override;\n\n      void handle_message(const graphene::net::message& message_to_process) override;\n\n      bool is_included_block(const graphene::chain::block_id_type& block_id);\n\n      /**\n       * Assuming all data elements are ordered in some way, this method should\n       * return up to limit ids that occur *after* the last ID in synopsis that\n       * we recognize.\n       *\n       * On return, remaining_item_count will be set to the number of items\n       * in our blockchain after the last item returned in the result,\n       * or 0 if the result contains the last item in the blockchain\n       */\n      virtual std::vector<graphene::net::item_hash_t> get_block_ids(const std::vector<graphene::net::item_hash_t>& blockchain_synopsis,\n                                                     uint32_t& remaining_item_count,\n                                                     uint32_t limit) override;\n\n      /**\n       * Given the hash of the requested data, fetch the body.\n       */\n      virtual graphene::net::message get_item(const graphene::net::item_id& id) override;\n\n      virtual graphene::chain::chain_id_type get_chain_id()const override;\n\n      /**\n       * Returns a synopsis of the blockchain used for syncing.  This consists of a list of\n       * block hashes at intervals exponentially increasing towards the genesis block.\n       * When syncing to a peer, the peer uses this data to determine if we're on the same\n       * fork as they are, and if not, what blocks they need to send us to get us on their\n       * fork.\n       *\n       * In the over-simplified case, this is a straighforward synopsis of our current\n       * preferred blockchain; when we first connect up to a peer, this is what we will be sending.\n       * It looks like this:\n       *   If the blockchain is empty, it will return the empty list.\n       *   If the blockchain has one block, it will return a list containing just that block.\n       *   If it contains more than one block:\n       *     the first element in the list will be the hash of the highest numbered block that\n       *         we cannot undo\n       *     the second element will be the hash of an item at the half way point in the undoable\n       *         segment of the blockchain\n       *     the third will be ~3/4 of the way through the undoable segment of the block chain\n       *     the fourth will be at ~7/8...\n       *       &c.\n       *     the last item in the list will be the hash of the most recent block on our preferred chain\n       * so if the blockchain had 26 blocks labeled a - z, the synopsis would be:\n       *    a n u x z\n       * the idea being that by sending a small (<30) number of block ids, we can summarize a huge\n       * blockchain.  The block ids are more dense near the end of the chain where because we are\n       * more likely to be almost in sync when we first connect, and forks are likely to be short.\n       * If the peer we're syncing with in our example is on a fork that started at block 'v',\n       * then they will reply to our synopsis with a list of all blocks starting from block 'u',\n       * the last block they know that we had in common.\n       *\n       * In the real code, there are several complications.\n       *\n       * First, as an optimization, we don't usually send a synopsis of the entire blockchain, we\n       * send a synopsis of only the segment of the blockchain that we have undo data for.  If their\n       * fork doesn't build off of something in our undo history, we would be unable to switch, so there's\n       * no reason to fetch the blocks.\n       *\n       * Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think\n       * we are missing, they only send a chunk of a few thousand blocks at once.  After we get those\n       * block ids, we need to request more blocks by sending another synopsis (we can't just say \"send me\n       * the next 2000 ids\" because they may have switched forks themselves and they don't track what\n       * they've sent us).  For faster performance, we want to get a fairly long list of block ids first,\n       * then start downloading the blocks.\n       * The peer doesn't handle these follow-up block id requests any different from the initial request;\n       * it treats the synopsis we send as our blockchain and bases its response entirely off that.  So to\n       * get the response we want (the next chunk of block ids following the last one they sent us, or,\n       * failing that, the shortest fork off of the last list of block ids they sent), we need to construct\n       * a synopsis as if our blockchain was made up of:\n       *    1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)\n       *    2. the blocks we've already pushed from their fork (if there's a fork)\n       *    3. the block ids they've previously sent us\n       * Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in\n       * number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.\n       * We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and\n       * fork database.  The reference_point parameter is the last block from that peer that has been\n       * successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on\n       * the main chain.\n       */\n      virtual std::vector<graphene::net::item_hash_t> get_blockchain_synopsis(const graphene::net::item_hash_t& reference_point,\n                                                               uint32_t number_of_blocks_after_reference_point) override;\n\n      /**\n       * Call this after the call to handle_message succeeds.\n       *\n       * @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call\n       * @param item_count the number of items known to the node that haven't been sent to handle_item() yet.\n       *                   After `item_count` more calls to handle_item(), the node will be in sync\n       */\n      virtual void sync_status(uint32_t item_type, uint32_t item_count) override;\n\n      /**\n       * Call any time the number of connected peers changes.\n       */\n      virtual void connection_count_changed(uint32_t c) override;\n\n      virtual uint32_t get_block_number(const graphene::net::item_hash_t& block_id) override;\n\n      /**\n       * Returns the time a block was produced (if block_id = 0, returns genesis time).\n       * If we don't know about the block, returns time_point_sec::min()\n       */\n      virtual fc::time_point_sec get_block_time(const graphene::net::item_hash_t& block_id) override;\n\n      virtual graphene::net::item_hash_t get_head_block_id() const override;\n\n      virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const override;\n\n      virtual void error_encountered(const std::string& message, const fc::oexception& error) override;\n\n      uint8_t get_current_block_interval_in_seconds() const override;\n\n      application* _self;\n\n      fc::path _data_dir;\n      const boost::program_options::variables_map* _options = nullptr;\n      api_access _apiaccess;\n\n      std::shared_ptr<graphene::chain::database>            _chain_db;\n      std::shared_ptr<graphene::net::node>                  _p2p_network;\n      std::shared_ptr<fc::http::websocket_server>      _websocket_server;\n      std::shared_ptr<fc::http::websocket_tls_server>  _websocket_tls_server;\n\n      std::map<string, std::shared_ptr<abstract_plugin>> _active_plugins;\n      std::map<string, std::shared_ptr<abstract_plugin>> _available_plugins;\n\n      bool _is_finished_syncing = false;\n   private:\n      fc::serial_valve valve;\n   };\n\n}}} // namespace graphene namespace app namespace detail\n"
  },
  {
    "path": "libraries/app/config_util.cpp",
    "content": "/*\n * Copyright (c) 2018 Lubos Ilcik, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/app/config_util.hpp>\n#include <graphene/chain/config.hpp>\n\n#include <fc/reflect/variant.hpp>\n#include <fc/string.hpp>\n#include <fc/exception/exception.hpp>\n#include <fc/log/console_appender.hpp>\n#include <fc/log/file_appender.hpp>\n#include <fc/log/logger_config.hpp>\n\n#include <boost/property_tree/ptree.hpp>\n#include <boost/property_tree/ini_parser.hpp>\n#include <boost/algorithm/string/predicate.hpp>\n#include <boost/algorithm/string/split.hpp>\n#include <boost/algorithm/string/split.hpp>\n#include <boost/algorithm/string.hpp>\n\n#include <fstream>\n\nnamespace bpo = boost::program_options;\n\nnamespace graphene { namespace app { namespace detail {\n\nclass deduplicator\n{\npublic:\n   deduplicator() : modifier(nullptr) {}\n\n   deduplicator(const boost::shared_ptr<bpo::option_description> (*mod_fn)(const boost::shared_ptr<bpo::option_description>&))\n   : modifier(mod_fn) {}\n\n   const boost::shared_ptr<bpo::option_description> next(const boost::shared_ptr<bpo::option_description>& o)\n   {\n      const std::string name = o->long_name();\n      if( seen.find( name ) != seen.end() )\n         return nullptr;\n      seen.insert(name);\n      return modifier ? modifier(o) : o;\n   }\n\nprivate:\n   boost::container::flat_set<std::string> seen;\n   const boost::shared_ptr<bpo::option_description> (*modifier)(const boost::shared_ptr<bpo::option_description>&);\n};\n\n} } } // graphene::app::detail\n\n// Currently, you can only specify the filenames and logging levels, which\n// are all most users would want to change.  At a later time, options can\n// be added to control rotation intervals, compression, and other seldom-\n// used features\nstatic void write_default_logging_config_to_stream(std::ostream& out)\n{\n   out << \"# declare an appender named \\\"stderr\\\" that writes messages to the console\\n\"\n          \"[log.console_appender.stderr]\\n\"\n          \"stream=std_error\\n\\n\"\n          \"# declare an appender named \\\"default\\\" that writes messages to default.log\\n\"\n          \"[log.file_appender.default]\\n\"\n          \"# filename can be absolute or relative to this config file\\n\"\n          \"filename=logs/default/default.log\\n\"\n          \"# Rotate log every ? minutes, if leave out default to 60\\n\"\n          \"rotation_interval=60\\n\"\n          \"# how long will logs be kept (in days), if leave out default to 1\\n\"\n          \"rotation_limit=7\\n\\n\"\n          \"# declare an appender named \\\"p2p\\\" that writes messages to p2p.log\\n\"\n          \"[log.file_appender.p2p]\\n\"\n          \"# filename can be absolute or relative to this config file\\n\"\n          \"filename=logs/p2p/p2p.log\\n\"\n          \"# Rotate log every ? minutes, if leave out default to 60\\n\"\n          \"rotation_interval=60\\n\"\n          \"# how long will logs be kept (in days), if leave out default to 1\\n\"\n          \"rotation_limit=7\\n\\n\"\n          \"# declare an appender named \\\"rpc\\\" that writes messages to rpc.log\\n\"\n          \"[log.file_appender.rpc]\\n\"\n          \"# filename can be absolute or relative to this config file\\n\"\n          \"filename=logs/rpc/rpc.log\\n\"\n          \"# Rotate log every ? minutes, if leave out default to 60\\n\"\n          \"rotation_interval=60\\n\"\n          \"# how long will logs be kept (in days), if leave out default to 1\\n\"\n          \"rotation_limit=7\\n\\n\"\n          \"# route any messages logged to the default logger to the \\\"stderr\\\" appender and\\n\"\n          \"# \\\"default\\\" appender we declared above, if they are info level or higher\\n\"\n          \"[logger.default]\\n\"\n          \"level=info\\n\"\n          \"appenders=stderr,default\\n\\n\"\n          \"# route messages sent to the \\\"p2p\\\" logger to the \\\"p2p\\\" appender declared above\\n\"\n          \"[logger.p2p]\\n\"\n          \"level=warn\\n\"\n          \"appenders=p2p\\n\\n\"\n          \"# route messages sent to the \\\"rpc\\\" logger to the \\\"rpc\\\" appender declared above\\n\"\n          \"[logger.rpc]\\n\"\n          \"level=error\\n\"\n          \"appenders=rpc\\n\\n\";\n}\n\n// logging config is too complicated to be parsed by boost::program_options,\n// so we do it by hand\nstatic fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename)\n{\n   try\n   {\n      fc::logging_config logging_config;\n      bool found_logging_config = false;\n\n      boost::property_tree::ptree config_ini_tree;\n      boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree);\n      for (const auto& section : config_ini_tree)\n      {\n         const std::string& section_name = section.first;\n         const boost::property_tree::ptree& section_tree = section.second;\n\n         const std::string console_appender_section_prefix = \"log.console_appender.\";\n         const std::string file_appender_section_prefix = \"log.file_appender.\";\n         const std::string logger_section_prefix = \"logger.\";\n\n         if (boost::starts_with(section_name, console_appender_section_prefix))\n         {\n            std::string console_appender_name = section_name.substr(console_appender_section_prefix.length());\n            std::string stream_name = section_tree.get<std::string>(\"stream\");\n\n            // construct a default console appender config here\n            // stdout/stderr will be taken from ini file, everything else hard-coded here\n            fc::console_appender::config console_appender_config;\n            console_appender_config.level_colors.emplace_back(\n            fc::console_appender::level_color(fc::log_level::debug,\n                                              fc::console_appender::color::green));\n            console_appender_config.level_colors.emplace_back(\n            fc::console_appender::level_color(fc::log_level::warn,\n                                              fc::console_appender::color::brown));\n            console_appender_config.level_colors.emplace_back(\n            fc::console_appender::level_color(fc::log_level::error,\n                                              fc::console_appender::color::cyan));\n            console_appender_config.stream = fc::variant(stream_name).as<fc::console_appender::stream::type>(GRAPHENE_MAX_NESTED_OBJECTS);\n            logging_config.appenders.push_back(fc::appender_config(console_appender_name, \"console\", fc::variant(console_appender_config, GRAPHENE_MAX_NESTED_OBJECTS)));\n            found_logging_config = true;\n         }\n         else if (boost::starts_with(section_name, file_appender_section_prefix))\n         {\n            std::string file_appender_name = section_name.substr(file_appender_section_prefix.length());\n            fc::path file_name = section_tree.get<std::string>(\"filename\");\n            if (file_name.is_relative())\n               file_name = fc::absolute(config_ini_filename).parent_path() / file_name;\n\n            int interval = section_tree.get_optional<int>(\"rotation_interval\").get_value_or(60);\n            int limit = section_tree.get_optional<int>(\"rotation_limit\").get_value_or(1);\n\n            // construct a default file appender config here\n            // filename will be taken from ini file, everything else hard-coded here\n            fc::file_appender::config file_appender_config;\n            file_appender_config.filename = file_name;\n            file_appender_config.flush = true;\n            file_appender_config.rotate = true;\n            file_appender_config.rotation_interval = fc::minutes(interval);\n            file_appender_config.rotation_limit = fc::days(limit);\n            logging_config.appenders.push_back(fc::appender_config(file_appender_name, \"file\", fc::variant(file_appender_config, GRAPHENE_MAX_NESTED_OBJECTS)));\n            found_logging_config = true;\n         }\n         else if (boost::starts_with(section_name, logger_section_prefix))\n         {\n            std::string logger_name = section_name.substr(logger_section_prefix.length());\n            std::string level_string = section_tree.get<std::string>(\"level\");\n            std::string appenders_string = section_tree.get<std::string>(\"appenders\");\n            fc::logger_config logger_config(logger_name);\n            logger_config.level = fc::variant(level_string).as<fc::log_level>(5);\n            boost::split(logger_config.appenders, appenders_string,\n                         boost::is_any_of(\" ,\"),\n                         boost::token_compress_on);\n            logging_config.loggers.push_back(logger_config);\n            found_logging_config = true;\n         }\n      }\n      if (found_logging_config)\n         return logging_config;\n      else\n         return fc::optional<fc::logging_config>();\n   }\n   FC_RETHROW_EXCEPTIONS(warn, \"\")\n}\n\nstatic const boost::shared_ptr<bpo::option_description> new_option_description( const std::string& name, const bpo::value_semantic* value, const std::string& description )\n{\n   bpo::options_description helper(\"\");\n   helper.add_options()( name.c_str(), value, description.c_str() );\n   return helper.options()[0];\n}\n\n\nstatic void load_config_file(const fc::path& config_ini_path, const bpo::options_description& cfg_options,\n                             bpo::variables_map& options )\n{\n   graphene::app::detail::deduplicator dedup;\n   bpo::options_description unique_options(\"BitShares Witness Node\");\n   for( const boost::shared_ptr<bpo::option_description> opt : cfg_options.options() )\n   {\n      const boost::shared_ptr<bpo::option_description> od = dedup.next(opt);\n      if( !od ) continue;\n      unique_options.add( od );\n   }\n\n   // get the basic options\n   bpo::store(bpo::parse_config_file<char>(config_ini_path.preferred_string().c_str(),\n                                           unique_options, true), options);\n}\n\nstatic bool load_logging_config_file(const fc::path& config_ini_path)\n{\n   // try to get logging options from the config file.\n   try\n   {\n      fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);\n      if (logging_config)\n      {\n         fc::configure_logging(*logging_config);\n         return true;\n      }\n   }\n   catch (const fc::exception& ex)\n   {\n      wlog(\"Error parsing logging config from logging config file ${config}, using default config\", (\"config\", config_ini_path.preferred_string()));\n   }\n   return false;\n}\n\nstatic void create_new_config_file(const fc::path& config_ini_path, const fc::path& data_dir,\n                                   const bpo::options_description& cfg_options )\n{\n   ilog(\"Writing new config file at ${path}\", (\"path\", config_ini_path));\n   if( !fc::exists(data_dir) )\n      fc::create_directories(data_dir);\n\n   auto modify_option_defaults = [](const boost::shared_ptr<bpo::option_description>& o) -> const boost::shared_ptr<bpo::option_description> {\n       const std::string& name = o->long_name();\n       if( name == \"partial-operations\" )\n          return new_option_description(name, bpo::value<bool>()->default_value(true), o->description() );\n       if( name == \"max-ops-per-account\" )\n          return new_option_description(name, bpo::value<int>()->default_value(100), o->description() );\n       return o;\n   };\n   graphene::app::detail::deduplicator dedup(modify_option_defaults);\n   std::ofstream out_cfg(config_ini_path.preferred_string());\n   std::string plugin_header_surrounding( 78, '=' );\n   for( const boost::shared_ptr<bpo::option_description> opt : cfg_options.options() )\n   {\n      const boost::shared_ptr<bpo::option_description> od = dedup.next(opt);\n      if( !od ) continue;\n\n      if( od->long_name().find(\"plugin-cfg-header-\") == 0 ) // it's a plugin header\n      {\n         out_cfg << \"\\n\";\n         out_cfg << \"# \" << plugin_header_surrounding << \"\\n\";\n         out_cfg << \"# \" << od->description() << \"\\n\";\n         out_cfg << \"# \" << plugin_header_surrounding << \"\\n\";\n         out_cfg << \"\\n\";\n         continue;\n      }\n\n      if( !od->description().empty() )\n         out_cfg << \"# \" << od->description() << \"\\n\";\n      boost::any store;\n      if( !od->semantic()->apply_default(store) )\n         out_cfg << \"# \" << od->long_name() << \" = \\n\";\n      else {\n         auto example = od->format_parameter();\n         if( example.empty() )\n            // This is a boolean switch\n            out_cfg << od->long_name() << \" = \" << \"false\\n\";\n         else {\n            // The string is formatted \"arg (=<interesting part>)\"\n            example.erase(0, 6);\n            example.erase(example.length()-1);\n            out_cfg << od->long_name() << \" = \" << example << \"\\n\";\n         }\n      }\n      out_cfg << \"\\n\";\n   }\n\n   out_cfg << \"\\n\"\n           << \"# \" << plugin_header_surrounding << \"\\n\"\n           << \"# logging options\\n\"\n           << \"# \" << plugin_header_surrounding << \"\\n\"\n           << \"#\\n\"\n           << \"# Logging configuration is loaded from logging.ini by default.\\n\"\n           << \"# If logging.ini exists, logging configuration added in this file will be ignored.\\n\";\n   out_cfg.close();\n}\n\nstatic void create_logging_config_file(const fc::path& config_ini_path, const fc::path& data_dir)\n{\n   ilog(\"Writing new config file at ${path}\", (\"path\", config_ini_path));\n   if (!exists(data_dir))\n   {\n      create_directories(data_dir);\n   }\n\n   std::ofstream out_cfg(config_ini_path.preferred_string());\n   write_default_logging_config_to_stream(out_cfg);\n   out_cfg.close();\n}\n\nnamespace graphene { namespace app {\n\n   void load_configuration_options(const fc::path& data_dir, const bpo::options_description& cfg_options, bpo::variables_map& options)\n   {\n      const auto config_ini_path = data_dir / \"config.ini\";\n      const auto logging_ini_path = data_dir / \"logging.ini\";\n\n      if(!exists(config_ini_path) && fc::exists(logging_ini_path))\n      {\n         // this is an uncommon case\n         create_new_config_file(config_ini_path, data_dir, cfg_options);\n      }\n      else if(!exists(config_ini_path))\n      {\n         // create default config.ini and logging.ini\n         create_new_config_file(config_ini_path, data_dir, cfg_options);\n         create_logging_config_file(logging_ini_path, data_dir);\n      }\n\n      // load witness node configuration\n      load_config_file(config_ini_path, cfg_options, options);\n\n      // load logging configuration\n      if (fc::exists(logging_ini_path))\n      {\n         load_logging_config_file(logging_ini_path);\n      }\n      else\n      {\n         // this is the legacy config.ini case\n         load_logging_config_file(config_ini_path);\n      }\n   }\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/app/database_api.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"database_api_impl.hxx\"\n\n#include <graphene/app/util.hpp>\n#include <graphene/chain/get_config.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/protocol/pts_address.hpp>\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include <fc/crypto/hex.hpp>\n#include <fc/rpc/api_connection.hpp>\n\n#include <boost/range/iterator_range.hpp>\n\n#include <cctype>\n\ntemplate class fc::api<graphene::app::database_api>;\n\nnamespace graphene { namespace app {\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Constructors                                                     //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\ndatabase_api::database_api( graphene::chain::database& db, const application_options* app_options )\n   : my( new database_api_impl( db, app_options ) ) {}\n\ndatabase_api::~database_api() {}\n\ndatabase_api_impl::database_api_impl( graphene::chain::database& db, const application_options* app_options )\n:_db(db), _app_options(app_options)\n{\n   dlog(\"creating database api ${x}\", (\"x\",int64_t(this)) );\n   _new_connection = _db.new_objects.connect([this](const vector<object_id_type>& ids,\n                                                    const flat_set<account_id_type>& impacted_accounts) {\n                                on_objects_new(ids, impacted_accounts);\n                                });\n   _change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids,\n                                                           const flat_set<account_id_type>& impacted_accounts) {\n                                on_objects_changed(ids, impacted_accounts);\n                                });\n   _removed_connection = _db.removed_objects.connect([this](const vector<object_id_type>& ids,\n                                                            const vector<const object*>& objs,\n                                                            const flat_set<account_id_type>& impacted_accounts) {\n                                on_objects_removed(ids, objs, impacted_accounts);\n                                });\n   _applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); });\n\n   _pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){\n                                if( _pending_trx_callback )\n                                   _pending_trx_callback( fc::variant(trx, GRAPHENE_MAX_NESTED_OBJECTS) );\n                      });\n   try\n   {\n      amount_in_collateral_index = &_db.get_index_type< primary_index< call_order_index > >()\n                                    .get_secondary_index<graphene::api_helper_indexes::amount_in_collateral_index>();\n   }\n   catch( fc::assert_exception& e )\n   {\n      amount_in_collateral_index = nullptr;\n   }\n}\n\ndatabase_api_impl::~database_api_impl()\n{\n   dlog(\"freeing database api ${x}\", (\"x\",int64_t(this)) );\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Objects                                                          //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nfc::variants database_api::get_objects( const vector<object_id_type>& ids, optional<bool> subscribe )const\n{\n   return my->get_objects( ids, subscribe );\n}\n\nfc::variants database_api_impl::get_objects( const vector<object_id_type>& ids, optional<bool> subscribe )const\n{\n   bool to_subscribe = get_whether_to_subscribe( subscribe );\n\n   fc::variants result;\n   result.reserve(ids.size());\n\n   std::transform(ids.begin(), ids.end(), std::back_inserter(result),\n                  [this,to_subscribe](object_id_type id) -> fc::variant {\n      if(auto obj = _db.find_object(id))\n      {\n         if( to_subscribe && !id.is<operation_history_id_type>() && !id.is<account_transaction_history_id_type>() )\n            this->subscribe_to_item( id );\n         return obj->to_variant();\n      }\n      return {};\n   });\n\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Subscriptions                                                    //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvoid database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )\n{\n   my->set_subscribe_callback( cb, notify_remove_create );\n}\n\nvoid database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )\n{\n   if( notify_remove_create )\n   {\n      FC_ASSERT( _app_options && _app_options->enable_subscribe_to_all,\n                 \"Subscribing to universal object creation and removal is disallowed in this server.\" );\n   }\n\n   cancel_all_subscriptions(false, false);\n\n   _subscribe_callback = cb;\n   _notify_remove_create = notify_remove_create;\n}\n\nvoid database_api::set_auto_subscription( bool enable )\n{\n   my->set_auto_subscription( enable );\n}\n\nvoid database_api_impl::set_auto_subscription( bool enable )\n{\n   _enabled_auto_subscription = enable;\n}\n\nvoid database_api::set_pending_transaction_callback( std::function<void(const variant&)> cb )\n{\n   my->set_pending_transaction_callback( cb );\n}\n\nvoid database_api_impl::set_pending_transaction_callback( std::function<void(const variant&)> cb )\n{\n   _pending_trx_callback = cb;\n}\n\nvoid database_api::set_block_applied_callback( std::function<void(const variant& block_id)> cb )\n{\n   my->set_block_applied_callback( cb );\n}\n\nvoid database_api_impl::set_block_applied_callback( std::function<void(const variant& block_id)> cb )\n{\n   _block_applied_callback = cb;\n}\n\nvoid database_api::cancel_all_subscriptions()\n{\n   my->cancel_all_subscriptions(true, true);\n}\n\nvoid database_api_impl::cancel_all_subscriptions( bool reset_callback, bool reset_market_subscriptions )\n{\n   if ( reset_callback )\n      _subscribe_callback = std::function<void(const fc::variant&)>();\n\n   if ( reset_market_subscriptions )\n      _market_subscriptions.clear();\n\n   _notify_remove_create = false;\n   _subscribed_accounts.clear();\n   static fc::bloom_parameters param(10000, 1.0/100, 1024*8*8*2);\n   _subscribe_filter = fc::bloom_filter(param);\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Blocks and transactions                                          //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\noptional<block_header> database_api::get_block_header(uint32_t block_num)const\n{\n   return my->get_block_header( block_num );\n}\n\noptional<block_header> database_api_impl::get_block_header(uint32_t block_num) const\n{\n   auto result = _db.fetch_block_by_number(block_num);\n   if(result)\n      return *result;\n   return {};\n}\nmap<uint32_t, optional<block_header>> database_api::get_block_header_batch(const vector<uint32_t> block_nums)const\n{\n   return my->get_block_header_batch( block_nums );\n}\n\nmap<uint32_t, optional<block_header>> database_api_impl::get_block_header_batch(\n                                         const vector<uint32_t> block_nums) const\n{\n   map<uint32_t, optional<block_header>> results;\n   for (const uint32_t block_num : block_nums)\n   {\n      results[block_num] = get_block_header(block_num);\n   }\n   return results;\n}\n\noptional<signed_block> database_api::get_block(uint32_t block_num)const\n{\n   return my->get_block( block_num );\n}\n\noptional<signed_block> database_api_impl::get_block(uint32_t block_num)const\n{\n   return _db.fetch_block_by_number(block_num);\n}\n\nprocessed_transaction database_api::get_transaction( uint32_t block_num, uint32_t trx_in_block )const\n{\n   return my->get_transaction( block_num, trx_in_block );\n}\n\noptional<signed_transaction> database_api::get_recent_transaction_by_id( const transaction_id_type& id )const\n{\n   try {\n      return my->_db.get_recent_transaction( id );\n   } catch ( ... ) {\n      return optional<signed_transaction>();\n   }\n}\n\nprocessed_transaction database_api_impl::get_transaction(uint32_t block_num, uint32_t trx_num)const\n{\n   auto opt_block = _db.fetch_block_by_number(block_num);\n   FC_ASSERT( opt_block );\n   FC_ASSERT( opt_block->transactions.size() > trx_num );\n   return opt_block->transactions[trx_num];\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Globals                                                          //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nchain_property_object database_api::get_chain_properties()const\n{\n   return my->get_chain_properties();\n}\n\nchain_property_object database_api_impl::get_chain_properties()const\n{\n   return _db.get(chain_property_id_type());\n}\n\nglobal_property_object database_api::get_global_properties()const\n{\n   return my->get_global_properties();\n}\n\nglobal_property_object database_api_impl::get_global_properties()const\n{\n   return _db.get(global_property_id_type());\n}\n\nfc::variant_object database_api::get_config()const\n{\n   return my->get_config();\n}\n\nfc::variant_object database_api_impl::get_config()const\n{\n   return graphene::chain::get_config();\n}\n\nchain_id_type database_api::get_chain_id()const\n{\n   return my->get_chain_id();\n}\n\nchain_id_type database_api_impl::get_chain_id()const\n{\n   return _db.get_chain_id();\n}\n\ndynamic_global_property_object database_api::get_dynamic_global_properties()const\n{\n   return my->get_dynamic_global_properties();\n}\n\ndynamic_global_property_object database_api_impl::get_dynamic_global_properties()const\n{\n   return _db.get(dynamic_global_property_id_type());\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Keys                                                             //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<flat_set<account_id_type>> database_api::get_key_references( vector<public_key_type> key )const\n{\n   return my->get_key_references( key );\n}\n\n/**\n *  @return all accounts that referr to the key or account id in their owner or active authorities.\n */\nvector<flat_set<account_id_type>> database_api_impl::get_key_references( vector<public_key_type> keys )const\n{\n   // api_helper_indexes plugin is required for accessing the secondary index\n   FC_ASSERT( _app_options && _app_options->has_api_helper_indexes_plugin,\n              \"api_helper_indexes plugin is not enabled on this server.\" );\n\n   const auto configured_limit = _app_options->api_limit_get_key_references;\n   FC_ASSERT( keys.size() <= configured_limit,\n              \"Number of querying keys can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& idx = _db.get_index_type<account_index>();\n   const auto& aidx = dynamic_cast<const base_primary_index&>(idx);\n   const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();\n\n   vector< flat_set<account_id_type> > final_result;\n   final_result.reserve(keys.size());\n\n   for( auto& key : keys )\n   {\n      address a1( pts_address(key, false, 56) );\n      address a2( pts_address(key, true, 56) );\n      address a3( pts_address(key, false, 0)  );\n      address a4( pts_address(key, true, 0)  );\n      address a5( key );\n\n      flat_set<account_id_type> result;\n\n      for( auto& a : {a1,a2,a3,a4,a5} )\n      {\n          auto itr = refs.account_to_address_memberships.find(a);\n          if( itr != refs.account_to_address_memberships.end() )\n          {\n             result.reserve( result.size() + itr->second.size() );\n             for( auto item : itr->second )\n             {\n                result.insert(item);\n             }\n          }\n      }\n\n      auto itr = refs.account_to_key_memberships.find(key);\n      if( itr != refs.account_to_key_memberships.end() )\n      {\n         result.reserve( result.size() + itr->second.size() );\n         for( auto item : itr->second ) result.insert(item);\n      }\n      final_result.emplace_back( std::move(result) );\n   }\n\n   return final_result;\n}\n\nbool database_api::is_public_key_registered(string public_key) const\n{\n    return my->is_public_key_registered(public_key);\n}\n\nbool database_api_impl::is_public_key_registered(string public_key) const\n{\n    // Short-circuit\n    if (public_key.empty()) {\n        return false;\n    }\n\n    // Search among all keys using an existing map of *current* account keys\n    public_key_type key;\n    try {\n        key = public_key_type(public_key);\n    } catch ( ... ) {\n        // An invalid public key was detected\n        return false;\n    }\n\n   // api_helper_indexes plugin is required for accessing the secondary index\n   FC_ASSERT( _app_options && _app_options->has_api_helper_indexes_plugin,\n              \"api_helper_indexes plugin is not enabled on this server.\" );\n\n    const auto& idx = _db.get_index_type<account_index>();\n    const auto& aidx = dynamic_cast<const base_primary_index&>(idx);\n    const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();\n    auto itr = refs.account_to_key_memberships.find(key);\n    bool is_known = itr != refs.account_to_key_memberships.end();\n\n    return is_known;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Accounts                                                         //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\naccount_id_type database_api::get_account_id_from_string(const std::string& name_or_id)const\n{\n   return my->get_account_from_string( name_or_id )->id;\n}\n\nvector<optional<account_object>> database_api::get_accounts( const vector<std::string>& account_names_or_ids,\n                                                             optional<bool> subscribe )const\n{\n   return my->get_accounts( account_names_or_ids, subscribe );\n}\n\nvector<optional<account_object>> database_api_impl::get_accounts( const vector<std::string>& account_names_or_ids,\n                                                                  optional<bool> subscribe )const\n{\n   bool to_subscribe = get_whether_to_subscribe( subscribe );\n   vector<optional<account_object>> result; result.reserve(account_names_or_ids.size());\n   std::transform(account_names_or_ids.begin(), account_names_or_ids.end(), std::back_inserter(result),\n                  [this,to_subscribe](std::string id_or_name) -> optional<account_object> {\n\n      const account_object *account = get_account_from_string(id_or_name, false);\n      if(account == nullptr)\n         return {};\n      if( to_subscribe )\n         subscribe_to_item( account->id );\n      return *account;\n   });\n   return result;\n}\n\nstd::map<string,full_account> database_api::get_full_accounts( const vector<string>& names_or_ids,\n                                                               optional<bool> subscribe )\n{\n   return my->get_full_accounts( names_or_ids, subscribe );\n}\n\nstd::map<std::string, full_account> database_api_impl::get_full_accounts( const vector<std::string>& names_or_ids,\n                                                                          optional<bool> subscribe )\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_full_accounts;\n   FC_ASSERT( names_or_ids.size() <= configured_limit,\n              \"Number of querying accounts can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   bool to_subscribe = get_whether_to_subscribe( subscribe );\n\n   std::map<std::string, full_account> results;\n\n   for (const std::string& account_name_or_id : names_or_ids)\n   {\n      const account_object* account = get_account_from_string(account_name_or_id, false);\n      if (account == nullptr)\n         continue;\n\n      if( to_subscribe )\n      {\n         if(_subscribed_accounts.size() < 100) {\n            _subscribed_accounts.insert( account->get_id() );\n            subscribe_to_item( account->id );\n         }\n      }\n\n      full_account acnt;\n      acnt.account = *account;\n      acnt.statistics = account->statistics(_db);\n      acnt.registrar_name = account->registrar(_db).name;\n      acnt.referrer_name = account->referrer(_db).name;\n      acnt.lifetime_referrer_name = account->lifetime_referrer(_db).name;\n      acnt.votes = lookup_vote_ids( vector<vote_id_type>( account->options.votes.begin(),\n                                                          account->options.votes.end() ) );\n\n      if (account->cashback_vb)\n      {\n         acnt.cashback_balance = account->cashback_balance(_db);\n      }\n\n      size_t api_limit_get_full_accounts_lists = static_cast<size_t>(\n                _app_options->api_limit_get_full_accounts_lists );\n\n      // Add the account's proposals (if the data is available)\n      if( _app_options && _app_options->has_api_helper_indexes_plugin )\n      {\n         const auto& proposal_idx = _db.get_index_type< primary_index< proposal_index > >();\n         const auto& proposals_by_account = proposal_idx.get_secondary_index<\n                                                  graphene::chain::required_approval_index>();\n\n         auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id );\n         if( required_approvals_itr != proposals_by_account._account_to_proposals.end() )\n         {\n            acnt.proposals.reserve( std::min(required_approvals_itr->second.size(),\n                                             api_limit_get_full_accounts_lists) );\n            for( auto proposal_id : required_approvals_itr->second )\n            {\n               if(acnt.proposals.size() >= api_limit_get_full_accounts_lists) {\n                  acnt.more_data_available.proposals = true;\n                  break;\n               }\n               acnt.proposals.push_back(proposal_id(_db));\n            }\n         }\n      }\n\n      // Add the account's balances\n      const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().\n            get_secondary_index< balances_by_account_index >().get_account_balances( account->id );\n      for( const auto& balance : balances )\n      {\n         if(acnt.balances.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.balances = true;\n            break;\n         }\n         acnt.balances.emplace_back(*balance.second);\n      }\n\n      // Add the account's vesting balances\n      auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>()\n                              .equal_range(account->id);\n      for(auto itr = vesting_range.first; itr != vesting_range.second; ++itr)\n      {\n         if(acnt.vesting_balances.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.vesting_balances = true;\n            break;\n         }\n         acnt.vesting_balances.emplace_back(*itr);\n      }\n\n      // Add the account's orders\n      auto order_range = _db.get_index_type<limit_order_index>().indices().get<by_account>()\n                            .equal_range(account->id);\n      for(auto itr = order_range.first; itr != order_range.second; ++itr)\n      {\n         if(acnt.limit_orders.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.limit_orders = true;\n            break;\n         }\n         acnt.limit_orders.emplace_back(*itr);\n      }\n      auto call_range = _db.get_index_type<call_order_index>().indices().get<by_account>().equal_range(account->id);\n      for(auto itr = call_range.first; itr != call_range.second; ++itr)\n      {\n         if(acnt.call_orders.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.call_orders = true;\n            break;\n         }\n         acnt.call_orders.emplace_back(*itr);\n      }\n      auto settle_range = _db.get_index_type<force_settlement_index>().indices().get<by_account>()\n                             .equal_range(account->id);\n      for(auto itr = settle_range.first; itr != settle_range.second; ++itr)\n      {\n         if(acnt.settle_orders.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.settle_orders = true;\n            break;\n         }\n         acnt.settle_orders.emplace_back(*itr);\n      }\n\n      // get assets issued by user\n      auto asset_range = _db.get_index_type<asset_index>().indices().get<by_issuer>().equal_range(account->id);\n      for(auto itr = asset_range.first; itr != asset_range.second; ++itr)\n      {\n         if(acnt.assets.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.assets = true;\n            break;\n         }\n         acnt.assets.emplace_back(itr->id);\n      }\n\n      // get withdraws permissions\n      auto withdraw_indices = _db.get_index_type<withdraw_permission_index>().indices();\n      auto withdraw_from_range = withdraw_indices.get<by_from>().equal_range(account->id);\n      for(auto itr = withdraw_from_range.first; itr != withdraw_from_range.second; ++itr)\n      {\n         if(acnt.withdraws_from.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.withdraws_from = true;\n            break;\n         }\n         acnt.withdraws_from.emplace_back(*itr);\n      }\n      auto withdraw_authorized_range = withdraw_indices.get<by_authorized>().equal_range(account->id);\n      for(auto itr = withdraw_authorized_range.first; itr != withdraw_authorized_range.second; ++itr)\n      {\n         if(acnt.withdraws_to.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.withdraws_to = true;\n            break;\n         }\n         acnt.withdraws_to.emplace_back(*itr);\n      }\n\n      // get htlcs\n      auto htlc_from_range = _db.get_index_type<htlc_index>().indices().get<by_from_id>().equal_range(account->id);\n      for(auto itr = htlc_from_range.first; itr != htlc_from_range.second; ++itr)\n      {\n         if(acnt.htlcs_from.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.htlcs_from = true;\n            break;\n         }\n         acnt.htlcs_from.emplace_back(*itr);\n      }\n      auto htlc_to_range = _db.get_index_type<htlc_index>().indices().get<by_to_id>().equal_range(account->id);\n      for(auto itr = htlc_to_range.first; itr != htlc_to_range.second; ++itr)\n      {\n         if(acnt.htlcs_to.size() >= api_limit_get_full_accounts_lists) {\n            acnt.more_data_available.htlcs_to = true;\n            break;\n         }\n         acnt.htlcs_to.emplace_back(*itr);\n      }\n\n      results[account_name_or_id] = acnt;\n   }\n   return results;\n}\n\noptional<account_object> database_api::get_account_by_name( string name )const\n{\n   return my->get_account_by_name( name );\n}\n\noptional<account_object> database_api_impl::get_account_by_name( string name )const\n{\n   const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();\n   auto itr = idx.find(name);\n   if (itr != idx.end())\n      return *itr;\n   return optional<account_object>();\n}\n\nvector<account_id_type> database_api::get_account_references( const std::string account_id_or_name )const\n{\n   return my->get_account_references( account_id_or_name );\n}\n\nvector<account_id_type> database_api_impl::get_account_references( const std::string account_id_or_name )const\n{\n   // api_helper_indexes plugin is required for accessing the secondary index\n   FC_ASSERT( _app_options && _app_options->has_api_helper_indexes_plugin,\n              \"api_helper_indexes plugin is not enabled on this server.\" );\n\n   const auto& idx = _db.get_index_type<account_index>();\n   const auto& aidx = dynamic_cast<const base_primary_index&>(idx);\n   const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();\n   const account_id_type account_id = get_account_from_string(account_id_or_name)->id;\n   auto itr = refs.account_to_account_memberships.find(account_id);\n   vector<account_id_type> result;\n\n   if( itr != refs.account_to_account_memberships.end() )\n   {\n      result.reserve( itr->second.size() );\n      for( auto item : itr->second ) result.push_back(item);\n   }\n   return result;\n}\n\nvector<optional<account_object>> database_api::lookup_account_names(const vector<string>& account_names)const\n{\n   return my->lookup_account_names( account_names );\n}\n\nvector<optional<account_object>> database_api_impl::lookup_account_names(const vector<string>& account_names)const\n{\n   const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();\n   vector<optional<account_object> > result;\n   result.reserve(account_names.size());\n   std::transform(account_names.begin(), account_names.end(), std::back_inserter(result),\n                  [&accounts_by_name](const string& name) -> optional<account_object> {\n      auto itr = accounts_by_name.find(name);\n      return itr == accounts_by_name.end()? optional<account_object>() : *itr;\n   });\n   return result;\n}\n\nmap<string,account_id_type> database_api::lookup_accounts( const string& lower_bound_name,\n                                                           uint32_t limit,\n                                                           optional<bool> subscribe )const\n{\n   return my->lookup_accounts( lower_bound_name, limit, subscribe );\n}\n\nmap<string,account_id_type> database_api_impl::lookup_accounts( const string& lower_bound_name,\n                                                                uint32_t limit,\n                                                                optional<bool> subscribe )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_lookup_accounts;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();\n   map<string,account_id_type> result;\n\n   if( limit == 0 ) // shortcut to save a database query\n      return result;\n   // In addition to the common auto-subscription rules, here we auto-subscribe if only look for one account\n   bool to_subscribe = (limit == 1 && get_whether_to_subscribe( subscribe ));\n   for( auto itr = accounts_by_name.lower_bound(lower_bound_name);\n        limit-- && itr != accounts_by_name.end();\n        ++itr )\n   {\n      result.insert(make_pair(itr->name, itr->get_id()));\n      if( to_subscribe )\n         subscribe_to_item( itr->id );\n   }\n\n   return result;\n}\n\nuint64_t database_api::get_account_count()const\n{\n   return my->get_account_count();\n}\n\nuint64_t database_api_impl::get_account_count()const\n{\n   return _db.get_index_type<account_index>().indices().size();\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Balances                                                         //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<asset> database_api::get_account_balances( const std::string& account_name_or_id,\n                                                  const flat_set<asset_id_type>& assets )const\n{\n   return my->get_account_balances( account_name_or_id, assets );\n}\n\nvector<asset> database_api_impl::get_account_balances( const std::string& account_name_or_id,\n                                                       const flat_set<asset_id_type>& assets )const\n{\n   const account_object* account = get_account_from_string(account_name_or_id);\n   account_id_type acnt = account->id;\n   vector<asset> result;\n   if (assets.empty())\n   {\n      // if the caller passes in an empty list of assets, return balances for all assets the account owns\n      const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >();\n      const auto& balances = balance_index.get_secondary_index< balances_by_account_index >()\n                                          .get_account_balances( acnt );\n      for( const auto& balance : balances )\n         result.push_back( balance.second->get_balance() );\n   }\n   else\n   {\n      result.reserve(assets.size());\n\n      std::transform(assets.begin(), assets.end(), std::back_inserter(result),\n                     [this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });\n   }\n\n   return result;\n}\n\nvector<asset> database_api::get_named_account_balances( const std::string& name,\n                                                        const flat_set<asset_id_type>& assets )const\n{\n   return my->get_account_balances( name, assets );\n}\n\nvector<balance_object> database_api::get_balance_objects( const vector<address>& addrs )const\n{\n   return my->get_balance_objects( addrs );\n}\n\nvector<balance_object> database_api_impl::get_balance_objects( const vector<address>& addrs )const\n{\n   try\n   {\n      const auto& bal_idx = _db.get_index_type<balance_index>();\n      const auto& by_owner_idx = bal_idx.indices().get<by_owner>();\n\n      vector<balance_object> result;\n\n      for( const auto& owner : addrs )\n      {\n         auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) );\n         while( itr != by_owner_idx.end() && itr->owner == owner )\n         {\n            result.push_back( *itr );\n            ++itr;\n         }\n      }\n      return result;\n   }\n   FC_CAPTURE_AND_RETHROW( (addrs) )\n}\n\nvector<asset> database_api::get_vested_balances( const vector<balance_id_type>& objs )const\n{\n   return my->get_vested_balances( objs );\n}\n\nvector<asset> database_api_impl::get_vested_balances( const vector<balance_id_type>& objs )const\n{\n   try\n   {\n      vector<asset> result;\n      result.reserve( objs.size() );\n      auto now = _db.head_block_time();\n      for( auto obj : objs )\n         result.push_back( obj(_db).available( now ) );\n      return result;\n   } FC_CAPTURE_AND_RETHROW( (objs) )\n}\n\nvector<vesting_balance_object> database_api::get_vesting_balances( const std::string account_id_or_name )const\n{\n   return my->get_vesting_balances( account_id_or_name );\n}\n\nvector<vesting_balance_object> database_api_impl::get_vesting_balances( const std::string account_id_or_name )const\n{\n   try\n   {\n      const account_id_type account_id = get_account_from_string(account_id_or_name)->id;\n      vector<vesting_balance_object> result;\n      auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>()\n                              .equal_range(account_id);\n      std::for_each(vesting_range.first, vesting_range.second,\n                    [&result](const vesting_balance_object& balance) {\n                       result.emplace_back(balance);\n                    });\n      return result;\n   }\n   FC_CAPTURE_AND_RETHROW( (account_id_or_name) );\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Assets                                                           //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nasset_id_type database_api::get_asset_id_from_string(const std::string& symbol_or_id)const\n{\n   return my->get_asset_from_string( symbol_or_id )->id;\n}\n\nvector<optional<extended_asset_object>> database_api::get_assets(\n      const vector<std::string>& asset_symbols_or_ids,\n      optional<bool> subscribe )const\n{\n   return my->get_assets( asset_symbols_or_ids, subscribe );\n}\n\nvector<optional<extended_asset_object>> database_api_impl::get_assets(\n      const vector<std::string>& asset_symbols_or_ids,\n      optional<bool> subscribe )const\n{\n   bool to_subscribe = get_whether_to_subscribe( subscribe );\n   vector<optional<extended_asset_object>> result; result.reserve(asset_symbols_or_ids.size());\n   std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result),\n                  [this,to_subscribe](std::string id_or_name) -> optional<extended_asset_object> {\n\n      const asset_object* asset_obj = get_asset_from_string( id_or_name, false );\n      if( asset_obj == nullptr )\n         return {};\n      if( to_subscribe )\n         subscribe_to_item( asset_obj->id );\n      return extend_asset( *asset_obj );\n   });\n   return result;\n}\n\nvector<extended_asset_object> database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const\n{\n   return my->list_assets( lower_bound_symbol, limit );\n}\n\nvector<extended_asset_object> database_api_impl::list_assets(const string& lower_bound_symbol, uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_assets;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();\n   vector<extended_asset_object> result;\n   result.reserve(limit);\n\n   auto itr = assets_by_symbol.lower_bound(lower_bound_symbol);\n\n   if( lower_bound_symbol == \"\" )\n      itr = assets_by_symbol.begin();\n\n   while(limit-- && itr != assets_by_symbol.end())\n      result.emplace_back( extend_asset( *itr++ ) );\n\n   return result;\n}\n\nuint64_t database_api::get_asset_count()const\n{\n   return my->get_asset_count();\n}\n\nuint64_t database_api_impl::get_asset_count()const\n{\n   return _db.get_index_type<asset_index>().indices().size();\n}\n\nvector<extended_asset_object> database_api::get_assets_by_issuer(const std::string& issuer_name_or_id,\n                                                                 asset_id_type start, uint32_t limit)const\n{\n   return my->get_assets_by_issuer(issuer_name_or_id, start, limit);\n}\n\nvector<extended_asset_object> database_api_impl::get_assets_by_issuer(const std::string& issuer_name_or_id,\n                                                                      asset_id_type start, uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_assets;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<extended_asset_object> result;\n   const account_id_type account = get_account_from_string(issuer_name_or_id)->id;\n   const auto& asset_idx = _db.get_index_type<asset_index>().indices().get<by_issuer>();\n   auto asset_index_end = asset_idx.end();\n   auto asset_itr = asset_idx.lower_bound(boost::make_tuple(account, start));\n   while(asset_itr != asset_index_end && asset_itr->issuer == account && result.size() < limit)\n   {\n      result.emplace_back( extend_asset( *asset_itr ) );\n      ++asset_itr;\n   }\n   return result;\n}\n\nvector<optional<extended_asset_object>> database_api::lookup_asset_symbols(\n                                                         const vector<string>& symbols_or_ids )const\n{\n   return my->lookup_asset_symbols( symbols_or_ids );\n}\n\nvector<optional<extended_asset_object>> database_api_impl::lookup_asset_symbols(\n                                                         const vector<string>& symbols_or_ids )const\n{\n   const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();\n   vector<optional<extended_asset_object> > result;\n   result.reserve(symbols_or_ids.size());\n   std::transform(symbols_or_ids.begin(), symbols_or_ids.end(), std::back_inserter(result),\n                  [this, &assets_by_symbol](const string& symbol_or_id) -> optional<extended_asset_object> {\n      if( !symbol_or_id.empty() && std::isdigit(symbol_or_id[0]) )\n      {\n         auto ptr = _db.find(variant(symbol_or_id, 1).as<asset_id_type>(1));\n         return ptr == nullptr? optional<extended_asset_object>() : extend_asset( *ptr );\n      }\n      auto itr = assets_by_symbol.find(symbol_or_id);\n      return itr == assets_by_symbol.end()? optional<extended_asset_object>() : extend_asset( *itr );\n   });\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Markets / feeds                                                  //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<limit_order_object> database_api::get_limit_orders(std::string a, std::string b, uint32_t limit)const\n{\n   return my->get_limit_orders( a, b, limit );\n}\n\nvector<limit_order_object> database_api_impl::get_limit_orders( const std::string& a, const std::string& b,\n                                                                uint32_t limit )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_limit_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const asset_id_type asset_a_id = get_asset_from_string(a)->id;\n   const asset_id_type asset_b_id = get_asset_from_string(b)->id;\n\n   return get_limit_orders(asset_a_id, asset_b_id, limit);\n}\n\nvector<limit_order_object> database_api::get_limit_orders_by_account( const string& account_name_or_id,\n                              optional<uint32_t> limit, optional<limit_order_id_type> start_id )\n{\n   return my->get_limit_orders_by_account( account_name_or_id, limit, start_id );\n}\n\nvector<limit_order_object> database_api_impl::get_limit_orders_by_account( const string& account_name_or_id,\n                              optional<uint32_t> olimit, optional<limit_order_id_type> ostart_id )\n{\n   uint32_t limit = olimit.valid() ? *olimit : 101;\n\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_limit_orders_by_account;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<limit_order_object> results;\n\n   const account_object* account = get_account_from_string(account_name_or_id);\n   if (account == nullptr)\n      return results;\n\n   limit_order_id_type start_id = ostart_id.valid() ? *ostart_id : limit_order_id_type();\n\n   const auto& index_by_account = _db.get_index_type<limit_order_index>().indices().get<by_account>();\n   auto lower_itr = index_by_account.lower_bound( std::make_tuple( account->id, start_id ) );\n   auto upper_itr = index_by_account.upper_bound( account->id );\n\n   results.reserve( limit );\n   uint32_t count = 0;\n   for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)\n   {\n      const limit_order_object &order = *lower_itr;\n      results.emplace_back(order);\n   }\n\n   return results;\n}\n\nvector<limit_order_object> database_api::get_account_limit_orders(\n                              const string& account_name_or_id, const string &base, const string &quote,\n                              uint32_t limit, optional<limit_order_id_type> ostart_id, optional<price> ostart_price )\n{\n   return my->get_account_limit_orders( account_name_or_id, base, quote, limit, ostart_id, ostart_price );\n}\n\nvector<limit_order_object> database_api_impl::get_account_limit_orders(\n                              const string& account_name_or_id, const string &base, const string &quote,\n                              uint32_t limit, optional<limit_order_id_type> ostart_id, optional<price> ostart_price )\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_account_limit_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<limit_order_object>   results;\n   uint32_t                     count = 0;\n\n   const account_object* account = get_account_from_string(account_name_or_id);\n   if (account == nullptr)\n      return results;\n\n   auto assets = lookup_asset_symbols( {base, quote} );\n   FC_ASSERT( assets[0], \"Invalid base asset symbol: ${s}\", (\"s\",base) );\n   FC_ASSERT( assets[1], \"Invalid quote asset symbol: ${s}\", (\"s\",quote) );\n\n   auto base_id = assets[0]->id;\n   auto quote_id = assets[1]->id;\n\n   if (ostart_price.valid()) {\n      FC_ASSERT(ostart_price->base.asset_id == base_id, \"Base asset inconsistent with start price\");\n      FC_ASSERT(ostart_price->quote.asset_id == quote_id, \"Quote asset inconsistent with start price\");\n   }\n\n   const auto& index_by_account = _db.get_index_type<limit_order_index>().indices().get<by_account_price>();\n   limit_order_multi_index_type::index<by_account_price>::type::const_iterator lower_itr;\n   limit_order_multi_index_type::index<by_account_price>::type::const_iterator upper_itr;\n\n   // if both order_id and price are invalid, query the first page\n   if ( !ostart_id.valid() && !ostart_price.valid() )\n   {\n      lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, price::max(base_id, quote_id)));\n   }\n   else if ( ostart_id.valid() )\n   {\n      // in case of the order been deleted during page querying\n      const limit_order_object *p_loo = _db.find(*ostart_id);\n\n      if ( !p_loo )\n      {\n         if ( ostart_price.valid() )\n         {\n            lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price, *ostart_id));\n         }\n         else\n         {\n            // start order id been deleted, yet not provided price either\n            FC_THROW(\"Order id invalid (maybe just been canceled?), and start price not provided\");\n         }\n      }\n      else\n      {\n         const limit_order_object &loo = *p_loo;\n\n         // in case of the order not belongs to specified account or market\n         FC_ASSERT(loo.sell_price.base.asset_id == base_id, \"Order base asset inconsistent\");\n         FC_ASSERT(loo.sell_price.quote.asset_id == quote_id, \"Order quote asset inconsistent with order\");\n         FC_ASSERT(loo.seller == account->get_id(), \"Order not owned by specified account\");\n\n         lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, loo.sell_price, *ostart_id));\n      }\n   }\n   else\n   {\n      // if reach here start_price must be valid\n      lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price));\n   }\n\n   upper_itr = index_by_account.upper_bound(std::make_tuple(account->id, price::min(base_id, quote_id)));\n\n   // Add the account's orders\n   for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)\n   {\n      const limit_order_object &order = *lower_itr;\n      results.emplace_back(order);\n   }\n\n   return results;\n}\n\nvector<call_order_object> database_api::get_call_orders(const std::string& a, uint32_t limit)const\n{\n   return my->get_call_orders( a, limit );\n}\n\nvector<call_order_object> database_api_impl::get_call_orders(const std::string& a, uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_call_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const asset_object* mia = get_asset_from_string(a);\n   const auto& call_index = _db.get_index_type<call_order_index>().indices().get<by_collateral>();\n   price index_price = price::min( mia->bitasset_data(_db).options.short_backing_asset, mia->get_id() );\n\n   vector< call_order_object> result;\n   auto itr_min = call_index.lower_bound(index_price);\n   auto itr_max = call_index.upper_bound(index_price.max());\n   while( itr_min != itr_max && result.size() < limit )\n   {\n      result.emplace_back(*itr_min);\n      ++itr_min;\n   }\n   return result;\n}\n\nvector<call_order_object> database_api::get_call_orders_by_account(const std::string& account_name_or_id,\n                                                                   asset_id_type start, uint32_t limit)const\n{\n   return my->get_call_orders_by_account( account_name_or_id, start, limit );\n}\n\nvector<call_order_object> database_api_impl::get_call_orders_by_account(const std::string& account_name_or_id,\n                                                                        asset_id_type start, uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_call_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<call_order_object> result;\n   const account_id_type account = get_account_from_string(account_name_or_id)->id;\n   const auto& call_idx = _db.get_index_type<call_order_index>().indices().get<by_account>();\n   auto call_index_end = call_idx.end();\n   auto call_itr = call_idx.lower_bound(boost::make_tuple(account, start));\n   while(call_itr != call_index_end && call_itr->borrower == account && result.size() < limit)\n   {\n      result.push_back(*call_itr);\n      ++call_itr;\n   }\n   return result;\n}\n\nvector<force_settlement_object> database_api::get_settle_orders(const std::string& a, uint32_t limit)const\n{\n   return my->get_settle_orders( a, limit );\n}\n\nvector<force_settlement_object> database_api_impl::get_settle_orders(const std::string& a, uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_settle_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const asset_id_type asset_a_id = get_asset_from_string(a)->id;\n   const auto& settle_index = _db.get_index_type<force_settlement_index>().indices().get<by_expiration>();\n   const asset_object& mia = _db.get(asset_a_id);\n\n   vector<force_settlement_object> result;\n   auto itr_min = settle_index.lower_bound(mia.get_id());\n   auto itr_max = settle_index.upper_bound(mia.get_id());\n   while( itr_min != itr_max && result.size() < limit )\n   {\n      result.emplace_back(*itr_min);\n      ++itr_min;\n   }\n   return result;\n}\n\nvector<force_settlement_object> database_api::get_settle_orders_by_account(\n      const std::string& account_name_or_id,\n      force_settlement_id_type start,\n      uint32_t limit )const\n{\n   return my->get_settle_orders_by_account( account_name_or_id, start, limit);\n}\n\nvector<force_settlement_object> database_api_impl::get_settle_orders_by_account(\n      const std::string& account_name_or_id,\n      force_settlement_id_type start,\n      uint32_t limit )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_settle_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<force_settlement_object> result;\n   const account_id_type account = get_account_from_string(account_name_or_id)->id;\n   const auto& settle_idx = _db.get_index_type<force_settlement_index>().indices().get<by_account>();\n   auto settle_index_end = settle_idx.end();\n   auto settle_itr = settle_idx.lower_bound(boost::make_tuple(account, start));\n   while(settle_itr != settle_index_end && settle_itr->owner == account && result.size() < limit)\n   {\n      result.push_back(*settle_itr);\n      ++settle_itr;\n   }\n   return result;\n}\n\n\nvector<call_order_object> database_api::get_margin_positions( const std::string account_id_or_name )const\n{\n   return my->get_margin_positions( account_id_or_name );\n}\n\nvector<call_order_object> database_api_impl::get_margin_positions( const std::string account_id_or_name )const\n{\n   try\n   {\n      const auto& idx = _db.get_index_type<call_order_index>();\n      const auto& aidx = idx.indices().get<by_account>();\n      const account_id_type id = get_account_from_string(account_id_or_name)->id;\n      auto start = aidx.lower_bound( boost::make_tuple( id, asset_id_type(0) ) );\n      auto end = aidx.lower_bound( boost::make_tuple( id+1, asset_id_type(0) ) );\n      vector<call_order_object> result;\n      while( start != end )\n      {\n         result.push_back(*start);\n         ++start;\n      }\n      return result;\n   } FC_CAPTURE_AND_RETHROW( (account_id_or_name) )\n}\n\nvector<collateral_bid_object> database_api::get_collateral_bids( const std::string& asset,\n                                                                 uint32_t limit, uint32_t start )const\n{\n   return my->get_collateral_bids( asset, limit, start );\n}\n\nvector<collateral_bid_object> database_api_impl::get_collateral_bids( const std::string& asset,\n                                                                      uint32_t limit, uint32_t skip )const\n{ try {\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_collateral_bids;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const asset_id_type asset_id = get_asset_from_string(asset)->id;\n   const asset_object& swan = asset_id(_db);\n   FC_ASSERT( swan.is_market_issued() );\n   const asset_bitasset_data_object& bad = swan.bitasset_data(_db);\n   const asset_object& back = bad.options.short_backing_asset(_db);\n   const auto& idx = _db.get_index_type<collateral_bid_index>();\n   const auto& aidx = idx.indices().get<by_price>();\n   auto start = aidx.lower_bound( boost::make_tuple( asset_id,\n                                                     price::max(back.id, asset_id),\n                                                     collateral_bid_id_type() ) );\n   auto end = aidx.lower_bound( boost::make_tuple( asset_id,\n                                                   price::min(back.id, asset_id),\n                                                   collateral_bid_id_type(GRAPHENE_DB_MAX_INSTANCE_ID) ) );\n   vector<collateral_bid_object> result;\n   while( skip-- > 0 && start != end ) { ++start; }\n   while( start != end && limit-- > 0)\n   {\n      result.push_back(*start);\n      ++start;\n   }\n   return result;\n} FC_CAPTURE_AND_RETHROW( (asset)(limit)(skip) ) }\n\nvoid database_api::subscribe_to_market( std::function<void(const variant&)> callback,\n                                        const std::string& a, const std::string& b )\n{\n   my->subscribe_to_market( callback, a, b );\n}\n\nvoid database_api_impl::subscribe_to_market( std::function<void(const variant&)> callback,\n                                             const std::string& a, const std::string& b )\n{\n   auto asset_a_id = get_asset_from_string(a)->id;\n   auto asset_b_id = get_asset_from_string(b)->id;\n\n   if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id);\n   FC_ASSERT(asset_a_id != asset_b_id);\n   _market_subscriptions[ std::make_pair(asset_a_id,asset_b_id) ] = callback;\n}\n\nvoid database_api::unsubscribe_from_market(const std::string& a, const std::string& b)\n{\n   my->unsubscribe_from_market( a, b );\n}\n\nvoid database_api_impl::unsubscribe_from_market(const std::string& a, const std::string& b)\n{\n   auto asset_a_id = get_asset_from_string(a)->id;\n   auto asset_b_id = get_asset_from_string(b)->id;\n\n   if(a > b) std::swap(asset_a_id,asset_b_id);\n   FC_ASSERT(asset_a_id != asset_b_id);\n   _market_subscriptions.erase(std::make_pair(asset_a_id,asset_b_id));\n}\n\nmarket_ticker database_api::get_ticker( const string& base, const string& quote )const\n{\n    return my->get_ticker( base, quote );\n}\n\nmarket_ticker database_api_impl::get_ticker( const string& base, const string& quote, bool skip_order_book )const\n{\n   FC_ASSERT( _app_options && _app_options->has_market_history_plugin, \"Market history plugin is not enabled.\" );\n\n   const auto assets = lookup_asset_symbols( {base, quote} );\n\n   FC_ASSERT( assets[0], \"Invalid base asset symbol: ${s}\", (\"s\",base) );\n   FC_ASSERT( assets[1], \"Invalid quote asset symbol: ${s}\", (\"s\",quote) );\n\n   auto base_id = assets[0]->id;\n   auto quote_id = assets[1]->id;\n   if( base_id > quote_id ) std::swap( base_id, quote_id );\n   const auto& ticker_idx = _db.get_index_type<market_ticker_index>().indices().get<by_market>();\n   auto itr = ticker_idx.find( std::make_tuple( base_id, quote_id ) );\n   const fc::time_point_sec now = _db.head_block_time();\n   if( itr != ticker_idx.end() )\n   {\n      order_book orders;\n      if (!skip_order_book)\n      {\n         orders = get_order_book(assets[0]->symbol, assets[1]->symbol, 1);\n      }\n      return market_ticker(*itr, now, *assets[0], *assets[1], orders);\n   }\n   // if no ticker is found for this market we return an empty ticker\n   market_ticker empty_result(now, *assets[0], *assets[1]);\n   return empty_result;\n}\n\nmarket_volume database_api::get_24_volume( const string& base, const string& quote )const\n{\n    return my->get_24_volume( base, quote );\n}\n\nmarket_volume database_api_impl::get_24_volume( const string& base, const string& quote )const\n{\n   const auto& ticker = get_ticker( base, quote, true );\n\n   market_volume result;\n   result.time = ticker.time;\n   result.base = ticker.base;\n   result.quote = ticker.quote;\n   result.base_volume = ticker.base_volume;\n   result.quote_volume = ticker.quote_volume;\n\n   return result;\n}\n\norder_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const\n{\n   return my->get_order_book( base, quote, limit);\n}\n\norder_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_order_book;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   order_book result;\n   result.base = base;\n   result.quote = quote;\n\n   auto assets = lookup_asset_symbols( {base, quote} );\n   FC_ASSERT( assets[0], \"Invalid base asset symbol: ${s}\", (\"s\",base) );\n   FC_ASSERT( assets[1], \"Invalid quote asset symbol: ${s}\", (\"s\",quote) );\n\n   auto base_id = assets[0]->id;\n   auto quote_id = assets[1]->id;\n   auto orders = get_limit_orders( base_id, quote_id, limit );\n\n   for( const auto& o : orders )\n   {\n      if( o.sell_price.base.asset_id == base_id )\n      {\n         order ord;\n         ord.price = price_to_string( o.sell_price, *assets[0], *assets[1] );\n         ord.quote = assets[1]->amount_to_string( share_type( fc::uint128_t( o.for_sale.value )\n                                                              * o.sell_price.quote.amount.value\n                                                              / o.sell_price.base.amount.value ) );\n         ord.base = assets[0]->amount_to_string( o.for_sale );\n         result.bids.push_back( ord );\n      }\n      else\n      {\n         order ord;\n         ord.price = price_to_string( o.sell_price, *assets[0], *assets[1] );\n         ord.quote = assets[1]->amount_to_string( o.for_sale );\n         ord.base = assets[0]->amount_to_string( share_type( fc::uint128_t( o.for_sale.value )\n                                                             * o.sell_price.quote.amount.value\n                                                             / o.sell_price.base.amount.value ) );\n         result.asks.push_back( ord );\n      }\n   }\n\n   return result;\n}\n\nvector<market_ticker> database_api::get_top_markets(uint32_t limit)const\n{\n   return my->get_top_markets(limit);\n}\n\nvector<market_ticker> database_api_impl::get_top_markets(uint32_t limit)const\n{\n   FC_ASSERT( _app_options && _app_options->has_market_history_plugin, \"Market history plugin is not enabled.\" );\n\n   const auto configured_limit = _app_options->api_limit_get_top_markets;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& volume_idx = _db.get_index_type<market_ticker_index>().indices().get<by_volume>();\n   auto itr = volume_idx.rbegin();\n   vector<market_ticker> result;\n   result.reserve(limit);\n   const fc::time_point_sec now = _db.head_block_time();\n\n   while( itr != volume_idx.rend() && result.size() < limit)\n   {\n      const asset_object base = itr->base(_db);\n      const asset_object quote = itr->quote(_db);\n      order_book orders;\n      orders = get_order_book(base.symbol, quote.symbol, 1);\n\n      result.emplace_back(market_ticker(*itr, now, base, quote, orders));\n      ++itr;\n   }\n   return result;\n}\n\nvector<market_trade> database_api::get_trade_history( const string& base,\n                                                      const string& quote,\n                                                      fc::time_point_sec start,\n                                                      fc::time_point_sec stop,\n                                                      unsigned limit )const\n{\n   return my->get_trade_history( base, quote, start, stop, limit );\n}\n\nvector<market_trade> database_api_impl::get_trade_history( const string& base,\n                                                           const string& quote,\n                                                           fc::time_point_sec start,\n                                                           fc::time_point_sec stop,\n                                                           unsigned limit )const\n{\n   FC_ASSERT( _app_options && _app_options->has_market_history_plugin, \"Market history plugin is not enabled.\" );\n\n   const auto configured_limit = _app_options->api_limit_get_trade_history;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   auto assets = lookup_asset_symbols( {base, quote} );\n   FC_ASSERT( assets[0], \"Invalid base asset symbol: ${s}\", (\"s\",base) );\n   FC_ASSERT( assets[1], \"Invalid quote asset symbol: ${s}\", (\"s\",quote) );\n\n   auto base_id = assets[0]->id;\n   auto quote_id = assets[1]->id;\n\n   if( base_id > quote_id ) std::swap( base_id, quote_id );\n\n   if ( start.sec_since_epoch() == 0 )\n      start = fc::time_point_sec( fc::time_point::now() );\n\n   uint32_t count = 0;\n   const auto& history_idx = _db.get_index_type<market_history::history_index>().indices().get<by_market_time>();\n   auto itr = history_idx.lower_bound( std::make_tuple( base_id, quote_id, start ) );\n   vector<market_trade> result;\n\n   while( itr != history_idx.end() && count < limit\n          && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) )\n   {\n      {\n         market_trade trade;\n\n         if( assets[0]->id == itr->op.receives.asset_id )\n         {\n            trade.amount = assets[1]->amount_to_string( itr->op.pays );\n            trade.value = assets[0]->amount_to_string( itr->op.receives );\n         }\n         else\n         {\n            trade.amount = assets[1]->amount_to_string( itr->op.receives );\n            trade.value = assets[0]->amount_to_string( itr->op.pays );\n         }\n\n         trade.date = itr->time;\n         trade.price = price_to_string( itr->op.fill_price, *assets[0], *assets[1] );\n\n         if( itr->op.is_maker )\n         {\n            trade.sequence = -itr->key.sequence;\n            trade.side1_account_id = itr->op.account_id;\n            if(itr->op.receives.asset_id == assets[0]->id)\n               trade.type = \"sell\";\n            else\n               trade.type = \"buy\";\n         }\n         else\n            trade.side2_account_id = itr->op.account_id;\n\n         auto next_itr = std::next(itr);\n         // Trades are usually tracked in each direction, exception: for global settlement only one side is recorded\n         if( next_itr != history_idx.end() && next_itr->key.base == base_id && next_itr->key.quote == quote_id\n             && next_itr->time == itr->time && next_itr->op.is_maker != itr->op.is_maker )\n         {  // next_itr now could be the other direction // FIXME not 100% sure\n            if( next_itr->op.is_maker )\n            {\n               trade.sequence = -next_itr->key.sequence;\n               trade.side1_account_id = next_itr->op.account_id;\n               if(next_itr->op.receives.asset_id == assets[0]->id)\n                  trade.type = \"sell\";\n               else\n                  trade.type = \"buy\";\n            }\n            else\n               trade.side2_account_id = next_itr->op.account_id;\n            // skip the other direction\n            itr = next_itr;\n         }\n\n         result.push_back( trade );\n         ++count;\n      }\n\n      ++itr;\n   }\n\n   return result;\n}\n\nvector<market_trade> database_api::get_trade_history_by_sequence(\n                                                      const string& base,\n                                                      const string& quote,\n                                                      int64_t start,\n                                                      fc::time_point_sec stop,\n                                                      unsigned limit )const\n{\n   return my->get_trade_history_by_sequence( base, quote, start, stop, limit );\n}\n\nvector<market_trade> database_api_impl::get_trade_history_by_sequence(\n                                                           const string& base,\n                                                           const string& quote,\n                                                           int64_t start,\n                                                           fc::time_point_sec stop,\n                                                           unsigned limit )const\n{\n   FC_ASSERT( _app_options && _app_options->has_market_history_plugin, \"Market history plugin is not enabled.\" );\n\n   const auto configured_limit = _app_options->api_limit_get_trade_history_by_sequence;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   FC_ASSERT( start >= 0 );\n   int64_t start_seq = -start;\n\n   auto assets = lookup_asset_symbols( {base, quote} );\n   FC_ASSERT( assets[0], \"Invalid base asset symbol: ${s}\", (\"s\",base) );\n   FC_ASSERT( assets[1], \"Invalid quote asset symbol: ${s}\", (\"s\",quote) );\n\n   auto base_id = assets[0]->id;\n   auto quote_id = assets[1]->id;\n\n   if( base_id > quote_id ) std::swap( base_id, quote_id );\n   const auto& history_idx = _db.get_index_type<graphene::market_history::history_index>().indices().get<by_key>();\n   history_key hkey;\n   hkey.base = base_id;\n   hkey.quote = quote_id;\n   hkey.sequence = start_seq;\n\n   uint32_t count = 0;\n   auto itr = history_idx.lower_bound( hkey );\n   vector<market_trade> result;\n\n   while( itr != history_idx.end() && count < limit\n          && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) )\n   {\n      if( itr->key.sequence == start_seq ) // found the key, should skip this and the other direction if found\n      {\n         auto next_itr = std::next(itr);\n         if( next_itr != history_idx.end() && next_itr->key.base == base_id && next_itr->key.quote == quote_id\n             && next_itr->time == itr->time && next_itr->op.is_maker != itr->op.is_maker )\n         {  // next_itr now could be the other direction // FIXME not 100% sure\n            // skip the other direction\n            itr = next_itr;\n         }\n      }\n      else\n      {\n         market_trade trade;\n\n         if( assets[0]->id == itr->op.receives.asset_id )\n         {\n            trade.amount = assets[1]->amount_to_string( itr->op.pays );\n            trade.value = assets[0]->amount_to_string( itr->op.receives );\n         }\n         else\n         {\n            trade.amount = assets[1]->amount_to_string( itr->op.receives );\n            trade.value = assets[0]->amount_to_string( itr->op.pays );\n         }\n\n         trade.date = itr->time;\n         trade.price = price_to_string( itr->op.fill_price, *assets[0], *assets[1] );\n\n         if( itr->op.is_maker )\n         {\n            trade.sequence = -itr->key.sequence;\n            trade.side1_account_id = itr->op.account_id;\n            if(itr->op.receives.asset_id == assets[0]->id)\n               trade.type = \"sell\";\n            else\n               trade.type = \"buy\";\n         }\n         else\n            trade.side2_account_id = itr->op.account_id;\n\n         auto next_itr = std::next(itr);\n         // Trades are usually tracked in each direction, exception: for global settlement only one side is recorded\n         if( next_itr != history_idx.end() && next_itr->key.base == base_id && next_itr->key.quote == quote_id\n             && next_itr->time == itr->time && next_itr->op.is_maker != itr->op.is_maker )\n         {  // next_itr now could be the other direction // FIXME not 100% sure\n            if( next_itr->op.is_maker )\n            {\n               trade.sequence = -next_itr->key.sequence;\n               trade.side1_account_id = next_itr->op.account_id;\n               if(next_itr->op.receives.asset_id == assets[0]->id)\n                  trade.type = \"sell\";\n               else\n                  trade.type = \"buy\";\n            }\n            else\n               trade.side2_account_id = next_itr->op.account_id;\n            // skip the other direction\n            itr = next_itr;\n         }\n\n         result.push_back( trade );\n         ++count;\n      }\n\n      ++itr;\n   }\n\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Liquidity pools                                                  //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<liquidity_pool_object> database_api::list_liquidity_pools(\n            optional<uint32_t> limit,\n            optional<liquidity_pool_id_type> start_id )const\n{\n   return my->list_liquidity_pools(\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api_impl::list_liquidity_pools(\n            optional<uint32_t> olimit,\n            optional<liquidity_pool_id_type> ostart_id )const\n{\n   uint32_t limit = olimit.valid() ? *olimit : 101;\n\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_liquidity_pools;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<liquidity_pool_object> results;\n\n   liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();\n\n   const auto& idx = _db.get_index_type<liquidity_pool_index>().indices().get<by_id>();\n   auto lower_itr = idx.lower_bound( start_id );\n   auto upper_itr = idx.end();\n\n   results.reserve( limit );\n   uint32_t count = 0;\n   for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)\n   {\n      results.emplace_back( *lower_itr );\n   }\n\n   return results;\n}\n\nvector<liquidity_pool_object> database_api::get_liquidity_pools_by_asset_a(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit,\n            optional<liquidity_pool_id_type> start_id )const\n{\n   return my->get_liquidity_pools_by_asset_a(\n            asset_symbol_or_id,\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_asset_a(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit,\n            optional<liquidity_pool_id_type> start_id )const\n{\n   return get_liquidity_pools_by_asset_x<by_asset_a>(\n            asset_symbol_or_id,\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api::get_liquidity_pools_by_asset_b(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit,\n            optional<liquidity_pool_id_type> start_id )const\n{\n   return my->get_liquidity_pools_by_asset_b(\n            asset_symbol_or_id,\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_asset_b(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit,\n            optional<liquidity_pool_id_type> start_id )const\n{\n   return get_liquidity_pools_by_asset_x<by_asset_b>(\n            asset_symbol_or_id,\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api::get_liquidity_pools_by_both_assets(\n            std::string asset_symbol_or_id_a,\n            std::string asset_symbol_or_id_b,\n            optional<uint32_t> limit,\n            optional<liquidity_pool_id_type> start_id )const\n{\n   return my->get_liquidity_pools_by_both_assets(\n            asset_symbol_or_id_a,\n            asset_symbol_or_id_b,\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_both_assets(\n            std::string asset_symbol_or_id_a,\n            std::string asset_symbol_or_id_b,\n            optional<uint32_t> olimit,\n            optional<liquidity_pool_id_type> ostart_id )const\n{\n   uint32_t limit = olimit.valid() ? *olimit : 101;\n\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_liquidity_pools;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<liquidity_pool_object> results;\n\n   asset_id_type asset_id_a = get_asset_from_string(asset_symbol_or_id_a)->id;\n   asset_id_type asset_id_b = get_asset_from_string(asset_symbol_or_id_b)->id;\n   if( asset_id_a > asset_id_b )\n      std::swap( asset_id_a, asset_id_b );\n\n   liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();\n\n   const auto& idx = _db.get_index_type<liquidity_pool_index>().indices().get<by_asset_ab>();\n   auto lower_itr = idx.lower_bound( std::make_tuple( asset_id_a, asset_id_b, start_id ) );\n   auto upper_itr = idx.upper_bound( std::make_tuple( asset_id_a, asset_id_b ) );\n\n   results.reserve( limit );\n   uint32_t count = 0;\n   for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)\n   {\n      results.emplace_back( *lower_itr );\n   }\n\n   return results;\n}\n\nvector<optional<liquidity_pool_object>> database_api::get_liquidity_pools_by_share_asset(\n            const vector<std::string>& asset_symbols_or_ids,\n            optional<bool> subscribe )const\n{\n   return my->get_liquidity_pools_by_share_asset(\n            asset_symbols_or_ids,\n            subscribe );\n}\n\nvector<optional<liquidity_pool_object>> database_api_impl::get_liquidity_pools_by_share_asset(\n            const vector<std::string>& asset_symbols_or_ids,\n            optional<bool> subscribe )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_liquidity_pools;\n   FC_ASSERT( asset_symbols_or_ids.size() <= configured_limit,\n              \"size of the querying list can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   bool to_subscribe = get_whether_to_subscribe( subscribe );\n   vector<optional<liquidity_pool_object>> result; result.reserve(asset_symbols_or_ids.size());\n   std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result),\n                  [this,to_subscribe](std::string id_or_name) -> optional<liquidity_pool_object> {\n\n      const asset_object* asset_obj = get_asset_from_string( id_or_name, false );\n      if( asset_obj == nullptr || !asset_obj->is_liquidity_pool_share_asset() )\n         return {};\n      const liquidity_pool_object& lp_obj = (*asset_obj->for_liquidity_pool)(_db);\n      if( to_subscribe )\n         subscribe_to_item( lp_obj.id );\n      return lp_obj;\n   });\n   return result;\n}\n\nvector<liquidity_pool_object> database_api::get_liquidity_pools_by_owner(\n            std::string account_name_or_id,\n            optional<uint32_t> limit,\n            optional<asset_id_type> start_id )const\n{\n   return my->get_liquidity_pools_by_owner(\n            account_name_or_id,\n            limit,\n            start_id );\n}\n\nvector<liquidity_pool_object> database_api_impl::get_liquidity_pools_by_owner(\n            std::string account_name_or_id,\n            optional<uint32_t> olimit,\n            optional<asset_id_type> ostart_id )const\n{\n   uint32_t limit = olimit.valid() ? *olimit : 101;\n\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_liquidity_pools;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<liquidity_pool_object> results;\n\n   account_id_type owner = get_account_from_string(account_name_or_id)->id;\n\n   asset_id_type start_id = ostart_id.valid() ? *ostart_id : asset_id_type();\n\n   // get assets owned by account\n   const auto& idx = _db.get_index_type<asset_index>().indices().get<by_issuer>();\n   auto lower_itr = idx.lower_bound( std::make_tuple( owner, start_id ) );\n   auto upper_itr = idx.upper_bound( owner );\n\n   results.reserve( limit );\n   uint32_t count = 0;\n   for ( ; lower_itr != upper_itr && count < limit; ++lower_itr )\n   {\n      const asset_object& asset_obj = *lower_itr;\n      if( !asset_obj.is_liquidity_pool_share_asset() ) // TODO improve performance\n         continue;\n      results.emplace_back( (*asset_obj.for_liquidity_pool)(_db) );\n      ++count;\n   }\n\n   return results;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Witnesses                                                        //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const\n{\n   return my->get_witnesses( witness_ids );\n}\n\nvector<optional<witness_object>> database_api_impl::get_witnesses(const vector<witness_id_type>& witness_ids)const\n{\n   vector<optional<witness_object>> result; result.reserve(witness_ids.size());\n   std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result),\n                  [this](witness_id_type id) -> optional<witness_object> {\n      if(auto o = _db.find(id))\n         return *o;\n      return {};\n   });\n   return result;\n}\n\nfc::optional<witness_object> database_api::get_witness_by_account(const std::string account_id_or_name)const\n{\n   return my->get_witness_by_account( account_id_or_name );\n}\n\nfc::optional<witness_object> database_api_impl::get_witness_by_account(const std::string account_id_or_name) const\n{\n   const auto& idx = _db.get_index_type<witness_index>().indices().get<by_account>();\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto itr = idx.find(account);\n   if( itr != idx.end() )\n      return *itr;\n   return {};\n}\n\nmap<string, witness_id_type> database_api::lookup_witness_accounts( const string& lower_bound_name,\n                                                                    uint32_t limit )const\n{\n   return my->lookup_witness_accounts( lower_bound_name, limit );\n}\n\nmap<string, witness_id_type> database_api_impl::lookup_witness_accounts( const string& lower_bound_name,\n                                                                         uint32_t limit )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_lookup_witness_accounts;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& witnesses_by_id = _db.get_index_type<witness_index>().indices().get<by_id>();\n\n   // we want to order witnesses by account name, but that name is in the account object\n   // so the witness_index doesn't have a quick way to access it.\n   // get all the names and look them all up, sort them, then figure out what\n   // records to return.  This could be optimized, but we expect the\n   // number of witnesses to be few and the frequency of calls to be rare\n   std::map<std::string, witness_id_type> witnesses_by_account_name;\n   for (const witness_object& witness : witnesses_by_id)\n       if (auto account_iter = _db.find(witness.witness_account))\n           if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name\n               witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id));\n\n   auto end_iter = witnesses_by_account_name.begin();\n   while (end_iter != witnesses_by_account_name.end() && limit--)\n       ++end_iter;\n   witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end());\n   return witnesses_by_account_name;\n}\n\nuint64_t database_api::get_witness_count()const\n{\n   return my->get_witness_count();\n}\n\nuint64_t database_api_impl::get_witness_count()const\n{\n   return _db.get_index_type<witness_index>().indices().size();\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Committee members                                                //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<optional<committee_member_object>> database_api::get_committee_members(\n                                             const vector<committee_member_id_type>& committee_member_ids )const\n{\n   return my->get_committee_members( committee_member_ids );\n}\n\nvector<optional<committee_member_object>> database_api_impl::get_committee_members(\n                                             const vector<committee_member_id_type>& committee_member_ids )const\n{\n   vector<optional<committee_member_object>> result; result.reserve(committee_member_ids.size());\n   std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result),\n                  [this](committee_member_id_type id) -> optional<committee_member_object> {\n      if(auto o = _db.find(id))\n         return *o;\n      return {};\n   });\n   return result;\n}\n\nfc::optional<committee_member_object> database_api::get_committee_member_by_account(\n                                         const std::string account_id_or_name )const\n{\n   return my->get_committee_member_by_account( account_id_or_name );\n}\n\nfc::optional<committee_member_object> database_api_impl::get_committee_member_by_account(\n                                         const std::string account_id_or_name )const\n{\n   const auto& idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto itr = idx.find(account);\n   if( itr != idx.end() )\n      return *itr;\n   return {};\n}\n\nmap<string, committee_member_id_type> database_api::lookup_committee_member_accounts(\n                                         const string& lower_bound_name, uint32_t limit )const\n{\n   return my->lookup_committee_member_accounts( lower_bound_name, limit );\n}\n\nmap<string, committee_member_id_type> database_api_impl::lookup_committee_member_accounts(\n                                         const string& lower_bound_name, uint32_t limit )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_lookup_committee_member_accounts;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& committee_members_by_id = _db.get_index_type<committee_member_index>().indices().get<by_id>();\n\n   // we want to order committee_members by account name, but that name is in the account object\n   // so the committee_member_index doesn't have a quick way to access it.\n   // get all the names and look them all up, sort them, then figure out what\n   // records to return.  This could be optimized, but we expect the\n   // number of committee_members to be few and the frequency of calls to be rare\n   std::map<std::string, committee_member_id_type> committee_members_by_account_name;\n   for (const committee_member_object& committee_member : committee_members_by_id)\n       if (auto account_iter = _db.find(committee_member.committee_member_account))\n           if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name\n               committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id));\n\n   auto end_iter = committee_members_by_account_name.begin();\n   while (end_iter != committee_members_by_account_name.end() && limit--)\n       ++end_iter;\n   committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end());\n   return committee_members_by_account_name;\n}\n\nuint64_t database_api::get_committee_count()const\n{\n    return my->get_committee_count();\n}\n\nuint64_t database_api_impl::get_committee_count()const\n{\n    return _db.get_index_type<committee_member_index>().indices().size();\n}\n\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Workers                                                          //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<worker_object> database_api::get_all_workers( const optional<bool> is_expired )const\n{\n   return my->get_all_workers( is_expired );\n}\n\nvector<worker_object> database_api_impl::get_all_workers( const optional<bool> is_expired )const\n{\n   vector<worker_object> result;\n\n   if( !is_expired.valid() ) // query for all workers\n   {\n      const auto& workers_idx = _db.get_index_type<worker_index>().indices().get<by_id>();\n      result.reserve( workers_idx.size() );\n      for( const auto& w : workers_idx )\n      {\n         result.push_back( w );\n      }\n   }\n   else // query for workers that are expired only or not expired only\n   {\n      const time_point_sec now = _db.head_block_time();\n      const auto& workers_idx = _db.get_index_type<worker_index>().indices().get<by_end_date>();\n      auto itr = *is_expired ? workers_idx.begin() : workers_idx.lower_bound( now );\n      auto end = *is_expired ? workers_idx.upper_bound( now ) : workers_idx.end();\n      for( ; itr != end; ++itr )\n      {\n         result.push_back( *itr );\n      }\n   }\n\n   return result;\n}\n\nvector<worker_object> database_api::get_workers_by_account(const std::string account_id_or_name)const\n{\n   return my->get_workers_by_account( account_id_or_name );\n}\n\nvector<worker_object> database_api_impl::get_workers_by_account(const std::string account_id_or_name)const\n{\n   vector<worker_object> result;\n   const auto& workers_idx = _db.get_index_type<worker_index>().indices().get<by_account>();\n\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto range = workers_idx.equal_range(account);\n   for(auto itr = range.first; itr != range.second; ++itr)\n   {\n      result.push_back( *itr );\n   }\n   return result;\n}\n\nuint64_t database_api::get_worker_count()const\n{\n    return my->get_worker_count();\n}\n\nuint64_t database_api_impl::get_worker_count()const\n{\n    return _db.get_index_type<worker_index>().indices().size();\n}\n\n\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Votes                                                            //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<variant> database_api::lookup_vote_ids( const vector<vote_id_type>& votes )const\n{\n   return my->lookup_vote_ids( votes );\n}\n\nvector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>& votes )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_lookup_vote_ids;\n   FC_ASSERT( votes.size() <= configured_limit,\n              \"Number of querying votes can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& witness_idx = _db.get_index_type<witness_index>().indices().get<by_vote_id>();\n   const auto& committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();\n   const auto& for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();\n   const auto& against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();\n\n   vector<variant> result;\n   result.reserve( votes.size() );\n   for( auto id : votes )\n   {\n      switch( id.type() )\n      {\n         case vote_id_type::committee:\n         {\n            auto itr = committee_idx.find( id );\n            if( itr != committee_idx.end() )\n               result.emplace_back( variant( *itr, 2 ) ); // Depth of committee_member_object is 1, add 1 to be safe\n            else\n               result.emplace_back( variant() );\n            break;\n         }\n         case vote_id_type::witness:\n         {\n            auto itr = witness_idx.find( id );\n            if( itr != witness_idx.end() )\n               result.emplace_back( variant( *itr, 2 ) ); // Depth of witness_object is 1, add 1 here to be safe\n            else\n               result.emplace_back( variant() );\n            break;\n         }\n         case vote_id_type::worker:\n         {\n            auto itr = for_worker_idx.find( id );\n            if( itr != for_worker_idx.end() ) {\n               result.emplace_back( variant( *itr, 4 ) ); // Depth of worker_object is 3, add 1 here to be safe.\n                                                          // If we want to extract the balance object inside,\n                                                          //   need to increase this value\n            }\n            else {\n               auto itr = against_worker_idx.find( id );\n               if( itr != against_worker_idx.end() ) {\n                  result.emplace_back( variant( *itr, 4 ) ); // Depth of worker_object is 3, add 1 here to be safe.\n                                                             // If we want to extract the balance object inside,\n                                                             //   need to increase this value\n               }\n               else {\n                  result.emplace_back( variant() );\n               }\n            }\n            break;\n         }\n         case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings\n         default:\n            FC_CAPTURE_AND_THROW( fc::out_of_range_exception, (id) );\n      }\n   }\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Authority / validation                                           //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nstd::string database_api::get_transaction_hex(const signed_transaction& trx)const\n{\n   return my->get_transaction_hex( trx );\n}\n\nstd::string database_api_impl::get_transaction_hex(const signed_transaction& trx)const\n{\n   return fc::to_hex(fc::raw::pack(trx));\n}\n\nstd::string database_api::get_transaction_hex_without_sig(\n   const signed_transaction &trx) const\n{\n   return my->get_transaction_hex_without_sig(trx);\n}\n\nstd::string database_api_impl::get_transaction_hex_without_sig(\n   const signed_transaction &trx) const\n{\n   return fc::to_hex(fc::raw::pack(static_cast<transaction>(trx)));\n}\n\nset<public_key_type> database_api::get_required_signatures( const signed_transaction& trx,\n                                                            const flat_set<public_key_type>& available_keys )const\n{\n   return my->get_required_signatures( trx, available_keys );\n}\n\nset<public_key_type> database_api_impl::get_required_signatures( const signed_transaction& trx,\n                                                            const flat_set<public_key_type>& available_keys )const\n{\n   auto chain_time = _db.head_block_time();\n   bool allow_non_immediate_owner = ( chain_time >= HARDFORK_CORE_584_TIME );\n   bool ignore_custom_op_reqd_auths = MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( chain_time );\n   auto result = trx.get_required_signatures( _db.get_chain_id(),\n                                       available_keys,\n                                       [&]( account_id_type id ){ return &id(_db).active; },\n                                       [&]( account_id_type id ){ return &id(_db).owner; },\n                                       allow_non_immediate_owner,\n                                       ignore_custom_op_reqd_auths,\n                                       _db.get_global_properties().parameters.max_authority_depth );\n   return result;\n}\n\nset<public_key_type> database_api::get_potential_signatures( const signed_transaction& trx )const\n{\n   return my->get_potential_signatures( trx );\n}\nset<address> database_api::get_potential_address_signatures( const signed_transaction& trx )const\n{\n   return my->get_potential_address_signatures( trx );\n}\n\nset<public_key_type> database_api_impl::get_potential_signatures( const signed_transaction& trx )const\n{\n   auto chain_time = _db.head_block_time();\n   bool allow_non_immediate_owner = ( chain_time >= HARDFORK_CORE_584_TIME );\n   bool ignore_custom_op_reqd_auths = MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( chain_time );\n\n   set<public_key_type> result;\n   auto get_active = [this, &result]( account_id_type id ){\n      const auto& auth = id( _db ).active;\n      for( const auto& k : auth.get_keys() )\n         result.insert( k );\n      return &auth;\n   };\n   auto get_owner = [this, &result]( account_id_type id ){\n      const auto& auth = id( _db ).owner;\n      for( const auto& k : auth.get_keys() )\n         result.insert( k );\n      return &auth;\n   };\n\n   trx.get_required_signatures( _db.get_chain_id(),\n                                flat_set<public_key_type>(),\n                                get_active, get_owner,\n                                allow_non_immediate_owner,\n                                ignore_custom_op_reqd_auths,\n                                _db.get_global_properties().parameters.max_authority_depth );\n\n   // Insert keys in required \"other\" authories\n   flat_set<account_id_type> required_active;\n   flat_set<account_id_type> required_owner;\n   vector<authority> other;\n   trx.get_required_authorities( required_active, required_owner, other, ignore_custom_op_reqd_auths );\n   for( const auto& auth : other )\n      for( const auto& key : auth.get_keys() )\n         result.insert( key );\n\n   return result;\n}\n\nset<address> database_api_impl::get_potential_address_signatures( const signed_transaction& trx )const\n{\n   auto chain_time = _db.head_block_time();\n   bool allow_non_immediate_owner = ( chain_time >= HARDFORK_CORE_584_TIME );\n   bool ignore_custom_op_reqd_auths = MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( chain_time );\n\n   set<address> result;\n   auto get_active = [this, &result]( account_id_type id ){\n      const auto& auth = id( _db ).active;\n      for( const auto& k : auth.get_addresses() )\n         result.insert( k );\n      return &auth;\n   };\n   auto get_owner = [this, &result]( account_id_type id ) {\n      const auto& auth = id( _db ).owner;\n      for (const auto& k : auth.get_addresses())\n         result.insert( k );\n      return &auth;\n   };\n\n   trx.get_required_signatures( _db.get_chain_id(),\n                                flat_set<public_key_type>(),\n                                get_active, get_owner,\n                                allow_non_immediate_owner,\n                                ignore_custom_op_reqd_auths,\n                                _db.get_global_properties().parameters.max_authority_depth );\n   return result;\n}\n\nbool database_api::verify_authority( const signed_transaction& trx )const\n{\n   return my->verify_authority( trx );\n}\n\nbool database_api_impl::verify_authority( const signed_transaction& trx )const\n{\n   bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_CORE_584_TIME );\n   trx.verify_authority( _db.get_chain_id(),\n                         [this]( account_id_type id ){ return &id(_db).active; },\n                         [this]( account_id_type id ){ return &id(_db).owner; },\n                         [this]( account_id_type id, const operation& op, rejected_predicate_map* rejects ) {\n                           return _db.get_viable_custom_authorities(id, op, rejects); },\n                         allow_non_immediate_owner,\n                         _db.get_global_properties().parameters.max_authority_depth );\n   return true;\n}\n\nbool database_api::verify_account_authority( const string& account_name_or_id,\n                                             const flat_set<public_key_type>& signers )const\n{\n   return my->verify_account_authority( account_name_or_id, signers );\n}\n\nbool database_api_impl::verify_account_authority( const string& account_name_or_id,\n      const flat_set<public_key_type>& keys )const\n{\n   // create a dummy transfer\n   transfer_operation op;\n   op.from = get_account_from_string(account_name_or_id)->id;\n   std::vector<operation> ops;\n   ops.emplace_back(op);\n\n   try\n   {\n      graphene::chain::verify_authority(ops, keys,\n            [this]( account_id_type id ){ return &id(_db).active; },\n            [this]( account_id_type id ){ return &id(_db).owner; },\n            // Use a no-op lookup for custom authorities; we don't want it even if one does apply for our dummy op\n            [](auto, auto, auto*) { return vector<authority>(); },\n            true, MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(_db.head_block_time()) );\n   }\n   catch (fc::exception& ex)\n   {\n      return false;\n   }\n\n   return true;\n}\n\nprocessed_transaction database_api::validate_transaction( const signed_transaction& trx )const\n{\n   return my->validate_transaction( trx );\n}\n\nprocessed_transaction database_api_impl::validate_transaction( const signed_transaction& trx )const\n{\n   return _db.validate_transaction(trx);\n}\n\nvector< fc::variant > database_api::get_required_fees( const vector<operation>& ops,\n                                                       const std::string& asset_id_or_symbol )const\n{\n   return my->get_required_fees( ops, asset_id_or_symbol );\n}\n\n/**\n * Container method for mutually recursive functions used to\n * implement get_required_fees() with potentially nested proposals.\n */\nstruct get_required_fees_helper\n{\n   get_required_fees_helper(\n      const fee_schedule& _current_fee_schedule,\n      const price& _core_exchange_rate,\n      uint32_t _max_recursion\n      )\n      : current_fee_schedule(_current_fee_schedule),\n        core_exchange_rate(_core_exchange_rate),\n        max_recursion(_max_recursion)\n   {}\n\n   fc::variant set_op_fees( operation& op )\n   {\n      if( op.is_type<proposal_create_operation>() )\n      {\n         return set_proposal_create_op_fees( op );\n      }\n      else\n      {\n         asset fee = current_fee_schedule.set_fee( op, core_exchange_rate );\n         fc::variant result;\n         fc::to_variant( fee, result, GRAPHENE_NET_MAX_NESTED_OBJECTS );\n         return result;\n      }\n   }\n\n   fc::variant set_proposal_create_op_fees( operation& proposal_create_op )\n   {\n      proposal_create_operation& op = proposal_create_op.get<proposal_create_operation>();\n      std::pair< asset, fc::variants > result;\n      for( op_wrapper& prop_op : op.proposed_ops )\n      {\n         FC_ASSERT( current_recursion < max_recursion );\n         ++current_recursion;\n         result.second.push_back( set_op_fees( prop_op.op ) );\n         --current_recursion;\n      }\n      // we need to do this on the boxed version, which is why we use\n      // two mutually recursive functions instead of a visitor\n      result.first = current_fee_schedule.set_fee( proposal_create_op, core_exchange_rate );\n      fc::variant vresult;\n      fc::to_variant( result, vresult, GRAPHENE_NET_MAX_NESTED_OBJECTS );\n      return vresult;\n   }\n\n   const fee_schedule& current_fee_schedule;\n   const price& core_exchange_rate;\n   uint32_t max_recursion;\n   uint32_t current_recursion = 0;\n};\n\nvector< fc::variant > database_api_impl::get_required_fees( const vector<operation>& ops,\n                                                            const std::string& asset_id_or_symbol )const\n{\n   vector< operation > _ops = ops;\n   //\n   // we copy the ops because we need to mutate an operation to reliably\n   // determine its fee, see #435\n   //\n\n   vector< fc::variant > result;\n   result.reserve(ops.size());\n   const asset_object& a = *get_asset_from_string(asset_id_or_symbol);\n   get_required_fees_helper helper(\n      _db.current_fee_schedule(),\n      a.options.core_exchange_rate,\n      GET_REQUIRED_FEES_MAX_RECURSION );\n   for( operation& op : _ops )\n   {\n      result.push_back( helper.set_op_fees( op ) );\n   }\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Proposed transactions                                            //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<proposal_object> database_api::get_proposed_transactions( const std::string account_id_or_name )const\n{\n   return my->get_proposed_transactions( account_id_or_name );\n}\n\nvector<proposal_object> database_api_impl::get_proposed_transactions( const std::string account_id_or_name )const\n{\n   // api_helper_indexes plugin is required for accessing the secondary index\n   FC_ASSERT( _app_options && _app_options->has_api_helper_indexes_plugin,\n              \"api_helper_indexes plugin is not enabled on this server.\" );\n\n   const auto& proposal_idx = _db.get_index_type< primary_index< proposal_index > >();\n   const auto& proposals_by_account = proposal_idx.get_secondary_index<graphene::chain::required_approval_index>();\n\n   vector<proposal_object> result;\n   const account_id_type id = get_account_from_string(account_id_or_name)->id;\n\n   auto required_approvals_itr = proposals_by_account._account_to_proposals.find( id );\n   if( required_approvals_itr != proposals_by_account._account_to_proposals.end() )\n   {\n      result.reserve( required_approvals_itr->second.size() );\n      for( auto proposal_id : required_approvals_itr->second )\n      {\n         result.push_back( proposal_id(_db) );\n      }\n   }\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Blinded balances                                                 //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<blinded_balance_object> database_api::get_blinded_balances(\n                                  const flat_set<commitment_type>& commitments )const\n{\n   return my->get_blinded_balances( commitments );\n}\n\nvector<blinded_balance_object> database_api_impl::get_blinded_balances(\n                                  const flat_set<commitment_type>& commitments )const\n{\n   vector<blinded_balance_object> result; result.reserve(commitments.size());\n   const auto& bal_idx = _db.get_index_type<blinded_balance_index>();\n   const auto& by_commitment_idx = bal_idx.indices().get<by_commitment>();\n   for( const auto& c : commitments )\n   {\n      auto itr = by_commitment_idx.find( c );\n      if( itr != by_commitment_idx.end() )\n         result.push_back( *itr );\n   }\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n//  Withdrawals                                                     //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nvector<withdraw_permission_object> database_api::get_withdraw_permissions_by_giver(\n                                      const std::string account_id_or_name,\n                                      withdraw_permission_id_type start,\n                                      uint32_t limit)const\n{\n   return my->get_withdraw_permissions_by_giver( account_id_or_name, start, limit );\n}\n\nvector<withdraw_permission_object> database_api_impl::get_withdraw_permissions_by_giver(\n                                      const std::string account_id_or_name,\n                                      withdraw_permission_id_type start,\n                                      uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_withdraw_permissions_by_giver;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<withdraw_permission_object> result;\n\n   const auto& withdraw_idx = _db.get_index_type<withdraw_permission_index>().indices().get<by_from>();\n   auto withdraw_index_end = withdraw_idx.end();\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto withdraw_itr = withdraw_idx.lower_bound(boost::make_tuple(account, start));\n   while( withdraw_itr != withdraw_index_end && withdraw_itr->withdraw_from_account == account\n          && result.size() < limit )\n   {\n      result.push_back(*withdraw_itr);\n      ++withdraw_itr;\n   }\n   return result;\n}\n\nvector<withdraw_permission_object> database_api::get_withdraw_permissions_by_recipient(\n                                      const std::string account_id_or_name,\n                                      withdraw_permission_id_type start,\n                                      uint32_t limit)const\n{\n   return my->get_withdraw_permissions_by_recipient( account_id_or_name, start, limit );\n}\n\nvector<withdraw_permission_object> database_api_impl::get_withdraw_permissions_by_recipient(\n                                      const std::string account_id_or_name,\n                                      withdraw_permission_id_type start,\n                                      uint32_t limit)const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_withdraw_permissions_by_recipient;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<withdraw_permission_object> result;\n\n   const auto& withdraw_idx = _db.get_index_type<withdraw_permission_index>().indices().get<by_authorized>();\n   auto withdraw_index_end = withdraw_idx.end();\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto withdraw_itr = withdraw_idx.lower_bound(boost::make_tuple(account, start));\n   while(withdraw_itr != withdraw_index_end && withdraw_itr->authorized_account == account && result.size() < limit)\n   {\n      result.push_back(*withdraw_itr);\n      ++withdraw_itr;\n   }\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n//  HTLC                                                            //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\noptional<htlc_object> database_api::get_htlc( htlc_id_type id, optional<bool> subscribe )const\n{\n   return my->get_htlc( id, subscribe );\n}\n\nfc::optional<htlc_object> database_api_impl::get_htlc( htlc_id_type id, optional<bool> subscribe )const\n{\n   auto obj = get_objects( { id }, subscribe ).front();\n   if ( !obj.is_null() )\n   {\n      return fc::optional<htlc_object>(obj.template as<htlc_object>(GRAPHENE_MAX_NESTED_OBJECTS));\n   }\n   return fc::optional<htlc_object>();\n}\n\nvector<htlc_object> database_api::get_htlc_by_from( const std::string account_id_or_name,\n                                                    htlc_id_type start, uint32_t limit )const\n{\n   return my->get_htlc_by_from(account_id_or_name, start, limit);\n}\n\nvector<htlc_object> database_api_impl::get_htlc_by_from( const std::string account_id_or_name,\n                                                         htlc_id_type start, uint32_t limit ) const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_htlc_by;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<htlc_object> result;\n\n   const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_from_id >();\n   auto htlc_index_end = htlc_idx.end();\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto htlc_itr = htlc_idx.lower_bound(boost::make_tuple(account, start));\n\n   while(htlc_itr != htlc_index_end && htlc_itr->transfer.from == account && result.size() < limit)\n   {\n      result.push_back(*htlc_itr);\n      ++htlc_itr;\n   }\n   return result;\n}\n\nvector<htlc_object> database_api::get_htlc_by_to( const std::string account_id_or_name,\n                                                  htlc_id_type start, uint32_t limit )const\n{\n   return my->get_htlc_by_to(account_id_or_name, start, limit);\n}\n\nvector<htlc_object> database_api_impl::get_htlc_by_to( const std::string account_id_or_name,\n                                                       htlc_id_type start, uint32_t limit ) const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_htlc_by;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<htlc_object> result;\n\n   const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_to_id >();\n   auto htlc_index_end = htlc_idx.end();\n   const account_id_type account = get_account_from_string(account_id_or_name)->id;\n   auto htlc_itr = htlc_idx.lower_bound(boost::make_tuple(account, start));\n\n   while(htlc_itr != htlc_index_end && htlc_itr->transfer.to == account && result.size() < limit)\n   {\n      result.push_back(*htlc_itr);\n      ++htlc_itr;\n   }\n   return result;\n}\n\nvector<htlc_object> database_api::list_htlcs(const htlc_id_type start, uint32_t limit)const\n{\n   return my->list_htlcs(start, limit);\n}\n\nvector<htlc_object> database_api_impl::list_htlcs(const htlc_id_type start, uint32_t limit) const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_list_htlcs;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   vector<htlc_object> result;\n   const auto& htlc_idx = _db.get_index_type<htlc_index>().indices().get<by_id>();\n   auto itr = htlc_idx.lower_bound(start);\n   while(itr != htlc_idx.end() && result.size() < limit)\n   {\n      result.push_back(*itr);\n      ++itr;\n   }\n   return result;\n}\n\n//////////////////////////////////////////////////////////////////////\n//                                                                  //\n// Private methods                                                  //\n//                                                                  //\n//////////////////////////////////////////////////////////////////////\n\nconst account_object* database_api_impl::get_account_from_string( const std::string& name_or_id,\n                                                                  bool throw_if_not_found ) const\n{\n   // TODO cache the result to avoid repeatly fetching from db\n   FC_ASSERT( name_or_id.size() > 0);\n   const account_object* account = nullptr;\n   if (std::isdigit(name_or_id[0]))\n      account = _db.find(fc::variant(name_or_id, 1).as<account_id_type>(1));\n   else\n   {\n      const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();\n      auto itr = idx.find(name_or_id);\n      if (itr != idx.end())\n         account = &*itr;\n   }\n   if(throw_if_not_found)\n      FC_ASSERT( account, \"no such account\" );\n   return account;\n}\n\nconst asset_object* database_api_impl::get_asset_from_string( const std::string& symbol_or_id,\n                                                              bool throw_if_not_found ) const\n{\n   // TODO cache the result to avoid repeatly fetching from db\n   FC_ASSERT( symbol_or_id.size() > 0);\n   const asset_object* asset = nullptr;\n   if (std::isdigit(symbol_or_id[0]))\n      asset = _db.find(fc::variant(symbol_or_id, 1).as<asset_id_type>(1));\n   else\n   {\n      const auto& idx = _db.get_index_type<asset_index>().indices().get<by_symbol>();\n      auto itr = idx.find(symbol_or_id);\n      if (itr != idx.end())\n         asset = &*itr;\n   }\n   if(throw_if_not_found)\n      FC_ASSERT( asset, \"no such asset\" );\n   return asset;\n}\n\n// helper function\nvector<optional<extended_asset_object>> database_api_impl::get_assets( const vector<asset_id_type>& asset_ids,\n                                                                       optional<bool> subscribe )const\n{\n   bool to_subscribe = get_whether_to_subscribe( subscribe );\n   vector<optional<extended_asset_object>> result; result.reserve(asset_ids.size());\n   std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result),\n           [this,to_subscribe](asset_id_type id) -> optional<extended_asset_object> {\n      if(auto o = _db.find(id))\n      {\n         if( to_subscribe )\n            subscribe_to_item( id );\n         return extend_asset( *o );\n      }\n      return {};\n   });\n   return result;\n}\n\n// helper function\nvector<limit_order_object> database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b,\n                                                                const uint32_t limit )const\n{\n   FC_ASSERT( _app_options, \"Internal error\" );\n   const auto configured_limit = _app_options->api_limit_get_limit_orders;\n   FC_ASSERT( limit <= configured_limit,\n              \"limit can not be greater than ${configured_limit}\",\n              (\"configured_limit\", configured_limit) );\n\n   const auto& limit_order_idx = _db.get_index_type<limit_order_index>();\n   const auto& limit_price_idx = limit_order_idx.indices().get<by_price>();\n\n   vector<limit_order_object> result;\n   result.reserve(limit*2);\n\n   uint32_t count = 0;\n   auto limit_itr = limit_price_idx.lower_bound(price::max(a,b));\n   auto limit_end = limit_price_idx.upper_bound(price::min(a,b));\n   while(limit_itr != limit_end && count < limit)\n   {\n      result.push_back(*limit_itr);\n      ++limit_itr;\n      ++count;\n   }\n   count = 0;\n   limit_itr = limit_price_idx.lower_bound(price::max(b,a));\n   limit_end = limit_price_idx.upper_bound(price::min(b,a));\n   while(limit_itr != limit_end && count < limit)\n   {\n      result.push_back(*limit_itr);\n      ++limit_itr;\n      ++count;\n   }\n\n   return result;\n}\n\nbool database_api_impl::is_impacted_account( const flat_set<account_id_type>& accounts)\n{\n   if( !_subscribed_accounts.size() || !accounts.size() )\n      return false;\n\n   return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) {\n      return _subscribed_accounts.find(account) != _subscribed_accounts.end();\n   });\n}\n\nvoid database_api_impl::broadcast_updates( const vector<variant>& updates )\n{\n   if( updates.size() && _subscribe_callback ) {\n      auto capture_this = shared_from_this();\n      fc::async([capture_this,updates](){\n          if(capture_this->_subscribe_callback)\n            capture_this->_subscribe_callback( fc::variant(updates) );\n      });\n   }\n}\n\nvoid database_api_impl::broadcast_market_updates( const market_queue_type& queue)\n{\n   if( queue.size() )\n   {\n      auto capture_this = shared_from_this();\n      fc::async([capture_this, this, queue](){\n          for( const auto& item : queue )\n          {\n            auto sub = _market_subscriptions.find(item.first);\n            if( sub != _market_subscriptions.end() )\n                sub->second( fc::variant(item.second ) );\n          }\n      });\n   }\n}\n\nvoid database_api_impl::on_objects_removed( const vector<object_id_type>& ids,\n                                            const vector<const object*>& objs,\n                                            const flat_set<account_id_type>& impacted_accounts )\n{\n   handle_object_changed(_notify_remove_create, false, ids, impacted_accounts,\n      [objs](object_id_type id) -> const object* {\n         auto it = std::find_if(\n               objs.begin(), objs.end(),\n               [id](const object* o) {return o != nullptr && o->id == id;});\n\n         if (it != objs.end())\n            return *it;\n\n         return nullptr;\n      }\n   );\n}\n\nvoid database_api_impl::on_objects_new( const vector<object_id_type>& ids,\n                                        const flat_set<account_id_type>& impacted_accounts )\n{\n   handle_object_changed(_notify_remove_create, true, ids, impacted_accounts,\n      std::bind(&object_database::find_object, &_db, std::placeholders::_1)\n   );\n}\n\nvoid database_api_impl::on_objects_changed( const vector<object_id_type>& ids,\n                                            const flat_set<account_id_type>& impacted_accounts )\n{\n   handle_object_changed(false, true, ids, impacted_accounts,\n      std::bind(&object_database::find_object, &_db, std::placeholders::_1)\n   );\n}\n\nvoid database_api_impl::handle_object_changed( bool force_notify,\n                                               bool full_object,\n                                               const vector<object_id_type>& ids,\n                                               const flat_set<account_id_type>& impacted_accounts,\n                                               std::function<const object*(object_id_type id)> find_object )\n{\n   if( _subscribe_callback )\n   {\n      vector<variant> updates;\n\n      for(auto id : ids)\n      {\n         if( force_notify || is_subscribed_to_item(id) || is_impacted_account(impacted_accounts) )\n         {\n            if( full_object )\n            {\n               auto obj = find_object(id);\n               if( obj )\n               {\n                  updates.emplace_back( obj->to_variant() );\n               }\n            }\n            else\n            {\n               updates.emplace_back( fc::variant( id, 1 ) );\n            }\n         }\n      }\n\n      if( updates.size() )\n         broadcast_updates(updates);\n   }\n\n   if( _market_subscriptions.size() )\n   {\n      market_queue_type broadcast_queue;\n\n      for(auto id : ids)\n      {\n         if( id.is<call_order_object>() )\n         {\n            enqueue_if_subscribed_to_market<call_order_object>( find_object(id), broadcast_queue, full_object );\n         }\n         else if( id.is<limit_order_object>() )\n         {\n            enqueue_if_subscribed_to_market<limit_order_object>( find_object(id), broadcast_queue, full_object );\n         }\n         else if( id.is<force_settlement_object>() )\n         {\n            enqueue_if_subscribed_to_market<force_settlement_object>( find_object(id), broadcast_queue,\n                                                                      full_object );\n         }\n      }\n\n      if( broadcast_queue.size() )\n         broadcast_market_updates(broadcast_queue);\n   }\n}\n\n/** note: this method cannot yield because it is called in the middle of\n * apply a block.\n */\nvoid database_api_impl::on_applied_block()\n{\n   if (_block_applied_callback)\n   {\n      auto capture_this = shared_from_this();\n      block_id_type block_id = _db.head_block_id();\n      fc::async([this,capture_this,block_id](){\n         _block_applied_callback(fc::variant(block_id, 1));\n      });\n   }\n\n   if(_market_subscriptions.size() == 0)\n      return;\n\n   const auto& ops = _db.get_applied_operations();\n   map< std::pair<asset_id_type,asset_id_type>, vector<pair<operation, operation_result>> > subscribed_markets_ops;\n   for(const optional< operation_history_object >& o_op : ops)\n   {\n      if( !o_op.valid() )\n         continue;\n      const operation_history_object& op = *o_op;\n\n      optional< std::pair<asset_id_type,asset_id_type> > market;\n      switch(op.op.which())\n      {\n         /*  This is sent via the object_changed callback\n         case operation::tag<limit_order_create_operation>::value:\n            market = op.op.get<limit_order_create_operation>().get_market();\n            break;\n         */\n         case operation::tag<fill_order_operation>::value:\n            market = op.op.get<fill_order_operation>().get_market();\n            break;\n            /*\n         case operation::tag<limit_order_cancel_operation>::value:\n         */\n         default: break;\n      }\n      if( market.valid() && _market_subscriptions.count(*market) )\n         // FIXME this may cause fill_order_operation be pushed before order creation\n         subscribed_markets_ops[*market].emplace_back(std::make_pair(op.op, op.result));\n   }\n   /// we need to ensure the database_api is not deleted for the life of the async operation\n   auto capture_this = shared_from_this();\n   fc::async([this,capture_this,subscribed_markets_ops](){\n      for(auto item : subscribed_markets_ops)\n      {\n         auto itr = _market_subscriptions.find(item.first);\n         if(itr != _market_subscriptions.end())\n            itr->second(fc::variant(item.second, GRAPHENE_NET_MAX_NESTED_OBJECTS));\n      }\n   });\n}\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/app/database_api_impl.hxx",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/app/database_api.hpp>\n\n#include <fc/bloom_filter.hpp>\n\n#define GET_REQUIRED_FEES_MAX_RECURSION 4\n\nnamespace graphene { namespace app {\n\ntypedef std::map< std::pair<graphene::chain::asset_id_type, graphene::chain::asset_id_type>,\n                  std::vector<fc::variant> > market_queue_type;\n\nclass database_api_impl : public std::enable_shared_from_this<database_api_impl>\n{\n   public:\n      explicit database_api_impl( graphene::chain::database& db, const application_options* app_options );\n      virtual ~database_api_impl();\n\n      // Objects\n      fc::variants get_objects( const vector<object_id_type>& ids, optional<bool> subscribe )const;\n\n      // Subscriptions\n      void set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create );\n      void set_auto_subscription( bool enable );\n      void set_pending_transaction_callback( std::function<void(const variant&)> cb );\n      void set_block_applied_callback( std::function<void(const variant& block_id)> cb );\n      void cancel_all_subscriptions(bool reset_callback, bool reset_market_subscriptions);\n\n      // Blocks and transactions\n      optional<block_header> get_block_header(uint32_t block_num)const;\n      map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;\n      optional<signed_block> get_block(uint32_t block_num)const;\n      processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;\n\n      // Globals\n      chain_property_object get_chain_properties()const;\n      global_property_object get_global_properties()const;\n      fc::variant_object get_config()const;\n      chain_id_type get_chain_id()const;\n      dynamic_global_property_object get_dynamic_global_properties()const;\n\n      // Keys\n      vector<flat_set<account_id_type>> get_key_references( vector<public_key_type> key )const;\n      bool is_public_key_registered(string public_key) const;\n\n      // Accounts\n      account_id_type get_account_id_from_string(const std::string& name_or_id)const;\n      vector<optional<account_object>> get_accounts( const vector<std::string>& account_names_or_ids,\n                                                     optional<bool> subscribe )const;\n      std::map<string,full_account> get_full_accounts( const vector<string>& names_or_ids,\n                                                       optional<bool> subscribe );\n      optional<account_object> get_account_by_name( string name )const;\n      vector<account_id_type> get_account_references( const std::string account_id_or_name )const;\n      vector<optional<account_object>> lookup_account_names(const vector<string>& account_names)const;\n      map<string,account_id_type> lookup_accounts( const string& lower_bound_name,\n                                                   uint32_t limit,\n                                                   optional<bool> subscribe )const;\n      uint64_t get_account_count()const;\n\n      // Balances\n      vector<asset> get_account_balances( const std::string& account_name_or_id,\n                                          const flat_set<asset_id_type>& assets )const;\n      vector<asset> get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets)const;\n      vector<balance_object> get_balance_objects( const vector<address>& addrs )const;\n      vector<asset> get_vested_balances( const vector<balance_id_type>& objs )const;\n      vector<vesting_balance_object> get_vesting_balances( const std::string account_id_or_name )const;\n\n      // Assets\n      uint64_t get_asset_count()const;\n      asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const;\n      vector<optional<extended_asset_object>> get_assets( const vector<std::string>& asset_symbols_or_ids,\n                                                          optional<bool> subscribe )const;\n      vector<extended_asset_object>           list_assets(const string& lower_bound_symbol, uint32_t limit)const;\n      vector<optional<extended_asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;\n      vector<extended_asset_object>           get_assets_by_issuer(const std::string& issuer_name_or_id,\n                                                                   asset_id_type start, uint32_t limit)const;\n\n      // Markets / feeds\n      vector<limit_order_object>         get_limit_orders( const std::string& a, const std::string& b,\n                                                           uint32_t limit)const;\n      vector<limit_order_object>         get_limit_orders_by_account( const string& account_name_or_id,\n                                                                      optional<uint32_t> limit,\n                                                                      optional<limit_order_id_type> start_id );\n      vector<limit_order_object>         get_account_limit_orders( const string& account_name_or_id,\n                                                                   const string &base,\n                                                                   const string &quote, uint32_t limit,\n                                                                   optional<limit_order_id_type> ostart_id,\n                                                                   optional<price> ostart_price );\n      vector<call_order_object>          get_call_orders(const std::string& a, uint32_t limit)const;\n      vector<call_order_object>          get_call_orders_by_account(const std::string& account_name_or_id,\n                                                                    asset_id_type start, uint32_t limit)const;\n      vector<force_settlement_object>    get_settle_orders(const std::string& a, uint32_t limit)const;\n      vector<force_settlement_object>    get_settle_orders_by_account(const std::string& account_name_or_id,\n                                                                      force_settlement_id_type start,\n                                                                      uint32_t limit)const;\n      vector<call_order_object>          get_margin_positions( const std::string account_id_or_name )const;\n      vector<collateral_bid_object>      get_collateral_bids( const std::string& asset,\n                                                              uint32_t limit, uint32_t start)const;\n\n      void subscribe_to_market( std::function<void(const variant&)> callback,\n                                const std::string& a, const std::string& b );\n      void unsubscribe_from_market(const std::string& a, const std::string& b);\n\n      market_ticker                      get_ticker( const string& base, const string& quote,\n                                                     bool skip_order_book = false )const;\n      market_volume                      get_24_volume( const string& base, const string& quote )const;\n      order_book                         get_order_book( const string& base, const string& quote,\n                                                         unsigned limit = 50 )const;\n      vector<market_ticker>              get_top_markets( uint32_t limit )const;\n      vector<market_trade>               get_trade_history( const string& base, const string& quote,\n                                                            fc::time_point_sec start, fc::time_point_sec stop,\n                                                            unsigned limit = 100 )const;\n      vector<market_trade>               get_trade_history_by_sequence( const string& base, const string& quote,\n                                                                        int64_t start, fc::time_point_sec stop,\n                                                                        unsigned limit = 100 )const;\n\n      // Liquidity pools\n      vector<liquidity_pool_object> list_liquidity_pools(\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n      vector<liquidity_pool_object> get_liquidity_pools_by_asset_a(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n      vector<liquidity_pool_object> get_liquidity_pools_by_asset_b(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n      vector<liquidity_pool_object> get_liquidity_pools_by_both_assets(\n            std::string asset_symbol_or_id_a,\n            std::string asset_symbol_or_id_b,\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n      vector<optional<liquidity_pool_object>> get_liquidity_pools_by_share_asset(\n            const vector<std::string>& asset_symbols_or_ids,\n            optional<bool> subscribe = optional<bool>() )const;\n      vector<liquidity_pool_object> get_liquidity_pools_by_owner(\n            std::string account_name_or_id,\n            optional<uint32_t> limit = 101,\n            optional<asset_id_type> start_id = optional<asset_id_type>() )const;\n\n      // Witnesses\n      vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;\n      fc::optional<witness_object> get_witness_by_account(const std::string account_id_or_name)const;\n      map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;\n      uint64_t get_witness_count()const;\n\n      // Committee members\n      vector<optional<committee_member_object>> get_committee_members(\n            const vector<committee_member_id_type>& committee_member_ids )const;\n      fc::optional<committee_member_object> get_committee_member_by_account(\n            const std::string account_id_or_name )const;\n      map<string, committee_member_id_type> lookup_committee_member_accounts(\n            const string& lower_bound_name, uint32_t limit )const;\n      uint64_t get_committee_count()const;\n\n      // Workers\n      vector<worker_object> get_all_workers( const optional<bool> is_expired = optional<bool>() )const;\n      vector<worker_object> get_workers_by_account(const std::string account_id_or_name)const;\n      uint64_t get_worker_count()const;\n\n      // Votes\n      vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;\n\n      // Authority / validation\n      std::string get_transaction_hex(const signed_transaction& trx)const;\n      std::string get_transaction_hex_without_sig(const signed_transaction& trx)const;\n\n      set<public_key_type> get_required_signatures( const signed_transaction& trx,\n                                                    const flat_set<public_key_type>& available_keys )const;\n      set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;\n      set<address> get_potential_address_signatures( const signed_transaction& trx )const;\n      bool verify_authority( const signed_transaction& trx )const;\n      bool verify_account_authority( const string& account_name_or_id,\n                                     const flat_set<public_key_type>& signers )const;\n      processed_transaction validate_transaction( const signed_transaction& trx )const;\n      vector< fc::variant > get_required_fees( const vector<operation>& ops,\n                                               const std::string& asset_id_or_symbol )const;\n\n      // Proposed transactions\n      vector<proposal_object> get_proposed_transactions( const std::string account_id_or_name )const;\n\n      // Blinded balances\n      vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;\n\n      // Withdrawals\n      vector<withdraw_permission_object> get_withdraw_permissions_by_giver( const std::string account_id_or_name,\n                                                                            withdraw_permission_id_type start,\n                                                                            uint32_t limit )const;\n      vector<withdraw_permission_object> get_withdraw_permissions_by_recipient( const std::string account_id_or_name,\n                                                                                withdraw_permission_id_type start,\n                                                                                uint32_t limit )const;\n\n      // HTLC\n      optional<htlc_object> get_htlc( htlc_id_type id, optional<bool> subscribe ) const;\n      vector<htlc_object> get_htlc_by_from( const std::string account_id_or_name,\n                                            htlc_id_type start, uint32_t limit ) const;\n      vector<htlc_object> get_htlc_by_to( const std::string account_id_or_name,\n                                          htlc_id_type start, uint32_t limit) const;\n      vector<htlc_object> list_htlcs(const htlc_id_type lower_bound_id, uint32_t limit) const;\n\n   //private:\n\n      ////////////////////////////////////////////////\n      // Accounts\n      ////////////////////////////////////////////////\n\n      const account_object* get_account_from_string( const std::string& name_or_id,\n                                                     bool throw_if_not_found = true ) const;\n\n      ////////////////////////////////////////////////\n      // Assets\n      ////////////////////////////////////////////////\n\n      template<class ASSET>\n      extended_asset_object extend_asset( ASSET&& a )const\n      {\n         asset_id_type id = a.id;\n         extended_asset_object result = extended_asset_object( std::forward<ASSET>( a ) );\n         if( amount_in_collateral_index )\n         {\n            result.total_in_collateral = amount_in_collateral_index->get_amount_in_collateral( id );\n            if( result.bitasset_data_id.valid() )\n               result.total_backing_collateral = amount_in_collateral_index->get_backing_collateral( id );\n         }\n         return result;\n      }\n\n      const asset_object* get_asset_from_string( const std::string& symbol_or_id,\n                                                 bool throw_if_not_found = true ) const;\n      // helper function\n      vector<optional<extended_asset_object>> get_assets( const vector<asset_id_type>& asset_ids,\n                                                          optional<bool> subscribe = optional<bool>() )const;\n\n      ////////////////////////////////////////////////\n      // Markets\n      ////////////////////////////////////////////////\n\n      // helper function\n      vector<limit_order_object> get_limit_orders( const asset_id_type a, const asset_id_type b,\n                                                   const uint32_t limit )const;\n\n      ////////////////////////////////////////////////\n      // Liquidity pools\n      ////////////////////////////////////////////////\n\n      // template function to reduce duplicate code\n      template <typename X>\n      vector<liquidity_pool_object> get_liquidity_pools_by_asset_x(\n                  std::string asset_symbol_or_id,\n                  optional<uint32_t> olimit,\n                  optional<liquidity_pool_id_type> ostart_id )const\n      {\n         uint32_t limit = olimit.valid() ? *olimit : 101;\n\n         FC_ASSERT( _app_options, \"Internal error\" );\n         const auto configured_limit = _app_options->api_limit_get_liquidity_pools;\n         FC_ASSERT( limit <= configured_limit,\n                    \"limit can not be greater than ${configured_limit}\",\n                    (\"configured_limit\", configured_limit) );\n\n         vector<liquidity_pool_object> results;\n\n         const asset_id_type asset_id = get_asset_from_string(asset_symbol_or_id)->id;\n\n         liquidity_pool_id_type start_id = ostart_id.valid() ? *ostart_id : liquidity_pool_id_type();\n\n         const auto& idx = _db.get_index_type<liquidity_pool_index>().indices().get<X>();\n         auto lower_itr = idx.lower_bound( std::make_tuple( asset_id, start_id ) );\n         auto upper_itr = idx.upper_bound( asset_id );\n\n         results.reserve( limit );\n         uint32_t count = 0;\n         for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)\n         {\n            results.emplace_back( *lower_itr );\n         }\n\n         return results;\n      }\n\n      ////////////////////////////////////////////////\n      // Subscription\n      ////////////////////////////////////////////////\n\n      // Decides whether to subscribe using member variables and given parameter\n      bool get_whether_to_subscribe( optional<bool> subscribe )const\n      {\n         if( !_subscribe_callback )\n            return false;\n         if( subscribe.valid() )\n            return *subscribe;\n         return _enabled_auto_subscription;\n      }\n\n      // Note:\n      //   Different type of object_id<T> objects could become identical after packed.\n      //   For example, both `account_id_type a=1.2.0` and `asset_id_type b=1.3.0` will become `0` after packed.\n      //   In order to avoid collision, we don't use a template function here, instead, we implicitly convert all\n      //   object IDs to `object_id_type` when subscribing.\n      //\n      //   If need to subscribe to other data types, override this function with the types as parameter.\n      //   For example, we had a `get_subscription_key( const public_key_type& item )` function here, which was\n      //   removed lately since we no longer subscribe to public keys.\n      vector<char> get_subscription_key( const object_id_type& item )const\n      {\n         return fc::raw::pack(item);\n      }\n\n      template<typename T>\n      void subscribe_to_item( const T& item )const\n      {\n         if( !_subscribe_callback )\n            return;\n\n         vector<char> key = get_subscription_key( item );\n         if( !_subscribe_filter.contains( key.data(), key.size() ) )\n         {\n            _subscribe_filter.insert( key.data(), key.size() );\n         }\n      }\n\n      template<typename T>\n      bool is_subscribed_to_item( const T& item )const\n      {\n         if( !_subscribe_callback )\n            return false;\n\n         vector<char> key = get_subscription_key( item );\n         return _subscribe_filter.contains( key.data(), key.size() );\n      }\n\n      // for full-account subscription\n      bool is_impacted_account( const flat_set<account_id_type>& accounts );\n\n      // for market subscription\n      template<typename T>\n      const std::pair<asset_id_type,asset_id_type> get_order_market( const T& order )\n      {\n         return order.get_market();\n      }\n\n      // for market subscription\n      const std::pair<asset_id_type,asset_id_type> get_order_market( const force_settlement_object& order )\n      {\n         // TODO cache the result to avoid repeatly fetching from db\n         asset_id_type backing_id = order.balance.asset_id( _db ).bitasset_data( _db ).options.short_backing_asset;\n         auto tmp = std::make_pair( order.balance.asset_id, backing_id );\n         if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );\n         return tmp;\n      }\n\n      template<typename T>\n      void enqueue_if_subscribed_to_market(const object* obj, market_queue_type& queue, bool full_object=true)\n      {\n         const T* order = dynamic_cast<const T*>(obj);\n         FC_ASSERT( order != nullptr);\n\n         const auto& market = get_order_market( *order );\n\n         auto sub = _market_subscriptions.find( market );\n         if( sub != _market_subscriptions.end() ) {\n            queue[market].emplace_back( full_object ? obj->to_variant() : fc::variant(obj->id, 1) );\n         }\n      }\n\n      void broadcast_updates( const vector<variant>& updates );\n      void broadcast_market_updates( const market_queue_type& queue);\n      void handle_object_changed( bool force_notify,\n                                  bool full_object,\n                                  const vector<object_id_type>& ids,\n                                  const flat_set<account_id_type>& impacted_accounts,\n                                  std::function<const object*(object_id_type id)> find_object );\n\n      /** called every time a block is applied to report the objects that were changed */\n      void on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);\n      void on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);\n      void on_objects_removed(const vector<object_id_type>& ids, const vector<const object*>& objs,\n                              const flat_set<account_id_type>& impacted_accounts);\n      void on_applied_block();\n\n      ////////////////////////////////////////////////\n      // Member variables\n      ////////////////////////////////////////////////\n\n      bool _notify_remove_create = false;\n      bool _enabled_auto_subscription = true;\n\n      mutable fc::bloom_filter  _subscribe_filter;\n      std::set<account_id_type> _subscribed_accounts;\n\n      std::function<void(const fc::variant&)> _subscribe_callback;\n      std::function<void(const fc::variant&)> _pending_trx_callback;\n      std::function<void(const fc::variant&)> _block_applied_callback;\n\n      boost::signals2::scoped_connection _new_connection;\n      boost::signals2::scoped_connection _change_connection;\n      boost::signals2::scoped_connection _removed_connection;\n      boost::signals2::scoped_connection _applied_block_connection;\n      boost::signals2::scoped_connection _pending_trx_connection;\n\n      map< pair<asset_id_type,asset_id_type>, std::function<void(const variant&)> > _market_subscriptions;\n\n      graphene::chain::database& _db;\n      const application_options* _app_options = nullptr;\n\n      const graphene::api_helper_indexes::amount_in_collateral_index* amount_in_collateral_index;\n};\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/app/include/graphene/app/api.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/database_api.hpp>\n\n#include <graphene/protocol/types.hpp>\n#include <graphene/protocol/confidential.hpp>\n\n#include <graphene/market_history/market_history_plugin.hpp>\n#include <graphene/grouped_orders/grouped_orders_plugin.hpp>\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n\n#include <graphene/elasticsearch/elasticsearch_plugin.hpp>\n\n#include <graphene/debug_witness/debug_api.hpp>\n\n#include <graphene/net/node.hpp>\n\n#include <fc/api.hpp>\n#include <fc/optional.hpp>\n#include <fc/crypto/elliptic.hpp>\n#include <fc/network/ip.hpp>\n\n#include <boost/container/flat_set.hpp>\n\n#include <functional>\n#include <map>\n#include <string>\n#include <vector>\n\nnamespace graphene { namespace app {\n   using namespace graphene::chain;\n   using namespace graphene::market_history;\n   using namespace graphene::grouped_orders;\n   using namespace graphene::custom_operations;\n\n   using namespace fc::ecc;\n   using std::string;\n   using std::vector;\n   using std::map;\n\n   class application;\n\n   struct verify_range_result\n   {\n      bool        success;\n      uint64_t    min_val;\n      uint64_t    max_val;\n   };\n\n   struct verify_range_proof_rewind_result\n   {\n      bool                          success;\n      uint64_t                      min_val;\n      uint64_t                      max_val;\n      uint64_t                      value_out;\n      fc::ecc::blind_factor_type    blind_out;\n      string                        message_out;\n   };\n\n   struct account_asset_balance\n   {\n      string          name;\n      account_id_type account_id;\n      share_type      amount;\n   };\n   struct asset_holders\n   {\n      asset_id_type   asset_id;\n      int             count;\n   };\n\n   struct history_operation_detail {\n      uint32_t total_count = 0;\n      vector<operation_history_object> operation_history_objs;\n   };\n\n   /**\n    * @brief summary data of a group of limit orders\n    */\n   struct limit_order_group\n   {\n      limit_order_group( const std::pair<limit_order_group_key,limit_order_group_data>& p )\n         :  min_price( p.first.min_price ),\n            max_price( p.second.max_price ),\n            total_for_sale( p.second.total_for_sale )\n            {}\n      limit_order_group() {}\n\n      price         min_price; ///< possible lowest price in the group\n      price         max_price; ///< possible highest price in the group\n      share_type    total_for_sale; ///< total amount of asset for sale, asset id is min_price.base.asset_id\n   };\n\n   /**\n    * @brief The history_api class implements the RPC API for account history\n    *\n    * This API contains methods to access account histories\n    */\n   class history_api\n   {\n      public:\n         history_api(application& app)\n               :_app(app), database_api( std::ref(*app.chain_database()), &(app.get_options())) {}\n\n         /**\n          * @brief Get operations relevant to the specificed account\n          * @param account_id_or_name The account ID or name whose history should be queried\n          * @param stop ID of the earliest operation to retrieve\n          * @param limit Maximum number of operations to retrieve (must not exceed 100)\n          * @param start ID of the most recent operation to retrieve\n          * @return A list of operations performed by account, ordered from most recent to oldest.\n          */\n         vector<operation_history_object> get_account_history(\n            const std::string account_id_or_name,\n            operation_history_id_type stop = operation_history_id_type(),\n            unsigned limit = 100,\n            operation_history_id_type start = operation_history_id_type()\n         )const;\n\n         /**\n          * @brief Get operations relevant to the specified account filtering by operation type\n          * @param account_id_or_name The account ID or name whose history should be queried\n          * @param operation_types The IDs of the operation we want to get operations in the account\n          * ( 0 = transfer , 1 = limit order create, ...)\n          * @param start the sequence number where to start looping back throw the history\n          * @param limit the max number of entries to return (from start number)\n          * @return history_operation_detail\n          */\n         history_operation_detail get_account_history_by_operations(\n            const std::string account_id_or_name,\n            flat_set<uint16_t> operation_types,\n            uint32_t start,\n            unsigned limit\n         )const;\n\n         /**\n          * @brief Get only asked operations relevant to the specified account\n          * @param account_id_or_name The account ID or name whose history should be queried\n          * @param operation_type The type of the operation we want to get operations in the account\n          * ( 0 = transfer , 1 = limit order create, ...)\n          * @param stop ID of the earliest operation to retrieve\n          * @param limit Maximum number of operations to retrieve (must not exceed 100)\n          * @param start ID of the most recent operation to retrieve\n          * @return A list of operations performed by account, ordered from most recent to oldest.\n          */\n         vector<operation_history_object> get_account_history_operations(\n            const std::string account_id_or_name,\n            int operation_type,\n            operation_history_id_type start = operation_history_id_type(),\n            operation_history_id_type stop = operation_history_id_type(),\n            unsigned limit = 100\n         )const;\n\n         /**\n          * @brief Get operations relevant to the specified account referenced\n          * by an event numbering specific to the account. The current number of operations\n          * for the account can be found in the account statistics (or use 0 for start).\n          * @param account_id_or_name The account ID or name whose history should be queried\n          * @param stop Sequence number of earliest operation. 0 is default and will\n          * query 'limit' number of operations.\n          * @param limit Maximum number of operations to retrieve (must not exceed 100)\n          * @param start Sequence number of the most recent operation to retrieve.\n          * 0 is default, which will start querying from the most recent operation.\n          * @return A list of operations performed by account, ordered from most recent to oldest.\n          */\n         vector<operation_history_object> get_relative_account_history( const std::string account_id_or_name,\n                                                                        uint64_t stop = 0,\n                                                                        unsigned limit = 100,\n                                                                        uint64_t start = 0) const;\n\n         /**\n          * @brief Get details of order executions occurred most recently in a trading pair\n          * @param a Asset symbol or ID in a trading pair\n          * @param b The other asset symbol or ID in the trading pair\n          * @param limit Maximum records to return\n          * @return a list of order_history objects, in \"most recent first\" order\n          */\n         vector<order_history_object> get_fill_order_history( std::string a, std::string b, uint32_t limit )const;\n\n         /**\n          * @brief Get OHLCV data of a trading pair in a time range\n          * @param a Asset symbol or ID in a trading pair\n          * @param b The other asset symbol or ID in the trading pair\n          * @param bucket_seconds Length of each time bucket in seconds.\n          * Note: it need to be within result of get_market_history_buckets() API, otherwise no data will be returned\n          * @param start The start of a time range, E.G. \"2018-01-01T00:00:00\"\n          * @param end The end of the time range\n          * @return A list of OHLCV data, in \"least recent first\" order.\n          * If there are more than 200 records in the specified time range, the first 200 records will be returned.\n          */\n         vector<bucket_object> get_market_history( std::string a, std::string b, uint32_t bucket_seconds,\n                                                   fc::time_point_sec start, fc::time_point_sec end )const;\n\n         /**\n          * @brief Get OHLCV time bucket lengths supported (configured) by this API server\n          * @return A list of time bucket lengths in seconds. E.G. if the result contains a number \"300\",\n          * it means this API server supports OHLCV data aggregated in 5-minute buckets.\n          */\n         flat_set<uint32_t> get_market_history_buckets()const;\n      private:\n           application& _app;\n           graphene::app::database_api database_api;\n   };\n\n   /**\n    * @brief Block api\n    */\n   class block_api\n   {\n   public:\n      block_api(graphene::chain::database& db);\n      ~block_api();\n\n      /**\n          * @brief Get signed blocks\n          * @param block_num_from The lowest block number\n          * @param block_num_to The highest block number\n          * @return A list of signed blocks from block_num_from till block_num_to\n          */\n      vector<optional<signed_block>> get_blocks(uint32_t block_num_from, uint32_t block_num_to)const;\n\n   private:\n      graphene::chain::database& _db;\n   };\n\n\n   /**\n    * @brief The network_broadcast_api class allows broadcasting of transactions.\n    */\n   class network_broadcast_api : public std::enable_shared_from_this<network_broadcast_api>\n   {\n      public:\n         network_broadcast_api(application& a);\n\n         struct transaction_confirmation\n         {\n            transaction_id_type   id;\n            uint32_t              block_num;\n            uint32_t              trx_num;\n            processed_transaction trx;\n         };\n\n         typedef std::function<void(variant/*transaction_confirmation*/)> confirmation_callback;\n\n         /**\n          * @brief Broadcast a transaction to the network\n          * @param trx The transaction to broadcast\n          *\n          * The transaction will be checked for validity in the local database prior to broadcasting. If it fails to\n          * apply locally, an error will be thrown and the transaction will not be broadcast.\n          */\n         void broadcast_transaction(const precomputable_transaction& trx);\n\n         /** This version of broadcast transaction registers a callback method that will be called when the\n          * transaction is included into a block.  The callback method includes the transaction id, block number,\n          * and transaction number in the block.\n          * @param cb the callback method\n          * @param trx the transaction\n          */\n         void broadcast_transaction_with_callback( confirmation_callback cb, const precomputable_transaction& trx);\n\n         /** This version of broadcast transaction waits until the transaction is included into a block,\n          *  then the transaction id, block number, and transaction number in the block will be returned.\n          * @param trx the transaction\n          * @return info about the block including the transaction\n          */\n         fc::variant broadcast_transaction_synchronous(const precomputable_transaction& trx);\n\n         /**\n          * @brief Broadcast a signed block to the network\n          * @param block The signed block to broadcast\n          */\n         void broadcast_block( const signed_block& block );\n\n         /**\n          * @brief Not reflected, thus not accessible to API clients.\n          *\n          * This function is registered to receive the applied_block\n          * signal from the chain database when a block is received.\n          * It then dispatches callbacks to clients who have requested\n          * to be notified when a particular txid is included in a block.\n          */\n         void on_applied_block( const signed_block& b );\n      private:\n         boost::signals2::scoped_connection             _applied_block_connection;\n         map<transaction_id_type,confirmation_callback> _callbacks;\n         application&                                   _app;\n   };\n\n   /**\n    * @brief The network_node_api class allows maintenance of p2p connections.\n    */\n   class network_node_api\n   {\n      public:\n         network_node_api(application& a);\n\n         /**\n          * @brief Return general network information, such as p2p port\n          */\n         fc::variant_object get_info() const;\n\n         /**\n          * @brief add_node Connect to a new peer\n          * @param ep The IP/Port of the peer to connect to\n          */\n         void add_node(const fc::ip::endpoint& ep);\n\n         /**\n          * @brief Get status of all current connections to peers\n          */\n         std::vector<net::peer_status> get_connected_peers() const;\n\n         /**\n          * @brief Get advanced node parameters, such as desired and max\n          *        number of connections\n          */\n         fc::variant_object get_advanced_node_parameters() const;\n\n         /**\n          * @brief Set advanced node parameters, such as desired and max\n          *        number of connections\n          * @param params a JSON object containing the name/value pairs for the parameters to set\n          */\n         void set_advanced_node_parameters(const fc::variant_object& params);\n\n         /**\n          * @brief Return list of potential peers\n          */\n         std::vector<net::potential_peer_record> get_potential_peers() const;\n\n      private:\n         application& _app;\n   };\n\n   /**\n    * @brief The crypto_api class allows computations related to blinded transfers.\n    */\n   class crypto_api\n   {\n      public:\n         crypto_api();\n\n         /**\n          * @brief Generates a pedersen commitment: *commit = blind * G + value * G2.\n          * The commitment is 33 bytes, the blinding factor is 32 bytes.\n          * For more information about pederson commitment check url https://en.wikipedia.org/wiki/Commitment_scheme\n          * @param blind Sha-256 blind factor type\n          * @param value Positive 64-bit integer value\n          * @return A 33-byte pedersen commitment: *commit = blind * G + value * G2\n          */\n         fc::ecc::commitment_type blind( const fc::ecc::blind_factor_type& blind, uint64_t value );\n\n         /**\n          * @brief Get sha-256 blind factor type\n          * @param blinds_in List of sha-256 blind factor types\n          * @param non_neg 32-bit integer value\n          * @return A blind factor type\n          */\n         fc::ecc::blind_factor_type blind_sum( const std::vector<blind_factor_type>& blinds_in, uint32_t non_neg );\n\n         /**\n          * @brief Verifies that commits + neg_commits + excess == 0\n          * @param commits_in List of 33-byte pedersen commitments\n          * @param neg_commits_in List of 33-byte pedersen commitments\n          * @param excess Sum of two list of 33-byte pedersen commitments\n          *               where sums the first set and subtracts the second\n          * @return Boolean - true in event of commits + neg_commits + excess == 0, otherwise false\n          */\n         bool verify_sum(\n            const std::vector<commitment_type>& commits_in,\n            const std::vector<commitment_type>& neg_commits_in,\n            int64_t excess\n         );\n\n         /**\n          * @brief Verifies range proof for 33-byte pedersen commitment\n          * @param commit 33-byte pedersen commitment\n          * @param proof List of characters\n          * @return A structure with success, min and max values\n          */\n         verify_range_result verify_range( const fc::ecc::commitment_type& commit, const std::vector<char>& proof );\n\n         /**\n          * @brief Proves with respect to min_value the range for pedersen\n          * commitment which has the provided blinding factor and value\n          * @param min_value Positive 64-bit integer value\n          * @param commit 33-byte pedersen commitment\n          * @param commit_blind Sha-256 blind factor type for the correct digits\n          * @param nonce Sha-256 blind factor type for our non-forged signatures\n          * @param base10_exp Exponents base 10 in range [-1 ; 18] inclusively\n          * @param min_bits 8-bit positive integer, must be in range [0 ; 64] inclusively\n          * @param actual_value 64-bit positive integer, must be greater or equal min_value\n          * @return A list of characters as proof in proof\n          */\n         std::vector<char> range_proof_sign( uint64_t min_value,\n                                             const commitment_type& commit,\n                                             const blind_factor_type& commit_blind,\n                                             const blind_factor_type& nonce,\n                                             int8_t base10_exp,\n                                             uint8_t min_bits,\n                                             uint64_t actual_value );\n\n         /**\n          * @brief Verifies range proof rewind for 33-byte pedersen commitment\n          * @param nonce Sha-256 blind refactor type\n          * @param commit 33-byte pedersen commitment\n          * @param proof List of characters\n          * @return A structure with success, min, max, value_out, blind_out and message_out values\n          */\n         verify_range_proof_rewind_result verify_range_proof_rewind( const blind_factor_type& nonce,\n                                                                     const fc::ecc::commitment_type& commit,\n                                                                     const std::vector<char>& proof );\n\n         /**\n          * @brief Gets \"range proof\" info. The cli_wallet includes functionality for sending blind transfers\n          * in which the values of the input and outputs amounts are “blinded.”\n          * In the case where a transaction produces two or more outputs, (e.g. an amount to the intended\n          * recipient plus “change” back to the sender),\n          * a \"range proof\" must be supplied to prove that none of the outputs commit to a negative value.\n          * @param proof List of proof's characters\n          * @return A range proof info structure with exponent, mantissa, min and max values\n          */\n         range_proof_info range_get_info( const std::vector<char>& proof );\n   };\n\n   /**\n    * @brief The asset_api class allows query of info about asset holders.\n    */\n   class asset_api\n   {\n      public:\n         asset_api(graphene::app::application& app);\n         ~asset_api();\n\n         /**\n          * @brief Get asset holders for a specific asset\n          * @param asset The specific asset id or symbol\n          * @param start The start index\n          * @param limit Maximum limit must not exceed 100\n          * @return A list of asset holders for the specified asset\n          */\n         vector<account_asset_balance> get_asset_holders( std::string asset, uint32_t start, uint32_t limit  )const;\n\n         /**\n          * @brief Get asset holders count for a specific asset\n          * @param asset The specific asset id or symbol\n          * @return Holders count for the specified asset\n          */\n         int get_asset_holders_count( std::string asset )const;\n\n         /**\n          * @brief Get all asset holders\n          * @return A list of all asset holders\n          */\n         vector<asset_holders> get_all_asset_holders() const;\n\n      private:\n         graphene::app::application& _app;\n         graphene::chain::database& _db;\n         graphene::app::database_api database_api;\n   };\n\n   /**\n    * @brief the orders_api class exposes access to data processed with grouped orders plugin.\n    */\n   class orders_api\n   {\n      public:\n         orders_api(application& app)\n         :_app(app), database_api( std::ref(*app.chain_database()), &(app.get_options()) ){}\n         //virtual ~orders_api() {}\n\n         /**\n          * @brief Get tracked groups configured by the server.\n          * @return A list of numbers which indicate configured groups, of those, 1 means 0.01% diff on price.\n          */\n         flat_set<uint16_t> get_tracked_groups()const;\n\n         /**\n          * @brief Get grouped limit orders in given market.\n          *\n          * @param base_asset ID or symbol of asset being sold\n          * @param quote_asset ID or symbol of asset being purchased\n          * @param group Maximum price diff within each order group, have to be one of configured values\n          * @param start Optional price to indicate the first order group to retrieve\n          * @param limit Maximum number of order groups to retrieve (must not exceed 101)\n          * @return The grouped limit orders, ordered from best offered price to worst\n          */\n         vector< limit_order_group > get_grouped_limit_orders( std::string base_asset,\n                                                               std::string quote_asset,\n                                                               uint16_t group,\n                                                               optional<price> start,\n                                                               uint32_t limit )const;\n\n      private:\n         application& _app;\n         graphene::app::database_api database_api;\n   };\n\n   /**\n    * @brief The custom_operations_api class exposes access to standard custom objects parsed by the\n    * custom_operations_plugin.\n    */\n   class custom_operations_api\n   {\n      public:\n         custom_operations_api(application& app):_app(app), database_api( *app.chain_database(),\n               &(app.get_options()) ){}\n\n         /**\n          * @brief Get all stored objects of an account in a particular catalog\n          *\n          * @param account The account ID or name to get info from\n          * @param catalog Category classification. Each account can store multiple catalogs.\n          *\n          * @return The vector of objects of the account or empty\n          */\n         vector<account_storage_object> get_storage_info(std::string account_id_or_name, std::string catalog)const;\n\n   private:\n         application& _app;\n         graphene::app::database_api database_api;\n   };\n} } // graphene::app\n\nextern template class fc::api<graphene::app::block_api>;\nextern template class fc::api<graphene::app::network_broadcast_api>;\nextern template class fc::api<graphene::app::network_node_api>;\nextern template class fc::api<graphene::app::history_api>;\nextern template class fc::api<graphene::app::crypto_api>;\nextern template class fc::api<graphene::app::asset_api>;\nextern template class fc::api<graphene::app::orders_api>;\nextern template class fc::api<graphene::debug_witness::debug_api>;\nextern template class fc::api<graphene::app::custom_operations_api>;\n\nnamespace graphene { namespace app {\n   /**\n    * @brief The login_api class implements the bottom layer of the RPC API\n    *\n    * All other APIs must be requested from this API.\n    */\n   class login_api\n   {\n      public:\n         login_api(application& a);\n         ~login_api();\n\n         /**\n          * @brief Authenticate to the RPC server\n          * @param user Username to login with\n          * @param password Password to login with\n          * @return True if logged in successfully; false otherwise\n          *\n          * @note This must be called prior to requesting other APIs.\n          *       Other APIs may not be accessible until the client has sucessfully authenticated.\n          */\n         bool login(const string& user, const string& password);\n         /// @brief Retrieve the network block API\n         fc::api<block_api> block()const;\n         /// @brief Retrieve the network broadcast API\n         fc::api<network_broadcast_api> network_broadcast()const;\n         /// @brief Retrieve the database API\n         fc::api<database_api> database()const;\n         /// @brief Retrieve the history API\n         fc::api<history_api> history()const;\n         /// @brief Retrieve the network node API\n         fc::api<network_node_api> network_node()const;\n         /// @brief Retrieve the cryptography API\n         fc::api<crypto_api> crypto()const;\n         /// @brief Retrieve the asset API\n         fc::api<asset_api> asset()const;\n         /// @brief Retrieve the orders API\n         fc::api<orders_api> orders()const;\n         /// @brief Retrieve the debug API (if available)\n         fc::api<graphene::debug_witness::debug_api> debug()const;\n         /// @brief Retrieve the custom operations API\n         fc::api<custom_operations_api> custom_operations()const;\n\n         /// @brief Called to enable an API, not reflected.\n         void enable_api( const string& api_name );\n      private:\n\n         application& _app;\n         optional< fc::api<block_api> > _block_api;\n         optional< fc::api<database_api> > _database_api;\n         optional< fc::api<network_broadcast_api> > _network_broadcast_api;\n         optional< fc::api<network_node_api> > _network_node_api;\n         optional< fc::api<history_api> >  _history_api;\n         optional< fc::api<crypto_api> > _crypto_api;\n         optional< fc::api<asset_api> > _asset_api;\n         optional< fc::api<orders_api> > _orders_api;\n         optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;\n         optional< fc::api<custom_operations_api> > _custom_operations_api;\n   };\n\n}}  // graphene::app\n\nextern template class fc::api<graphene::app::login_api>;\n\nFC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation,\n        (id)(block_num)(trx_num)(trx) )\nFC_REFLECT( graphene::app::verify_range_result,\n        (success)(min_val)(max_val) )\nFC_REFLECT( graphene::app::verify_range_proof_rewind_result,\n        (success)(min_val)(max_val)(value_out)(blind_out)(message_out) )\nFC_REFLECT( graphene::app::history_operation_detail,\n            (total_count)(operation_history_objs) )\nFC_REFLECT( graphene::app::limit_order_group,\n            (min_price)(max_price)(total_for_sale) )\n//FC_REFLECT_TYPENAME( fc::ecc::compact_signature )\n//FC_REFLECT_TYPENAME( fc::ecc::commitment_type )\n\nFC_REFLECT( graphene::app::account_asset_balance, (name)(account_id)(amount) )\nFC_REFLECT( graphene::app::asset_holders, (asset_id)(count) )\n\nFC_API(graphene::app::history_api,\n       (get_account_history)\n       (get_account_history_by_operations)\n       (get_account_history_operations)\n       (get_relative_account_history)\n       (get_fill_order_history)\n       (get_market_history)\n       (get_market_history_buckets)\n     )\nFC_API(graphene::app::block_api,\n       (get_blocks)\n     )\nFC_API(graphene::app::network_broadcast_api,\n       (broadcast_transaction)\n       (broadcast_transaction_with_callback)\n       (broadcast_transaction_synchronous)\n       (broadcast_block)\n     )\nFC_API(graphene::app::network_node_api,\n       (get_info)\n       (add_node)\n       (get_connected_peers)\n       (get_potential_peers)\n       (get_advanced_node_parameters)\n       (set_advanced_node_parameters)\n     )\nFC_API(graphene::app::crypto_api,\n       (blind)\n       (blind_sum)\n       (verify_sum)\n       (verify_range)\n       (range_proof_sign)\n       (verify_range_proof_rewind)\n       (range_get_info)\n     )\nFC_API(graphene::app::asset_api,\n       (get_asset_holders)\n\t   (get_asset_holders_count)\n       (get_all_asset_holders)\n     )\nFC_API(graphene::app::orders_api,\n       (get_tracked_groups)\n       (get_grouped_limit_orders)\n     )\nFC_API(graphene::app::custom_operations_api,\n       (get_storage_info)\n     )\nFC_API(graphene::app::login_api,\n       (login)\n       (block)\n       (network_broadcast)\n       (database)\n       (history)\n       (network_node)\n       (crypto)\n       (asset)\n       (orders)\n       (debug)\n       (custom_operations)\n     )\n"
  },
  {
    "path": "libraries/app/include/graphene/app/api_access.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/reflect/reflect.hpp>\n\n#include <map>\n#include <string>\n#include <vector>\n\nnamespace graphene { namespace app {\n\nstruct api_access_info\n{\n   std::string password_hash_b64;\n   std::string password_salt_b64;\n   std::vector< std::string > allowed_apis;\n};\n\nstruct api_access\n{\n   std::map< std::string, api_access_info > permission_map;\n};\n\n} } // graphene::app\n\nFC_REFLECT( graphene::app::api_access_info,\n    (password_hash_b64)\n    (password_salt_b64)\n    (allowed_apis)\n   )\n\nFC_REFLECT( graphene::app::api_access,\n    (permission_map)\n   )\n"
  },
  {
    "path": "libraries/app/include/graphene/app/api_objects.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n\n#include <graphene/api_helper_indexes/api_helper_indexes.hpp>\n#include <graphene/market_history/market_history_plugin.hpp>\n\n#include <fc/optional.hpp>\n\nnamespace graphene { namespace app {\n   using namespace graphene::chain;\n   using namespace graphene::market_history;\n\n   struct more_data\n   {\n      bool balances = false;\n      bool vesting_balances = false;\n      bool limit_orders = false;\n      bool call_orders = false;\n      bool settle_orders = false;\n      bool proposals = false;\n      bool assets = false;\n      bool withdraws_from = false;\n      bool withdraws_to = false;\n      bool htlcs_from = false;\n      bool htlcs_to = false;\n   };\n\n   struct full_account\n   {\n      account_object                   account;\n      account_statistics_object        statistics;\n      string                           registrar_name;\n      string                           referrer_name;\n      string                           lifetime_referrer_name;\n      vector<variant>                  votes;\n      optional<vesting_balance_object> cashback_balance;\n      vector<account_balance_object>   balances;\n      vector<vesting_balance_object>   vesting_balances;\n      vector<limit_order_object>       limit_orders;\n      vector<call_order_object>        call_orders;\n      vector<force_settlement_object>  settle_orders;\n      vector<proposal_object>          proposals;\n      vector<asset_id_type>            assets;\n      vector<withdraw_permission_object> withdraws_from;\n      vector<withdraw_permission_object> withdraws_to;\n      vector<htlc_object>              htlcs_from;\n      vector<htlc_object>              htlcs_to;\n      more_data                        more_data_available;\n   };\n\n   struct order\n   {\n      string                     price;\n      string                     quote;\n      string                     base;\n   };\n\n   struct order_book\n   {\n     string                      base;\n     string                      quote;\n     vector< order >             bids;\n     vector< order >             asks;\n   };\n\n   struct market_ticker\n   {\n      time_point_sec             time;\n      string                     base;\n      string                     quote;\n      string                     latest;\n      string                     lowest_ask;\n      string                     lowest_ask_base_size;\n      string                     lowest_ask_quote_size;\n      string                     highest_bid;\n      string                     highest_bid_base_size;\n      string                     highest_bid_quote_size;\n      string                     percent_change;\n      string                     base_volume;\n      string                     quote_volume;\n\n      market_ticker() {}\n      market_ticker(const market_ticker_object& mto,\n                    const fc::time_point_sec& now,\n                    const asset_object& asset_base,\n                    const asset_object& asset_quote,\n                    const order_book& orders);\n      market_ticker(const fc::time_point_sec& now,\n                    const asset_object& asset_base,\n                    const asset_object& asset_quote);\n   };\n\n   struct market_volume\n   {\n      time_point_sec             time;\n      string                     base;\n      string                     quote;\n      string                     base_volume;\n      string                     quote_volume;\n   };\n\n   struct market_trade\n   {\n      int64_t                    sequence = 0;\n      fc::time_point_sec         date;\n      string                     price;\n      string                     amount;\n      string                     value;\n      string                     type;\n      account_id_type            side1_account_id = GRAPHENE_NULL_ACCOUNT;\n      account_id_type            side2_account_id = GRAPHENE_NULL_ACCOUNT;\n   };\n\n   struct extended_asset_object : asset_object\n   {\n      extended_asset_object() {}\n      explicit extended_asset_object( const asset_object& a ) : asset_object( a ) {}\n      explicit extended_asset_object( asset_object&& a ) : asset_object( std::move(a) ) {}\n\n      optional<share_type> total_in_collateral;\n      optional<share_type> total_backing_collateral;\n   };\n\n} }\n\nFC_REFLECT( graphene::app::more_data,\n            (balances) (vesting_balances) (limit_orders) (call_orders)\n            (settle_orders) (proposals) (assets) (withdraws_from) (withdraws_to) (htlcs_from) (htlcs_to)\n          )\n\nFC_REFLECT( graphene::app::full_account,\n            (account)\n            (statistics)\n            (registrar_name)\n            (referrer_name)\n            (lifetime_referrer_name)\n            (votes)\n            (cashback_balance)\n            (balances)\n            (vesting_balances)\n            (limit_orders)\n            (call_orders)\n            (settle_orders)\n            (proposals)\n            (assets)\n            (withdraws_from)\n            (withdraws_to)\n            (htlcs_from)\n            (htlcs_to)\n            (more_data_available)\n          )\n\nFC_REFLECT( graphene::app::order, (price)(quote)(base) );\nFC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );\nFC_REFLECT( graphene::app::market_ticker,\n            (time)(base)(quote)(latest)(lowest_ask)(lowest_ask_base_size)(lowest_ask_quote_size)\n            (highest_bid)(highest_bid_base_size)(highest_bid_quote_size)(percent_change)(base_volume)(quote_volume) );\nFC_REFLECT( graphene::app::market_volume, (time)(base)(quote)(base_volume)(quote_volume) );\nFC_REFLECT( graphene::app::market_trade, (sequence)(date)(price)(amount)(value)(type)\n            (side1_account_id)(side2_account_id));\n\nFC_REFLECT_DERIVED( graphene::app::extended_asset_object, (graphene::chain::asset_object),\n                    (total_in_collateral)(total_backing_collateral) );\n"
  },
  {
    "path": "libraries/app/include/graphene/app/application.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/api_access.hpp>\n#include <graphene/net/node.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <boost/program_options.hpp>\n\nnamespace graphene { namespace app {\n   namespace detail { class application_impl; }\n   using std::string;\n\n   class abstract_plugin;\n\n   class application_options\n   {\n      public:\n         bool enable_subscribe_to_all = false;\n\n         bool has_api_helper_indexes_plugin = false;\n         bool has_market_history_plugin = false;\n\n         uint64_t api_limit_get_account_history_operations = 100;\n         uint64_t api_limit_get_account_history = 100;\n         uint64_t api_limit_get_grouped_limit_orders = 101;\n         uint64_t api_limit_get_relative_account_history = 100;\n         uint64_t api_limit_get_account_history_by_operations = 100;\n         uint64_t api_limit_get_asset_holders = 100;\n         uint64_t api_limit_get_key_references = 100;\n         uint64_t api_limit_get_htlc_by = 100;\n         uint64_t api_limit_get_full_accounts = 50;\n         uint64_t api_limit_get_full_accounts_lists = 500;\n         uint64_t api_limit_get_call_orders = 300;\n         uint64_t api_limit_get_settle_orders = 300;\n         uint64_t api_limit_get_assets = 101;\n         uint64_t api_limit_get_limit_orders = 300;\n         uint64_t api_limit_get_limit_orders_by_account = 101;\n         uint64_t api_limit_get_order_book = 50;\n         uint64_t api_limit_list_htlcs = 100;\n         uint64_t api_limit_lookup_accounts = 1000;\n         uint64_t api_limit_lookup_witness_accounts = 1000;\n         uint64_t api_limit_lookup_committee_member_accounts = 1000;\n         uint64_t api_limit_lookup_vote_ids = 1000;\n         uint64_t api_limit_get_account_limit_orders = 101;\n         uint64_t api_limit_get_collateral_bids = 100;\n         uint64_t api_limit_get_top_markets = 100;\n         uint64_t api_limit_get_trade_history = 100;\n         uint64_t api_limit_get_trade_history_by_sequence = 100;\n         uint64_t api_limit_get_withdraw_permissions_by_giver = 101;\n         uint64_t api_limit_get_withdraw_permissions_by_recipient = 101;\n         uint64_t api_limit_get_liquidity_pools = 101;\n   };\n\n   class application\n   {\n      public:\n         application();\n         ~application();\n\n         void set_program_options(boost::program_options::options_description& command_line_options,\n                                  boost::program_options::options_description& configuration_file_options)const;\n         void initialize(const fc::path& data_dir, const boost::program_options::variables_map& options);\n         void initialize_plugins(const boost::program_options::variables_map& options);\n         void startup();\n         void shutdown();\n         void startup_plugins();\n         void shutdown_plugins();\n\n         template<typename PluginType>\n         std::shared_ptr<PluginType> register_plugin(bool auto_load = false) {\n            auto plug = std::make_shared<PluginType>();\n            plug->plugin_set_app(this);\n\n            string cli_plugin_desc = plug->plugin_name() + \" plugin. \" + plug->plugin_description() + \"\\nOptions\";\n            boost::program_options::options_description plugin_cli_options( cli_plugin_desc ), plugin_cfg_options;\n            plug->plugin_set_program_options(plugin_cli_options, plugin_cfg_options);\n\n            if( !plugin_cli_options.options().empty() )\n               _cli_options.add(plugin_cli_options);\n\n            if( !plugin_cfg_options.options().empty() )\n            {\n               std::string header_name = \"plugin-cfg-header-\" + plug->plugin_name();\n               std::string header_desc = plug->plugin_name() + \" plugin options\";\n               _cfg_options.add_options()(header_name.c_str(), header_desc.c_str());\n               _cfg_options.add(plugin_cfg_options);\n            }\n\n            add_available_plugin( plug );\n\n            if (auto_load)\n                enable_plugin(plug->plugin_name());\n\n            return plug;\n         }\n         std::shared_ptr<abstract_plugin> get_plugin( const string& name )const;\n\n         template<typename PluginType>\n         std::shared_ptr<PluginType> get_plugin( const string& name ) const\n         {\n            std::shared_ptr<abstract_plugin> abs_plugin = get_plugin( name );\n            std::shared_ptr<PluginType> result = std::dynamic_pointer_cast<PluginType>( abs_plugin );\n            FC_ASSERT( result != std::shared_ptr<PluginType>(), \"Unable to load plugin '${p}'\", (\"p\",name) );\n            return result;\n         }\n\n         net::node_ptr                    p2p_node();\n         std::shared_ptr<chain::database> chain_database()const;\n         void set_api_limit();\n         void set_block_production(bool producing_blocks);\n         fc::optional< api_access_info > get_api_access_info( const string& username )const;\n         void set_api_access_info(const string& username, api_access_info&& permissions);\n\n         bool is_finished_syncing()const;\n         /// Emitted when syncing finishes (is_finished_syncing will return true)\n         boost::signals2::signal<void()> syncing_finished;\n\n         const application_options& get_options();\n\n         void enable_plugin( const string& name );\n\n         bool is_plugin_enabled(const string& name) const;\n\n         std::shared_ptr<fc::thread> elasticsearch_thread;\n\n   private:\n         void add_available_plugin( std::shared_ptr<abstract_plugin> p );\n         std::shared_ptr<detail::application_impl> my;\n\n         boost::program_options::options_description _cli_options;\n         boost::program_options::options_description _cfg_options;\n   };\n\n} }\n"
  },
  {
    "path": "libraries/app/include/graphene/app/config_util.hpp",
    "content": "/*\n * Copyright (c) 2018 Lubos Ilcik, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/filesystem.hpp>\n#include <boost/program_options.hpp>\n\nnamespace graphene { namespace app {\n\n   void load_configuration_options(const fc::path &data_dir, const boost::program_options::options_description &cfg_options,\n                           boost::program_options::variables_map &options);\n\n} } // graphene::app"
  },
  {
    "path": "libraries/app/include/graphene/app/database_api.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/api_objects.hpp>\n\n#include <graphene/protocol/types.hpp>\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/chain_property_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/liquidity_pool_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <fc/api.hpp>\n#include <fc/variant_object.hpp>\n\n#include <fc/network/ip.hpp>\n\n#include <boost/container/flat_set.hpp>\n\n#include <functional>\n#include <map>\n#include <memory>\n#include <vector>\n\nnamespace graphene { namespace app {\n\nusing namespace graphene::chain;\nusing namespace graphene::market_history;\nusing std::string;\nusing std::vector;\nusing std::map;\n\nclass database_api_impl;\n\n/**\n * @brief The database_api class implements the RPC API for the chain database.\n *\n * This API exposes accessors on the database which query state tracked by a blockchain validating node. This API is\n * read-only; all modifications to the database must be performed via transactions. Transactions are broadcast via\n * the @ref network_broadcast_api.\n */\nclass database_api\n{\n   public:\n      database_api(graphene::chain::database& db, const application_options* app_options = nullptr );\n      ~database_api();\n\n      /////////////\n      // Objects //\n      /////////////\n\n      /**\n       * @brief Get the objects corresponding to the provided IDs\n       * @param ids IDs of the objects to retrieve\n       * @param subscribe @a true to subscribe to the queried objects; @a false to not subscribe;\n       *                  @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                  (see @ref set_auto_subscription)\n       * @return The objects retrieved, in the order they are mentioned in ids\n       * @note operation_history_object (1.11.x) and account_transaction_history_object (2.9.x)\n       *       can not be subscribed.\n       *\n       * If any of the provided IDs does not map to an object, a null variant is returned in its position.\n       */\n      fc::variants get_objects( const vector<object_id_type>& ids,\n                                optional<bool> subscribe = optional<bool>() )const;\n\n      ///////////////////\n      // Subscriptions //\n      ///////////////////\n\n      /**\n       * @brief Register a callback handle which then can be used to subscribe to object database changes\n       * @param cb The callback handle to register\n       * @param notify_remove_create Whether subscribe to universal object creation and removal events.\n       *        If this is set to true, the API server will notify all newly created objects and ID of all\n       *        newly removed objects to the client, no matter whether client subscribed to the objects.\n       *        By default, API servers don't allow subscribing to universal events, which can be changed\n       *        on server startup.\n       *\n       * Note: auto-subscription is enabled by default and can be disabled with @ref set_auto_subscription API.\n       */\n      void set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create );\n      /**\n       * @brief Set auto-subscription behavior of follow-up API queries\n       * @param enable whether follow-up API queries will automatically subscribe to queried objects\n       *\n       * Impacts behavior of these APIs:\n       * - get_accounts\n       * - get_assets\n       * - get_objects\n       * - lookup_accounts\n       * - get_full_accounts\n       * - get_htlc\n       * - get_liquidity_pools_by_share_asset\n       *\n       * Note: auto-subscription is enabled by default\n       *\n       * @see @ref set_subscribe_callback\n       */\n      void set_auto_subscription( bool enable );\n      /**\n       * @brief Register a callback handle which will get notified when a transaction is pushed to database\n       * @param cb The callback handle to register\n       *\n       * Note: a transaction can be pushed to database and be popped from database several times while\n       *   processing, before and after included in a block. Everytime when a push is done, the client will\n       *   be notified.\n       */\n      void set_pending_transaction_callback( std::function<void(const variant& signed_transaction_object)> cb );\n      /**\n       * @brief Register a callback handle which will get notified when a block is pushed to database\n       * @param cb The callback handle to register\n       */\n      void set_block_applied_callback( std::function<void(const variant& block_id)> cb );\n      /**\n       * @brief Stop receiving any notifications\n       *\n       * This unsubscribes from all subscribed markets and objects.\n       */\n      void cancel_all_subscriptions();\n\n      /////////////////////////////\n      // Blocks and transactions //\n      /////////////////////////////\n\n      /**\n       * @brief Retrieve a block header\n       * @param block_num Height of the block whose header should be returned\n       * @return header of the referenced block, or null if no matching block was found\n       */\n      optional<block_header> get_block_header(uint32_t block_num)const;\n\n      /**\n      * @brief Retrieve multiple block header by block numbers\n      * @param block_nums vector containing heights of the block whose header should be returned\n      * @return array of headers of the referenced blocks, or null if no matching block was found\n      */\n      map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;\n\n      /**\n       * @brief Retrieve a full, signed block\n       * @param block_num Height of the block to be returned\n       * @return the referenced block, or null if no matching block was found\n       */\n      optional<signed_block> get_block(uint32_t block_num)const;\n\n      /**\n       * @brief used to fetch an individual transaction.\n       * @param block_num height of the block to fetch\n       * @param trx_in_block the index (sequence number) of the transaction in the block, starts from 0\n       * @return the transaction at the given position\n       */\n      processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;\n\n      /**\n       * If the transaction has not expired, this method will return the transaction for the given ID or\n       * it will return NULL if it is not known.  Just because it is not known does not mean it wasn't\n       * included in the blockchain.\n       *\n       * @param txid hash of the transaction\n       * @return the corresponding transaction if found, or null if not found\n       */\n      optional<signed_transaction> get_recent_transaction_by_id( const transaction_id_type& txid )const;\n\n      /////////////\n      // Globals //\n      /////////////\n\n      /**\n       * @brief Retrieve the @ref graphene::chain::chain_property_object associated with the chain\n       */\n      chain_property_object get_chain_properties()const;\n\n      /**\n       * @brief Retrieve the current @ref graphene::chain::global_property_object\n       */\n      global_property_object get_global_properties()const;\n\n      /**\n       * @brief Retrieve compile-time constants\n       */\n      fc::variant_object get_config()const;\n\n      /**\n       * @brief Get the chain ID\n       */\n      chain_id_type get_chain_id()const;\n\n      /**\n       * @brief Retrieve the current @ref graphene::chain::dynamic_global_property_object\n       */\n      dynamic_global_property_object get_dynamic_global_properties()const;\n\n      //////////\n      // Keys //\n      //////////\n\n      /**\n       * @brief Get all accounts that refer to the specified public keys in their owner authority, active authorities\n       *        or memo key\n       * @param keys a list of public keys to query\n       * @return ID of all accounts that refer to the specified keys\n       */\n      vector<flat_set<account_id_type>> get_key_references( vector<public_key_type> keys )const;\n\n      /**\n       * Determine whether a textual representation of a public key\n       * (in Base-58 format) is *currently* linked\n       * to any *registered* (i.e. non-stealth) account on the blockchain\n       * @param public_key Public key\n       * @return Whether a public key is known\n       */\n      bool is_public_key_registered(string public_key) const;\n\n      //////////////\n      // Accounts //\n      //////////////\n\n      /**\n       * @brief Get account object from a name or ID\n       * @param name_or_id name or ID of the account\n       * @return Account ID\n       *\n       */\n      account_id_type get_account_id_from_string(const std::string& name_or_id) const;\n\n      /**\n       * @brief Get a list of accounts by names or IDs\n       * @param account_names_or_ids names or IDs of the accounts to retrieve\n       * @param subscribe @a true to subscribe to the queried account objects; @a false to not subscribe;\n       *                  @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                  (see @ref set_auto_subscription)\n       * @return The accounts corresponding to the provided names or IDs\n       *\n       * This function has semantics identical to @ref get_objects\n       */\n      vector<optional<account_object>> get_accounts( const vector<std::string>& account_names_or_ids,\n                                                     optional<bool> subscribe = optional<bool>() )const;\n\n      /**\n       * @brief Fetch all objects relevant to the specified accounts and optionally subscribe to updates\n       * @param names_or_ids Each item must be the name or ID of an account to retrieve\n       * @param subscribe @a true to subscribe to the queried full account objects; @a false to not subscribe;\n       *                  @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                  (see @ref set_auto_subscription)\n       * @return Map of string from @p names_or_ids to the corresponding account\n       *\n       * This function fetches all relevant objects for the given accounts, and subscribes to updates to the given\n       * accounts. If any of the strings in @p names_or_ids cannot be tied to an account, that input will be\n       * ignored. All other accounts will be retrieved and subscribed.\n       *\n       */\n      std::map<string,full_account> get_full_accounts( const vector<string>& names_or_ids,\n                                                       optional<bool> subscribe = optional<bool>() );\n\n      /**\n       * @brief Get info of an account by name\n       * @param name Name of the account to retrieve\n       * @return The account holding the provided name\n       */\n      optional<account_object> get_account_by_name( string name )const;\n\n      /**\n       * @brief Get all accounts that refer to the specified account in their owner or active authorities\n       * @param account_name_or_id Account name or ID to query\n       * @return all accounts that refer to the specified account in their owner or active authorities\n       */\n      vector<account_id_type> get_account_references( const std::string account_name_or_id )const;\n\n      /**\n       * @brief Get a list of accounts by name\n       * @param account_names Names of the accounts to retrieve\n       * @return The accounts holding the provided names\n       *\n       * This function has semantics identical to @ref get_objects, but doesn't subscribe.\n       */\n      vector<optional<account_object>> lookup_account_names(const vector<string>& account_names)const;\n\n      /**\n       * @brief Get names and IDs for registered accounts\n       * @param lower_bound_name Lower bound of the first name to return\n       * @param limit Maximum number of results to return -- must not exceed 1000\n       * @param subscribe @a true to subscribe to the queried account objects; @a false to not subscribe;\n       *                  @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                  (see @ref set_auto_subscription)\n       * @return Map of account names to corresponding IDs\n       *\n       * @note In addition to the common auto-subscription rules,\n       *       this API will subscribe to the returned account only if @p limit is 1.\n       */\n      map<string,account_id_type> lookup_accounts( const string& lower_bound_name,\n                                                   uint32_t limit,\n                                                   optional<bool> subscribe = optional<bool>() )const;\n\n      //////////////\n      // Balances //\n      //////////////\n\n      /**\n       * @brief Get an account's balances in various assets\n       * @param account_name_or_id name or ID of the account to get balances for\n       * @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in\n       * @return Balances of the account\n       */\n      vector<asset> get_account_balances( const std::string& account_name_or_id,\n                                          const flat_set<asset_id_type>& assets )const;\n\n      /// Semantically equivalent to @ref get_account_balances.\n      vector<asset> get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets)const;\n\n      /**\n       * @brief Return all unclaimed balance objects for a list of addresses\n       * @param addrs a list of addresses\n       * @return all unclaimed balance objects for the addresses\n       */\n      vector<balance_object> get_balance_objects( const vector<address>& addrs )const;\n\n      /**\n       * @brief Calculate how much assets in the given balance objects are able to be claimed at current head\n       *        block time\n       * @param objs a list of balance object IDs\n       * @return a list indicating how much asset in each balance object is available to be claimed\n       */\n      vector<asset> get_vested_balances( const vector<balance_id_type>& objs )const;\n\n      /**\n       * @brief Return all vesting balance objects owned by an account\n       * @param account_name_or_id name or ID of an account\n       * @return all vesting balance objects owned by the account\n       */\n      vector<vesting_balance_object> get_vesting_balances( const std::string account_name_or_id )const;\n\n      /**\n       * @brief Get the total number of accounts registered with the blockchain\n       */\n      uint64_t get_account_count()const;\n\n      ////////////\n      // Assets //\n      ////////////\n\n      /**\n       * @brief Get asset ID from an asset symbol or ID\n       * @param symbol_or_id symbol name or ID of the asset\n       * @return asset ID\n       */\n      asset_id_type get_asset_id_from_string(const std::string& symbol_or_id) const;\n\n      /**\n       * @brief Get a list of assets by symbol names or IDs\n       * @param asset_symbols_or_ids symbol names or IDs of the assets to retrieve\n       * @param subscribe @a true to subscribe to the queried asset objects; @a false to not subscribe;\n       *                  @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                  (see @ref set_auto_subscription)\n       * @return The assets corresponding to the provided symbol names or IDs\n       *\n       * This function has semantics identical to @ref get_objects\n       */\n      vector<optional<extended_asset_object>> get_assets( const vector<std::string>& asset_symbols_or_ids,\n                                                          optional<bool> subscribe = optional<bool>() )const;\n\n      /**\n       * @brief Get assets alphabetically by symbol name\n       * @param lower_bound_symbol Lower bound of symbol names to retrieve\n       * @param limit Maximum number of assets to fetch (must not exceed 101)\n       * @return The assets found\n       */\n      vector<extended_asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;\n\n      /**\n       * @brief Get a list of assets by symbol names or IDs\n       * @param symbols_or_ids symbol names or IDs of the assets to retrieve\n       * @return The assets corresponding to the provided symbols or IDs\n       *\n       * This function has semantics identical to @ref get_objects, but doesn't subscribe\n       */\n      vector<optional<extended_asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;\n\n      /**\n       * @brief Get assets count\n       * @return The assets count\n       */\n      uint64_t get_asset_count()const;\n\n      /**\n       * @brief Get assets issued (owned) by a given account\n       * @param issuer_name_or_id Account name or ID to get objects from\n       * @param start Asset objects(1.3.X) before this ID will be skipped in results. Pagination purposes.\n       * @param limit Maximum number of orders to retrieve\n       * @return The assets issued (owned) by the account\n       */\n      vector<extended_asset_object> get_assets_by_issuer(const std::string& issuer_name_or_id,\n                                                         asset_id_type start, uint32_t limit)const;\n\n      /////////////////////\n      // Markets / feeds //\n      /////////////////////\n\n      /**\n       * @brief Get limit orders in a given market\n       * @param a symbol or ID of asset being sold\n       * @param b symbol or ID of asset being purchased\n       * @param limit Maximum number of orders to retrieve\n       * @return The limit orders, ordered from least price to greatest\n       */\n      vector<limit_order_object> get_limit_orders(std::string a, std::string b, uint32_t limit)const;\n\n      /**\n       * @brief Fetch open limit orders in all markets relevant to the specified account, ordered by ID\n       *\n       * @param account_name_or_id  The name or ID of an account to retrieve\n       * @param limit  The limitation of items each query can fetch, not greater than a configured value\n       * @param start_id  Start order id, fetch orders whose IDs are greater than or equal to this order\n       *\n       * @return List of limit orders of the specified account\n       *\n       * @note\n       * 1. if @p account_name_or_id cannot be tied to an account, an error will be returned\n       * 2. @p limit can be omitted or be null, if so the default value 101 will be used\n       * 3. @p start_id can be omitted or be null, if so the api will return the \"first page\" of orders\n       * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle\n       */\n      vector<limit_order_object> get_limit_orders_by_account( const string& account_name_or_id,\n            optional<uint32_t> limit = 101,\n            optional<limit_order_id_type> start_id = optional<limit_order_id_type>() );\n\n      /**\n       * @brief Fetch all orders relevant to the specified account and specified market, result orders\n       *        are sorted descendingly by price\n       *\n       * @param account_name_or_id  The name or ID of an account to retrieve\n       * @param base  Base asset\n       * @param quote  Quote asset\n       * @param limit  The limitation of items each query can fetch, not greater than 101\n       * @param ostart_id  Start order id, fetch orders which price lower than this order,\n       *                   or price equal to this order but order ID greater than this order\n       * @param ostart_price  Fetch orders with price lower than or equal to this price\n       *\n       * @return List of orders from @p account_name_or_id to the corresponding account\n       *\n       * @note\n       * 1. if @p account_name_or_id cannot be tied to an account, an error will be returned\n       * 2. @p ostart_id and @p ostart_price can be empty, if so the api will return the \"first page\" of orders;\n       *    if @p ostart_id is specified, its price will be used to do page query preferentially,\n       *    otherwise the @p ostart_price will be used;\n       *    @p ostart_id and @p ostart_price may be used cooperatively in case of the order specified by @p ostart_id\n       *    was just canceled accidentally, in such case, the result orders' price may lower or equal to\n       *    @p ostart_price, but orders' id greater than @p ostart_id\n       */\n      vector<limit_order_object> get_account_limit_orders( const string& account_name_or_id,\n                                    const string &base,\n                                    const string &quote,\n                                    uint32_t limit = 101,\n                                    optional<limit_order_id_type> ostart_id = optional<limit_order_id_type>(),\n                                    optional<price> ostart_price = optional<price>());\n\n      /**\n       * @brief Get call orders (aka margin positions) for a given asset\n       * @param a symbol name or ID of the debt asset\n       * @param limit Maximum number of orders to retrieve\n       * @return The call orders, ordered from earliest to be called to latest\n       */\n      vector<call_order_object> get_call_orders(const std::string& a, uint32_t limit)const;\n\n      /**\n       * @brief Get call orders (aka margin positions) of a given account\n       * @param account_name_or_id Account name or ID to get objects from\n       * @param start Asset objects(1.3.X) before this ID will be skipped in results. Pagination purposes.\n       * @param limit Maximum number of objects to retrieve\n       * @return The call orders of the account\n       */\n      vector<call_order_object> get_call_orders_by_account(const std::string& account_name_or_id,\n                                                           asset_id_type start, uint32_t limit)const;\n\n      /**\n       * @brief Get forced settlement orders in a given asset\n       * @param a Symbol or ID of asset being settled\n       * @param limit Maximum number of orders to retrieve\n       * @return The settle orders, ordered from earliest settlement date to latest\n       */\n      vector<force_settlement_object> get_settle_orders(const std::string& a, uint32_t limit)const;\n\n      /**\n       * @brief Get forced settlement orders of a given account\n       * @param account_name_or_id Account name or ID to get objects from\n       * @param start Force settlement objects(1.4.X) before this ID will be skipped in results. Pagination purposes.\n       * @param limit Maximum number of orders to retrieve\n       * @return The settle orders of the account\n       */\n      vector<force_settlement_object> get_settle_orders_by_account( const std::string& account_name_or_id,\n                                                                    force_settlement_id_type start,\n                                                                    uint32_t limit )const;\n\n      /**\n       * @brief Get collateral_bid_objects for a given asset\n       * @param a Symbol or ID of asset\n       * @param limit Maximum number of objects to retrieve\n       * @param start skip that many results\n       * @return The settle orders, ordered from earliest settlement date to latest\n       */\n      vector<collateral_bid_object> get_collateral_bids(const std::string& a, uint32_t limit, uint32_t start)const;\n\n      /**\n       * @brief Get all open margin positions of a given account\n       * @param account_name_or_id name or ID of an account\n       * @return all open margin positions of the account\n       *\n       * Similar to @ref get_call_orders_by_account, but without pagination.\n       */\n      vector<call_order_object> get_margin_positions( const std::string account_name_or_id )const;\n\n      /**\n       * @brief Request notification when the active orders in the market between two assets changes\n       * @param callback Callback method which is called when the market changes\n       * @param a symbol name or ID of the first asset\n       * @param b symbol name or ID of the second asset\n       *\n       * Callback will be passed a variant containing a vector<pair<operation, operation_result>>. The vector will\n       * contain, in order, the operations which changed the market, and their results.\n       */\n      void subscribe_to_market(std::function<void(const variant&)> callback,\n                               const std::string& a, const std::string& b);\n\n      /**\n       * @brief Unsubscribe from updates to a given market\n       * @param a symbol name or ID of the first asset\n       * @param b symbol name or ID of the second asset\n       */\n      void unsubscribe_from_market( const std::string& a, const std::string& b );\n\n      /**\n       * @brief Returns the ticker for the market assetA:assetB\n       * @param base symbol name or ID of the base asset\n       * @param quote symbol name or ID of the quote asset\n       * @return The market ticker for the past 24 hours.\n       */\n      market_ticker get_ticker( const string& base, const string& quote )const;\n\n      /**\n       * @brief Returns the 24 hour volume for the market assetA:assetB\n       * @param base symbol name or ID of the base asset\n       * @param quote symbol name or ID of the quote asset\n       * @return The market volume over the past 24 hours\n       */\n      market_volume get_24_volume( const string& base, const string& quote )const;\n\n      /**\n       * @brief Returns the order book for the market base:quote\n       * @param base symbol name or ID of the base asset\n       * @param quote symbol name or ID of the quote asset\n       * @param limit depth of the order book to retrieve, for bids and asks each, capped at 50\n       * @return Order book of the market\n       */\n      order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const;\n\n      /**\n       * @brief Returns vector of tickers sorted by reverse base_volume\n       * Note: this API is experimental and subject to change in next releases\n       * @param limit Max number of results\n       * @return Desc Sorted ticker vector\n       */\n      vector<market_ticker> get_top_markets(uint32_t limit)const;\n\n      /**\n       * @brief Returns recent trades for the market base:quote, ordered by time, most recent first.\n       * Note: Currently, timezone offsets are not supported. The time must be UTC. The range is [stop, start).\n       *       In case when there are more than 100 trades occurred in the same second, this API only returns\n       *       the first 100 records, can use another API @ref get_trade_history_by_sequence to query for the rest.\n       * @param base symbol or ID of the base asset\n       * @param quote symbol or ID of the quote asset\n       * @param start Start time as a UNIX timestamp, the latest trade to retrieve\n       * @param stop Stop time as a UNIX timestamp, the earliest trade to retrieve\n       * @param limit Number of trasactions to retrieve, capped at 100.\n       * @return Recent transactions in the market\n       */\n      vector<market_trade> get_trade_history( const string& base, const string& quote,\n                                              fc::time_point_sec start, fc::time_point_sec stop,\n                                              unsigned limit = 100 )const;\n\n      /**\n       * @brief Returns trades for the market base:quote, ordered by time, most recent first.\n       * Note: Currently, timezone offsets are not supported. The time must be UTC. The range is [stop, start).\n       * @param base symbol or ID of the base asset\n       * @param quote symbol or ID of the quote asset\n       * @param start Start sequence as an Integer, the latest trade to retrieve\n       * @param stop Stop time as a UNIX timestamp, the earliest trade to retrieve\n       * @param limit Number of trasactions to retrieve, capped at 100\n       * @return Transactions in the market\n       */\n      vector<market_trade> get_trade_history_by_sequence( const string& base, const string& quote,\n                                                          int64_t start, fc::time_point_sec stop,\n                                                          unsigned limit = 100 )const;\n\n\n      /////////////////////\n      // Liquidity pools //\n      /////////////////////\n\n      /**\n       * @brief Get a list of liquidity pools\n       * @param limit  The limitation of items each query can fetch, not greater than a configured value\n       * @param start_id  Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID\n       * @return The liquidity pools\n       *\n       * @note\n       * 1. @p limit can be omitted or be null, if so the default value 101 will be used\n       * 2. @p start_id can be omitted or be null, if so the api will return the \"first page\" of pools\n       * 3. can only omit one or more arguments in the end of the list, but not one or more in the middle\n       */\n      vector<liquidity_pool_object> list_liquidity_pools(\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n\n      /**\n       * @brief Get a list of liquidity pools by the symbol or ID of the first asset in the pool\n       * @param asset_symbol_or_id symbol name or ID of the asset\n       * @param limit  The limitation of items each query can fetch, not greater than a configured value\n       * @param start_id  Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID\n       * @return The liquidity pools\n       *\n       * @note\n       * 1. if @p asset_symbol_or_id cannot be tied to an asset, an error will be returned\n       * 2. @p limit can be omitted or be null, if so the default value 101 will be used\n       * 3. @p start_id can be omitted or be null, if so the api will return the \"first page\" of pools\n       * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle\n       */\n      vector<liquidity_pool_object> get_liquidity_pools_by_asset_a(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n\n      /**\n       * @brief Get a list of liquidity pools by the symbol or ID of the second asset in the pool\n       * @param asset_symbol_or_id symbol name or ID of the asset\n       * @param limit  The limitation of items each query can fetch, not greater than a configured value\n       * @param start_id  Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID\n       * @return The liquidity pools\n       *\n       * @note\n       * 1. if @p asset_symbol_or_id cannot be tied to an asset, an error will be returned\n       * 2. @p limit can be omitted or be null, if so the default value 101 will be used\n       * 3. @p start_id can be omitted or be null, if so the api will return the \"first page\" of pools\n       * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle\n       */\n      vector<liquidity_pool_object> get_liquidity_pools_by_asset_b(\n            std::string asset_symbol_or_id,\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n\n      /**\n       * @brief Get a list of liquidity pools by the symbols or IDs of the two assets in the pool\n       * @param asset_symbol_or_id_a symbol name or ID of one asset\n       * @param asset_symbol_or_id_b symbol name or ID of the other asset\n       * @param limit  The limitation of items each query can fetch, not greater than a configured value\n       * @param start_id  Start liquidity pool id, fetch pools whose IDs are greater than or equal to this ID\n       * @return The liquidity pools\n       *\n       * @note\n       * 1. if @p asset_symbol_or_id_a or @p asset_symbol_or_id_b cannot be tied to an asset,\n       *    an error will be returned\n       * 2. @p limit can be omitted or be null, if so the default value 101 will be used\n       * 3. @p start_id can be omitted or be null, if so the api will return the \"first page\" of pools\n       * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle\n       */\n      vector<liquidity_pool_object> get_liquidity_pools_by_both_assets(\n            std::string asset_symbol_or_id_a,\n            std::string asset_symbol_or_id_b,\n            optional<uint32_t> limit = 101,\n            optional<liquidity_pool_id_type> start_id = optional<liquidity_pool_id_type>() )const;\n\n      /**\n       * @brief Get a list of liquidity pools by their share asset symbols or IDs\n       * @param asset_symbols_or_ids symbol names or IDs of the share assets\n       * @param subscribe @a true to subscribe to the queried objects; @a false to not subscribe;\n       *                  @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                  (see @ref set_auto_subscription)\n       * @return The liquidity pools that the assets are for\n       *\n       * @note if an asset in the list can not be found or is not a share asset of any liquidity pool,\n       *       the corresponding data in the returned list is null.\n       */\n      vector<optional<liquidity_pool_object>> get_liquidity_pools_by_share_asset(\n            const vector<std::string>& asset_symbols_or_ids,\n            optional<bool> subscribe = optional<bool>() )const;\n\n      /**\n       * @brief Get a list of liquidity pools by the name or ID of the owner account\n       * @param account_name_or_id name or ID of the owner account\n       * @param limit  The limitation of items each query can fetch, not greater than a configured value\n       * @param start_id  Start share asset id, fetch pools whose share asset IDs are greater than or equal to this ID\n       * @return The liquidity pools\n       *\n       * @note\n       * 1. if @p account_name_or_id cannot be tied to an account, an error will be returned\n       * 2. @p limit can be omitted or be null, if so the default value 101 will be used\n       * 3. @p start_id can be omitted or be null, if so the api will return the \"first page\" of pools\n       * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle\n       */\n      vector<liquidity_pool_object> get_liquidity_pools_by_owner(\n            std::string account_name_or_id,\n            optional<uint32_t> limit = 101,\n            optional<asset_id_type> start_id = optional<asset_id_type>() )const;\n\n      ///////////////\n      // Witnesses //\n      ///////////////\n\n      /**\n       * @brief Get a list of witnesses by ID\n       * @param witness_ids IDs of the witnesses to retrieve\n       * @return The witnesses corresponding to the provided IDs\n       *\n       * This function has semantics identical to @ref get_objects, but doesn't subscribe\n       */\n      vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;\n\n      /**\n       * @brief Get the witness owned by a given account\n       * @param account_name_or_id The name or ID of the account whose witness should be retrieved\n       * @return The witness object, or null if the account does not have a witness\n       */\n      fc::optional<witness_object> get_witness_by_account(const std::string account_name_or_id)const;\n\n      /**\n       * @brief Get names and IDs for registered witnesses\n       * @param lower_bound_name Lower bound of the first name to return\n       * @param limit Maximum number of results to return -- must not exceed 1000\n       * @return Map of witness names to corresponding IDs\n       */\n      map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;\n\n      /**\n       * @brief Get the total number of witnesses registered with the blockchain\n       */\n      uint64_t get_witness_count()const;\n\n      ///////////////////////\n      // Committee members //\n      ///////////////////////\n\n      /**\n       * @brief Get a list of committee_members by ID\n       * @param committee_member_ids IDs of the committee_members to retrieve\n       * @return The committee_members corresponding to the provided IDs\n       *\n       * This function has semantics identical to @ref get_objects, but doesn't subscribe\n       */\n      vector<optional<committee_member_object>> get_committee_members(\n            const vector<committee_member_id_type>& committee_member_ids)const;\n\n      /**\n       * @brief Get the committee_member owned by a given account\n       * @param account_name_or_id The name or ID of the account whose committee_member should be retrieved\n       * @return The committee_member object, or null if the account does not have a committee_member\n       */\n      fc::optional<committee_member_object> get_committee_member_by_account( const string account_name_or_id )const;\n\n      /**\n       * @brief Get names and IDs for registered committee_members\n       * @param lower_bound_name Lower bound of the first name to return\n       * @param limit Maximum number of results to return -- must not exceed 1000\n       * @return Map of committee_member names to corresponding IDs\n       */\n      map<string, committee_member_id_type> lookup_committee_member_accounts( const string& lower_bound_name,\n                                                                              uint32_t limit )const;\n\n      /**\n       * @brief Get the total number of committee registered with the blockchain\n      */\n      uint64_t get_committee_count()const;\n\n\n      ///////////////////////\n      // Worker proposals  //\n      ///////////////////////\n\n      /**\n       * @brief Get workers\n       * @param is_expired null for all workers, true for expired workers only, false for non-expired workers only\n       * @return A list of worker objects\n       *\n      */\n      vector<worker_object> get_all_workers( const optional<bool> is_expired = optional<bool>() )const;\n\n      /**\n       * @brief Get the workers owned by a given account\n       * @param account_name_or_id The name or ID of the account whose worker should be retrieved\n       * @return A list of worker objects owned by the account\n       */\n      vector<worker_object> get_workers_by_account(const std::string account_name_or_id)const;\n\n      /**\n       * @brief Get the total number of workers registered with the blockchain\n      */\n      uint64_t get_worker_count()const;\n\n\n\n      ///////////\n      // Votes //\n      ///////////\n\n      /**\n       * @brief Given a set of votes, return the objects they are voting for\n       * @param votes a list of vote IDs\n       * @return the referenced objects\n       *\n       * This will be a mixture of committee_member_objects, witness_objects, and worker_objects\n       *\n       * The results will be in the same order as the votes.  Null will be returned for\n       * any vote IDs that are not found.\n       */\n      vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;\n\n      ////////////////////////////\n      // Authority / validation //\n      ////////////////////////////\n\n      /**\n       * @brief Get a hexdump of the serialized binary form of a transaction\n       * @param trx a transaction to get hexdump from\n       * @return the hexdump of the transaction\n       */\n      std::string get_transaction_hex(const signed_transaction& trx)const;\n\n      /**\n       * @brief Get a hexdump of the serialized binary form of a signatures-stripped transaction\n       * @param trx a transaction to get hexdump from\n       * @return the hexdump of the transaction without the signatures\n       */\n      std::string get_transaction_hex_without_sig( const signed_transaction &trx ) const;\n\n      /**\n       *  This API will take a partially signed transaction and a set of public keys that the owner\n       *  has the ability to sign for and return the minimal subset of public keys that should add\n       *  signatures to the transaction.\n       *\n       *  @param trx the transaction to be signed\n       *  @param available_keys a set of public keys\n       *  @return a subset of @p available_keys that could sign for the given transaction\n       */\n      set<public_key_type> get_required_signatures( const signed_transaction& trx,\n                                                    const flat_set<public_key_type>& available_keys )const;\n\n      /**\n       *  This method will return the set of all public keys that could possibly sign for a given transaction.\n       *  This call can be used by wallets to filter their set of public keys to just the relevant subset prior\n       *  to calling @ref get_required_signatures to get the minimum subset.\n       *\n       *  @param trx the transaction to be signed\n       *  @return a set of public keys that could possibly sign for the given transaction\n       */\n      set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;\n\n      /**\n       *  This method will return the set of all addresses that could possibly sign for a given transaction.\n       *\n       *  @param trx the transaction to be signed\n       *  @return a set of addresses that could possibly sign for the given transaction\n       */\n      set<address> get_potential_address_signatures( const signed_transaction& trx )const;\n\n      /**\n       * Check whether a transaction has all of the required signatures\n       * @param trx a transaction to be verified\n       * @return true if the @p trx has all of the required signatures, otherwise throws an exception\n       */\n      bool           verify_authority( const signed_transaction& trx )const;\n\n      /**\n       * @brief Verify that the public keys have enough authority to approve an operation for this account\n       * @param account_name_or_id name or ID of an account to check\n       * @param signers the public keys\n       * @return true if the passed in keys have enough authority to approve an operation for this account\n       */\n      bool verify_account_authority( const string& account_name_or_id,\n                                     const flat_set<public_key_type>& signers )const;\n\n      /**\n       * @brief Validates a transaction against the current state without broadcasting it on the network\n       * @param trx a transaction to be validated\n       * @return a processed_transaction object if the transaction passes the validation,\n                 otherwise an exception will be thrown\n       */\n      processed_transaction validate_transaction( const signed_transaction& trx )const;\n\n      /**\n       * @brief For each operation calculate the required fee in the specified asset type\n       * @param ops a list of operations to be query for required fees\n       * @param asset_symbol_or_id symbol name or ID of an asset that to be used to pay the fees\n       * @return a list of objects which indicates required fees of each operation\n       */\n      vector< fc::variant > get_required_fees( const vector<operation>& ops,\n                                               const std::string& asset_symbol_or_id )const;\n\n      ///////////////////////////\n      // Proposed transactions //\n      ///////////////////////////\n\n      /**\n       * @brief return a set of proposed transactions (aka proposals) that the specified account\n       *        can add approval to or remove approval from\n       * @param account_name_or_id The name or ID of an account\n       * @return a set of proposed transactions that the specified account can act on\n       */\n      vector<proposal_object> get_proposed_transactions( const std::string account_name_or_id )const;\n\n      //////////////////////\n      // Blinded balances //\n      //////////////////////\n\n      /**\n       * @brief return the set of blinded balance objects by commitment ID\n       * @param commitments a set of commitments to query for\n       * @return the set of blinded balance objects by commitment ID\n       */\n      vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;\n\n      /////////////////\n      // Withdrawals //\n      /////////////////\n\n      /**\n       *  @brief Get non expired withdraw permission objects for a giver(ex:recurring customer)\n       *  @param account_name_or_id Account name or ID to get objects from\n       *  @param start Withdraw permission objects(1.12.X) before this ID will be skipped in results.\n       *               Pagination purposes.\n       *  @param limit Maximum number of objects to retrieve\n       *  @return Withdraw permission objects for the account\n       */\n      vector<withdraw_permission_object> get_withdraw_permissions_by_giver( const std::string account_name_or_id,\n                                                                            withdraw_permission_id_type start,\n                                                                            uint32_t limit )const;\n\n      /**\n       *  @brief Get non expired withdraw permission objects for a recipient(ex:service provider)\n       *  @param account_name_or_id Account name or ID to get objects from\n       *  @param start Withdraw permission objects(1.12.X) before this ID will be skipped in results.\n       *               Pagination purposes.\n       *  @param limit Maximum number of objects to retrieve\n       *  @return Withdraw permission objects for the account\n       */\n      vector<withdraw_permission_object> get_withdraw_permissions_by_recipient( const std::string account_name_or_id,\n                                                                                withdraw_permission_id_type start,\n                                                                                uint32_t limit )const;\n\n      //////////\n      // HTLC //\n      //////////\n\n      /**\n       *  @brief Get HTLC object\n       *  @param id HTLC contract id\n       *  @param subscribe @a true to subscribe to the queried HTLC objects; @a false to not subscribe;\n       *                   @a null to subscribe or not subscribe according to current auto-subscription setting\n       *                   (see @ref set_auto_subscription)\n       *  @return HTLC object for the id\n       */\n      optional<htlc_object> get_htlc( htlc_id_type id, optional<bool> subscribe = optional<bool>() ) const;\n\n      /**\n       *  @brief Get non expired HTLC objects using the sender account\n       *  @param account_name_or_id Account name or ID to get objects from\n       *  @param start htlc objects before this ID will be skipped in results. Pagination purposes.\n       *  @param limit Maximum number of objects to retrieve\n       *  @return HTLC objects for the account\n       */\n      vector<htlc_object> get_htlc_by_from( const std::string account_name_or_id,\n                                            htlc_id_type start,\n                                            uint32_t limit ) const;\n\n      /**\n       *  @brief Get non expired HTLC objects using the receiver account\n       *  @param account_name_or_id Account name or ID to get objects from\n       *  @param start htlc objects before this ID will be skipped in results. Pagination purposes.\n       *  @param limit Maximum number of objects to retrieve\n       *  @return HTLC objects for the account\n      */\n      vector<htlc_object> get_htlc_by_to( const std::string account_name_or_id,\n                                          htlc_id_type start,\n                                          uint32_t limit ) const;\n\n      /**\n       * @brief Get all HTLCs\n       * @param start Lower bound of htlc id to start getting results\n       * @param limit Maximum number of htlc objects to fetch\n       * @return The htlc object list\n      */\n      vector<htlc_object> list_htlcs(const htlc_id_type start, uint32_t limit) const;\n\n\nprivate:\n      std::shared_ptr< database_api_impl > my;\n};\n\n} }\n\nextern template class fc::api<graphene::app::database_api>;\n\nFC_API(graphene::app::database_api,\n   // Objects\n   (get_objects)\n\n   // Subscriptions\n   (set_subscribe_callback)\n   (set_auto_subscription)\n   (set_pending_transaction_callback)\n   (set_block_applied_callback)\n   (cancel_all_subscriptions)\n\n   // Blocks and transactions\n   (get_block_header)\n   (get_block_header_batch)\n   (get_block)\n   (get_transaction)\n   (get_recent_transaction_by_id)\n\n   // Globals\n   (get_chain_properties)\n   (get_global_properties)\n   (get_config)\n   (get_chain_id)\n   (get_dynamic_global_properties)\n\n   // Keys\n   (get_key_references)\n   (is_public_key_registered)\n\n   // Accounts\n   (get_account_id_from_string)\n   (get_accounts)\n   (get_full_accounts)\n   (get_account_by_name)\n   (get_account_references)\n   (lookup_account_names)\n   (lookup_accounts)\n   (get_account_count)\n\n   // Balances\n   (get_account_balances)\n   (get_named_account_balances)\n   (get_balance_objects)\n   (get_vested_balances)\n   (get_vesting_balances)\n\n   // Assets\n   (get_assets)\n   (list_assets)\n   (lookup_asset_symbols)\n   (get_asset_count)\n   (get_assets_by_issuer)\n   (get_asset_id_from_string)\n\n   // Markets / feeds\n   (get_order_book)\n   (get_limit_orders)\n   (get_limit_orders_by_account)\n   (get_account_limit_orders)\n   (get_call_orders)\n   (get_call_orders_by_account)\n   (get_settle_orders)\n   (get_settle_orders_by_account)\n   (get_margin_positions)\n   (get_collateral_bids)\n   (subscribe_to_market)\n   (unsubscribe_from_market)\n   (get_ticker)\n   (get_24_volume)\n   (get_top_markets)\n   (get_trade_history)\n   (get_trade_history_by_sequence)\n\n   // Liquidity pools\n   (get_liquidity_pools_by_asset_a)\n   (get_liquidity_pools_by_asset_b)\n   (get_liquidity_pools_by_both_assets)\n   (get_liquidity_pools_by_share_asset)\n   (list_liquidity_pools)\n\n   // Witnesses\n   (get_witnesses)\n   (get_witness_by_account)\n   (lookup_witness_accounts)\n   (get_witness_count)\n\n   // Committee members\n   (get_committee_members)\n   (get_committee_member_by_account)\n   (lookup_committee_member_accounts)\n   (get_committee_count)\n\n   // workers\n   (get_all_workers)\n   (get_workers_by_account)\n   (get_worker_count)\n\n   // Votes\n   (lookup_vote_ids)\n\n   // Authority / validation\n   (get_transaction_hex)\n   (get_transaction_hex_without_sig)\n   (get_required_signatures)\n   (get_potential_signatures)\n   (get_potential_address_signatures)\n   (verify_authority)\n   (verify_account_authority)\n   (validate_transaction)\n   (get_required_fees)\n\n   // Proposed transactions\n   (get_proposed_transactions)\n\n   // Blinded balances\n   (get_blinded_balances)\n\n   // Withdrawals\n   (get_withdraw_permissions_by_giver)\n   (get_withdraw_permissions_by_recipient)\n\n   // HTLC\n   (get_htlc)\n   (get_htlc_by_from)\n   (get_htlc_by_to)\n   (list_htlcs)\n)\n"
  },
  {
    "path": "libraries/app/include/graphene/app/plugin.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/application.hpp>\n\n#include <boost/program_options.hpp>\n#include <fc/io/json.hpp>\n\nnamespace graphene { namespace app {\n\nclass abstract_plugin\n{\n   public:\n      virtual ~abstract_plugin(){}\n      virtual std::string plugin_name()const = 0;\n      virtual std::string plugin_description()const = 0;\n\n      /**\n       * @brief Perform early startup routines and register plugin indexes, callbacks, etc.\n       *\n       * Plugins MUST supply a method initialize() which will be called early in the application startup. This method\n       * should contain early setup code such as initializing variables, adding indexes to the database, registering\n       * callback methods from the database, adding APIs, etc., as well as applying any options in the @ref options map\n       *\n       * This method is called BEFORE the database is open, therefore any routines which require any chain state MUST\n       * NOT be called by this method. These routines should be performed in startup() instead.\n       *\n       * @param options The options passed to the application, via configuration files or command line\n       */\n      virtual void plugin_initialize( const boost::program_options::variables_map& options ) = 0;\n\n      /**\n       * @brief Begin normal runtime operations\n       *\n       * Plugins MUST supply a method startup() which will be called at the end of application startup. This method\n       * should contain code which schedules any tasks, or requires chain state.\n       */\n      virtual void plugin_startup() = 0;\n\n      /**\n       * @brief Cleanly shut down the plugin.\n       *\n       * This is called to request a clean shutdown (e.g. due to SIGINT or SIGTERM).\n       */\n      virtual void plugin_shutdown() = 0;\n\n      /**\n       * @brief Register the application instance with the plugin.\n       *\n       * This is called by the framework to set the application.\n       */\n      virtual void plugin_set_app( application* a ) = 0;\n\n      /**\n       * @brief Fill in command line parameters used by the plugin.\n       *\n       * @param command_line_options All options this plugin supports taking on the command-line\n       * @param config_file_options All options this plugin supports storing in a configuration file\n       *\n       * This method populates its arguments with any\n       * command-line and configuration file options the plugin supports.\n       * If a plugin does not need these options, it\n       * may simply provide an empty implementation of this method.\n       */\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& command_line_options,\n         boost::program_options::options_description& config_file_options\n         ) = 0;\n};\n\n/**\n * Provides basic default implementations of abstract_plugin functions.\n */\n\nclass plugin : public abstract_plugin\n{\n   public:\n      plugin();\n      virtual ~plugin() override;\n\n      virtual std::string plugin_name()const override;\n      virtual std::string plugin_description()const override;\n      virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;\n      virtual void plugin_startup() override;\n      virtual void plugin_shutdown() override;\n      virtual void plugin_set_app( application* app ) override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& command_line_options,\n         boost::program_options::options_description& config_file_options\n         ) override;\n\n      chain::database& database() { return *app().chain_database(); }\n      application& app()const { assert(_app); return *_app; }\n   protected:\n      net::node& p2p_node() { return *app().p2p_node(); }\n\n   private:\n      application* _app = nullptr;\n};\n\n/// @group Some useful tools for boost::program_options arguments using vectors of JSON strings\n/// @{\ntemplate<typename T>\nT dejsonify(const string& s, uint32_t max_depth)\n{\n   return fc::json::from_string(s).as<T>(max_depth);\n}\n\nnamespace impl {\n   template<typename T>\n   T dejsonify( const string& s )\n   {\n      return graphene::app::dejsonify<T>( s, GRAPHENE_MAX_NESTED_OBJECTS );\n   }\n}\n\n#define DEFAULT_VALUE_VECTOR(value) default_value({fc::json::to_string(value)}, fc::json::to_string(value))\n#define LOAD_VALUE_SET(options, name, container, type) \\\nif( options.count(name) ) { \\\n      const std::vector<std::string>& ops = options[name].as<std::vector<std::string>>(); \\\n      std::transform(ops.begin(), ops.end(), std::inserter(container, container.end()), &graphene::app::impl::dejsonify<type>); \\\n}\n/// @}\n\n} } //graphene::app\n"
  },
  {
    "path": "libraries/app/include/graphene/app/util.hpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/uint128.hpp>\n\nnamespace graphene {\nnamespace protocol {\n   struct price;\n}\nnamespace chain {\n   class asset_object;\n}\n\nnamespace app {\n   std::string uint128_amount_to_string( const fc::uint128_t& amount, const uint8_t precision );\n   std::string price_to_string( const graphene::protocol::price& _price,\n                                const uint8_t base_precision,\n                                const uint8_t quote_precision );\n   std::string price_to_string( const graphene::protocol::price& _price,\n                                const graphene::chain::asset_object& _base,\n                                const graphene::chain::asset_object& _quote );\n   std::string price_diff_percent_string( const graphene::protocol::price& old_price,\n                                          const graphene::protocol::price& new_price );\n} }\n"
  },
  {
    "path": "libraries/app/plugin.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\nnamespace graphene { namespace app {\n\nplugin::plugin()\n{\n   _app = nullptr;\n   return;\n}\n\nplugin::~plugin()\n{\n   return;\n}\n\nstd::string plugin::plugin_name()const\n{\n   return \"<unknown plugin>\";\n}\n\nstd::string plugin::plugin_description()const\n{\n   return \"<no description>\";\n}\n\nvoid plugin::plugin_initialize( const boost::program_options::variables_map& options )\n{\n   return;\n}\n\nvoid plugin::plugin_startup()\n{\n   return;\n}\n\nvoid plugin::plugin_shutdown()\n{\n   return;\n}\n\nvoid plugin::plugin_set_app( application* app )\n{\n   _app = app;\n   return;\n}\n\nvoid plugin::plugin_set_program_options(\n   boost::program_options::options_description& command_line_options,\n   boost::program_options::options_description& config_file_options\n)\n{\n   return;\n}\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/app/util.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/multiprecision/cpp_int.hpp>\n\n#include <graphene/app/util.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/chain/asset_object.hpp>\n\nnamespace graphene { namespace app {\n\nusing boost::multiprecision::uint256_t;\n\nstatic fc::uint128_t to_capped_128( const uint256_t& t )\n{\n   if( t >= std::numeric_limits<fc::uint128_t>::max() )\n      return std::numeric_limits<fc::uint128_t>::max();\n   return static_cast<fc::uint128_t>(t);\n}\n\nstd::string uint128_amount_to_string( const fc::uint128_t& amount, const uint8_t precision )\n{ try {\n   std::string s = fc::variant( amount, 2 ).as_string();\n   if( precision == 0 || amount == fc::uint128_t() )\n      return s;\n\n   std::stringstream ss;\n   uint8_t pos = s.find_last_not_of( '0' ); // should be >= 0\n   uint8_t len = s.size();\n   if( len > precision )\n   {\n      uint8_t left_len = len - precision;\n      ss << s.substr( 0, left_len );\n      if( pos >= left_len )\n         ss << '.' << s.substr( left_len, pos - left_len + 1 );\n   }\n   else\n   {\n      ss << \"0.\";\n      for( uint8_t i = precision - len; i > 0; --i )\n         ss << '0';\n      ss << s.substr( 0, pos + 1 );\n   }\n   return ss.str();\n} FC_CAPTURE_AND_RETHROW( (amount)(precision) ) }\n\nstd::string price_to_string( const graphene::protocol::price& _price,\n                             const uint8_t base_precision,\n                             const uint8_t quote_precision )\n{ try {\n   if( _price.base.amount == 0 )\n      return \"0\";\n   FC_ASSERT( _price.base.amount >= 0 );\n   FC_ASSERT( _price.quote.amount >= 0 );\n   FC_ASSERT( base_precision <= 19 );\n   FC_ASSERT( quote_precision <= 19 );\n   graphene::protocol::price new_price = _price;\n   if( new_price.quote.amount == 0 )\n   {\n      new_price.base.amount = std::numeric_limits<int64_t>::max();\n      new_price.quote.amount = 1;\n   }\n\n   // times (10**19) so won't overflow but have good accuracy\n   fc::uint128_t price128 = fc::uint128_t( new_price.base.amount.value ) * uint64_t(10000000000000000000ULL)\n                                                                     / new_price.quote.amount.value;\n\n   return uint128_amount_to_string( price128, 19 + base_precision - quote_precision );\n} FC_CAPTURE_AND_RETHROW( (_price)(base_precision)(quote_precision) ) }\n\nstd::string price_to_string( const graphene::protocol::price& _price,\n                             const graphene::chain::asset_object& _base,\n                             const graphene::chain::asset_object& _quote )\n{ try {\n   if( _price.base.asset_id == _base.id && _price.quote.asset_id == _quote.id )\n      return price_to_string( _price, _base.precision, _quote.precision );\n   else if( _price.base.asset_id == _quote.id && _price.quote.asset_id == _base.id )\n      return price_to_string( ~_price, _base.precision, _quote.precision );\n   else\n      FC_ASSERT( !\"bad parameters\" );\n} FC_CAPTURE_AND_RETHROW( (_price)(_base)(_quote) ) }\n\nstd::string price_diff_percent_string( const graphene::protocol::price& old_price,\n                                       const graphene::protocol::price& new_price )\n{ try {\n   FC_ASSERT( old_price.base.asset_id == new_price.base.asset_id );\n   FC_ASSERT( old_price.quote.asset_id == new_price.quote.asset_id );\n   FC_ASSERT( old_price.base.amount >= 0 );\n   FC_ASSERT( old_price.quote.amount >= 0 );\n   FC_ASSERT( new_price.base.amount >= 0 );\n   FC_ASSERT( new_price.quote.amount >= 0 );\n   graphene::protocol::price old_price1 = old_price;\n   if( old_price.base.amount == 0 )\n   {\n      old_price1.base.amount = 1;\n      old_price1.quote.amount = std::numeric_limits<int64_t>::max();\n   }\n   else if( old_price.quote.amount == 0 )\n   {\n      old_price1.base.amount = std::numeric_limits<int64_t>::max();\n      old_price1.quote.amount = 1;\n   }\n   graphene::protocol::price new_price1 = new_price;\n   if( new_price.base.amount == 0 )\n   {\n      new_price1.base.amount = 1;\n      new_price1.quote.amount = std::numeric_limits<int64_t>::max();\n   }\n   else if( new_price.quote.amount == 0 )\n   {\n      new_price1.base.amount = std::numeric_limits<int64_t>::max();\n      new_price1.quote.amount = 1;\n   }\n\n   // change = new/old - 1 = (new_base/new_quote)/(old_base/old_quote) - 1\n   //        = (new_base * old_quote) / (new_quote * old_base) - 1\n   //        = (new_base * old_quote - new_quote * old_base) / (new_quote * old_base)\n   uint256_t new256 = uint256_t( new_price1.base.amount.value ) * old_price1.quote.amount.value;\n   uint256_t old256 = uint256_t( old_price1.base.amount.value ) * new_price1.quote.amount.value;\n   bool non_negative = (new256 >= old256);\n   uint256_t diff256;\n   if( non_negative )\n      diff256 = new256 - old256;\n   else\n      diff256 = old256 - new256;\n   diff256 = diff256 * 10000 / old256;\n   std::string diff_str = uint128_amount_to_string( to_capped_128(diff256), 2 ); // at most 2 decimal digits\n   if( non_negative || diff_str == \"0\" )\n      return diff_str;\n   else\n      return \"-\" + diff_str;\n} FC_CAPTURE_AND_RETHROW( (old_price)(new_price) ) }\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/chain/CMakeLists.txt",
    "content": "\nset_source_files_properties( \"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp\" PROPERTIES GENERATED TRUE )\n\nfile(GLOB HEADERS \"include/graphene/chain/*.hpp\")\n\nif( GRAPHENE_DISABLE_UNITY_BUILD )\n   set( GRAPHENE_DB_FILES\n        db_balance.cpp\n        db_block.cpp\n        db_debug.cpp\n        db_getter.cpp\n        db_init.cpp\n        db_maint.cpp\n        db_management.cpp\n        db_market.cpp\n        db_update.cpp\n        db_witness_schedule.cpp\n        db_notify.cpp\n      )\n   message( STATUS \"Graphene database unity build disabled\" )\nelse( GRAPHENE_DISABLE_UNITY_BUILD )\n   set( GRAPHENE_DB_FILES\n        database.cpp )\n   message( STATUS \"Graphene database unity build enabled\" )\nendif( GRAPHENE_DISABLE_UNITY_BUILD )\n\n## SORT .cpp by most likely to change / break compile\nadd_library( graphene_chain\n\n             # As database takes the longest to compile, start it first\n             ${GRAPHENE_DB_FILES}\n             fork_database.cpp\n\n             genesis_state.cpp\n             get_config.cpp\n             exceptions.cpp\n\n             evaluator.cpp\n             liquidity_pool_evaluator.cpp\n             balance_evaluator.cpp\n             account_evaluator.cpp\n             assert_evaluator.cpp\n             witness_evaluator.cpp\n             committee_member_evaluator.cpp\n             asset_evaluator.cpp\n             transfer_evaluator.cpp\n             proposal_evaluator.cpp\n             market_evaluator.cpp\n             ticket_evaluator.cpp\n             vesting_balance_evaluator.cpp\n             withdraw_permission_evaluator.cpp\n             worker_evaluator.cpp\n             htlc_evaluator.cpp\n             confidential_evaluator.cpp\n             special_authority_evaluation.cpp\n             custom_authority_evaluator.cpp\n             buyback.cpp\n\n             account_object.cpp\n             asset_object.cpp\n             fba_object.cpp\n             market_object.cpp\n             proposal_object.cpp\n             vesting_balance_object.cpp\n             ticket_object.cpp\n             small_objects.cpp\n\n             block_database.cpp\n\n             is_authorized_asset.cpp\n\n             ${HEADERS}\n             \"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp\"\n           )\n\nadd_dependencies( graphene_chain build_hardfork_hpp )\ntarget_link_libraries( graphene_chain fc graphene_db graphene_protocol )\ntarget_include_directories( graphene_chain\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" \"${CMAKE_CURRENT_BINARY_DIR}/include\" )\n\nset( GRAPHENE_CHAIN_BIG_FILES\n     db_init.cpp\n     db_block.cpp\n     db_maint.cpp\n     db_market.cpp\n     database.cpp\n     block_database.cpp\n   )\n\nif(MSVC)\n  set_source_files_properties( ${GRAPHENE_CHAIN_BIG_FILES} PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nelse( MSVC )\n   if( MINGW )\n      set_source_files_properties( ${GRAPHENE_CHAIN_BIG_FILES} PROPERTIES COMPILE_FLAGS -Wa,-mbig-obj )\n      if( (CMAKE_BUILD_TYPE MATCHES Release) OR (CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) )\n         # Use -Os to avoid string table overflow\n         set_source_files_properties( database.cpp PROPERTIES COMPILE_FLAGS -Os )\n         set_source_files_properties( database.cpp PROPERTIES LINK_FLAGS -Os )\n         set_source_files_properties( database.cpp PROPERTIES STATIC_LIBRARY_FLAGS -Os )\n      endif( CMAKE_BUILD_TYPE )\n   endif( MINGW )\nendif(MSVC)\n\n# Support for CMake < 3.4\nif (${CMAKE_VERSION} VERSION_LESS \"3.4.0\")\n    MESSAGE(STATUS \"Configuring for legacy CMake (CMake version ${CMAKE_VERSION} older than 3.4.0)\")\n    MESSAGE(STATUS \"  Target propertes not supported (SOURCE_DIR, BINARY_DIR)\")\n\n    set(GRAPHENE_CHAIN_BIN_LEGACY \"${PROJECT_BINARY_DIR}/libraries/chain\" CACHE STRING \"Path to fc chain\")\n    set(GRAPHENE_CHAIN_SOURCE_LEGACY \"${PROJECT_SOURCE_DIR}/libraries/chain\" CACHE STRING \"Path to fc chain\")\nendif ()\n\nINSTALL( TARGETS\n   graphene_chain\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/chain\" )\n"
  },
  {
    "path": "libraries/chain/account_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/account_evaluator.hpp>\n#include <graphene/chain/buyback.hpp>\n#include <graphene/chain/buyback_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/internal_exceptions.hpp>\n#include <graphene/chain/special_authority_evaluation.hpp>\n#include <graphene/chain/special_authority_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n\n#include <algorithm>\n\nnamespace graphene { namespace chain {\n\nvoid verify_authority_accounts( const database& db, const authority& a )\n{\n   const auto& chain_params = db.get_global_properties().parameters;\n   GRAPHENE_ASSERT(\n      a.num_auths() <= chain_params.maximum_authority_membership,\n      internal_verify_auth_max_auth_exceeded,\n      \"Maximum authority membership exceeded\" );\n   for( const auto& acnt : a.account_auths )\n   {\n      GRAPHENE_ASSERT( db.find_object( acnt.first ) != nullptr,\n         internal_verify_auth_account_not_found,\n         \"Account ${a} specified in authority does not exist\",\n         (\"a\", acnt.first) );\n   }\n}\n\nvoid verify_account_votes( const database& db, const account_options& options )\n{\n   // ensure account's votes satisfy requirements\n   // NB only the part of vote checking that requires chain state is here,\n   // the rest occurs in account_options::validate()\n\n   const auto& gpo = db.get_global_properties();\n   const auto& chain_params = gpo.parameters;\n\n   FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count,\n              \"Voted for more witnesses than currently allowed (${c})\", (\"c\", chain_params.maximum_witness_count) );\n   FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,\n              \"Voted for more committee members than currently allowed (${c})\", (\"c\", chain_params.maximum_committee_count) );\n\n   FC_ASSERT( db.find_object(options.voting_account), \"Invalid proxy account specified.\" );\n\n   uint32_t max_vote_id = gpo.next_available_vote_id;\n   bool has_worker_votes = false;\n   for( auto id : options.votes )\n   {\n      FC_ASSERT( id < max_vote_id, \"Can not vote for ${id} which does not exist.\", (\"id\",id) );\n      has_worker_votes |= (id.type() == vote_id_type::worker);\n   }\n\n   if( has_worker_votes && (db.head_block_time() >= HARDFORK_607_TIME) )\n   {\n      const auto& against_worker_idx = db.get_index_type<worker_index>().indices().get<by_vote_against>();\n      for( auto id : options.votes )\n      {\n         if( id.type() == vote_id_type::worker )\n         {\n            FC_ASSERT( against_worker_idx.find( id ) == against_worker_idx.end(),\n                       \"Can no longer vote against a worker.\" );\n         }\n      }\n   }\n   if ( db.head_block_time() >= HARDFORK_CORE_143_TIME ) {\n      const auto& approve_worker_idx = db.get_index_type<worker_index>().indices().get<by_vote_for>();\n      const auto& committee_idx = db.get_index_type<committee_member_index>().indices().get<by_vote_id>();\n      const auto& witness_idx = db.get_index_type<witness_index>().indices().get<by_vote_id>();\n      for ( auto id : options.votes ) {\n         switch ( id.type() ) {\n            case vote_id_type::committee:\n               FC_ASSERT( committee_idx.find(id) != committee_idx.end(),\n                          \"Can not vote for ${id} which does not exist.\", (\"id\",id) );\n               break;\n            case vote_id_type::witness:\n               FC_ASSERT( witness_idx.find(id) != witness_idx.end(),\n                          \"Can not vote for ${id} which does not exist.\", (\"id\",id) );\n               break;\n            case vote_id_type::worker:\n               FC_ASSERT( approve_worker_idx.find( id ) != approve_worker_idx.end(),\n                          \"Can not vote for ${id} which does not exist.\", (\"id\",id) );\n               break;\n            default:\n               FC_THROW( \"Invalid Vote Type: ${id}\", (\"id\", id) );\n               break;\n         }\n      }\n   }\n}\n\nvoid_result account_create_evaluator::do_evaluate( const account_create_operation& op )\n{ try {\n   database& d = db();\n\n   FC_ASSERT( fee_paying_account->is_lifetime_member(), \"Only Lifetime members may register an account.\" );\n   FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), \"The referrer must be either a lifetime or annual subscriber.\" );\n\n   try\n   {\n      verify_authority_accounts( d, op.owner );\n      verify_authority_accounts( d, op.active );\n   }\n   GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded )\n   GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found )\n\n   if( op.extensions.value.owner_special_authority.valid() )\n      evaluate_special_authority( d, *op.extensions.value.owner_special_authority );\n   if( op.extensions.value.active_special_authority.valid() )\n      evaluate_special_authority( d, *op.extensions.value.active_special_authority );\n   if( op.extensions.value.buyback_options.valid() )\n      evaluate_buyback_account_options( d, *op.extensions.value.buyback_options );\n   verify_account_votes( d, op.options );\n\n   auto& acnt_indx = d.get_index_type<account_index>();\n   if( op.name.size() )\n   {\n      auto current_account_itr = acnt_indx.indices().get<by_name>().find( op.name );\n      FC_ASSERT( current_account_itr == acnt_indx.indices().get<by_name>().end(),\n                 \"Account '${a}' already exists.\", (\"a\",op.name) );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nobject_id_type account_create_evaluator::do_apply( const account_create_operation& o )\n{ try {\n\n   database& d = db();\n   uint16_t referrer_percent = o.referrer_percent;\n   bool has_small_percent = (\n         (db().head_block_time() <= HARDFORK_453_TIME)\n      && (o.referrer != o.registrar  )\n      && (o.referrer_percent != 0    )\n      && (o.referrer_percent <= 0x100)\n      );\n\n   if( has_small_percent )\n   {\n      if( referrer_percent >= 100 )\n      {\n         wlog( \"between 100% and 0x100%:  ${o}\", (\"o\", o) );\n      }\n      referrer_percent = referrer_percent*100;\n      if( referrer_percent > GRAPHENE_100_PERCENT )\n         referrer_percent = GRAPHENE_100_PERCENT;\n   }\n\n   const auto& global_properties = d.get_global_properties();\n\n   const auto& new_acnt_object = d.create<account_object>( [&o,&d,&global_properties,referrer_percent]( account_object& obj )\n   {\n         obj.registrar = o.registrar;\n         obj.referrer = o.referrer;\n         obj.lifetime_referrer = o.referrer(d).lifetime_referrer;\n\n         const auto& params = global_properties.parameters;\n         obj.network_fee_percentage = params.network_percent_of_fee;\n         obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee;\n         obj.referrer_rewards_percentage = referrer_percent;\n\n         obj.name             = o.name;\n         obj.owner            = o.owner;\n         obj.active           = o.active;\n         obj.options          = o.options;\n         obj.num_committee_voted = o.options.num_committee_voted();\n         obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){\n                             s.owner = obj.id;\n                             s.name = obj.name;\n                             s.is_voting = obj.options.is_voting();\n                          }).id;\n\n         if( o.extensions.value.owner_special_authority.valid() )\n            obj.owner_special_authority = *(o.extensions.value.owner_special_authority);\n         if( o.extensions.value.active_special_authority.valid() )\n            obj.active_special_authority = *(o.extensions.value.active_special_authority);\n         if( o.extensions.value.buyback_options.valid() )\n         {\n            obj.allowed_assets = o.extensions.value.buyback_options->markets;\n            obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy );\n         }\n   });\n\n   const auto& dynamic_properties = d.get_dynamic_global_properties();\n   d.modify(dynamic_properties, [](dynamic_global_property_object& p) {\n      ++p.accounts_registered_this_interval;\n   });\n\n   if( dynamic_properties.accounts_registered_this_interval % global_properties.parameters.accounts_per_fee_scale == 0\n         && global_properties.parameters.account_fee_scale_bitshifts != 0 )\n   {\n      d.modify(global_properties, [](global_property_object& p) {\n         p.parameters.get_mutable_fees().get<account_create_operation>().basic_fee <<= p.parameters.account_fee_scale_bitshifts;\n      });\n   }\n\n   if(    o.extensions.value.owner_special_authority.valid()\n       || o.extensions.value.active_special_authority.valid() )\n   {\n      db().create< special_authority_object >( [&]( special_authority_object& sa )\n      {\n         sa.account = new_acnt_object.id;\n      } );\n   }\n\n   if( o.extensions.value.buyback_options.valid() )\n   {\n      asset_id_type asset_to_buy = o.extensions.value.buyback_options->asset_to_buy;\n\n      d.create< buyback_object >( [&]( buyback_object& bo )\n      {\n         bo.asset_to_buy = asset_to_buy;\n      } );\n\n      d.modify( asset_to_buy(d), [&]( asset_object& a )\n      {\n         a.buyback_account = new_acnt_object.id;\n      } );\n   }\n\n   return new_acnt_object.id;\n} FC_CAPTURE_AND_RETHROW((o)) }\n\n\nvoid_result account_update_evaluator::do_evaluate( const account_update_operation& o )\n{ try {\n   database& d = db();\n\n   try\n   {\n      if( o.owner )  verify_authority_accounts( d, *o.owner );\n      if( o.active ) verify_authority_accounts( d, *o.active );\n   }\n   GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded )\n   GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found )\n\n   if( o.extensions.value.owner_special_authority.valid() )\n      evaluate_special_authority( d, *o.extensions.value.owner_special_authority );\n   if( o.extensions.value.active_special_authority.valid() )\n      evaluate_special_authority( d, *o.extensions.value.active_special_authority );\n\n   acnt = &o.account(d);\n\n   if( o.new_options.valid() )\n      verify_account_votes( d, *o.new_options );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result account_update_evaluator::do_apply( const account_update_operation& o )\n{ try {\n   database& d = db();\n\n   bool sa_before = acnt->has_special_authority();\n\n   // update account statistics\n   if( o.new_options.valid() )\n   {\n      if ( o.new_options->voting_account != acnt->options.voting_account\n           || o.new_options->votes != acnt->options.votes )\n      {\n         d.modify( acnt->statistics( d ), [&d,&o]( account_statistics_object& aso )\n         {\n            aso.is_voting = o.new_options->is_voting();\n            aso.last_vote_time = d.head_block_time();\n         } );\n      }\n   }\n\n   // update account object\n   d.modify( *acnt, [&o](account_object& a){\n      if( o.owner )\n      {\n         a.owner = *o.owner;\n         a.top_n_control_flags = 0;\n      }\n      if( o.active )\n      {\n         a.active = *o.active;\n         a.top_n_control_flags = 0;\n      }\n      if( o.new_options )\n      {\n         a.options = *o.new_options;\n         a.num_committee_voted = a.options.num_committee_voted();\n      }\n      if( o.extensions.value.owner_special_authority.valid() )\n      {\n         a.owner_special_authority = *(o.extensions.value.owner_special_authority);\n         a.top_n_control_flags = 0;\n      }\n      if( o.extensions.value.active_special_authority.valid() )\n      {\n         a.active_special_authority = *(o.extensions.value.active_special_authority);\n         a.top_n_control_flags = 0;\n      }\n   });\n\n   bool sa_after = acnt->has_special_authority();\n\n   if( sa_before && (!sa_after) )\n   {\n      const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get<by_account>();\n      auto sa_it = sa_idx.find( o.account );\n      assert( sa_it != sa_idx.end() );\n      d.remove( *sa_it );\n   }\n   else if( (!sa_before) && sa_after )\n   {\n      d.create< special_authority_object >( [&]( special_authority_object& sa )\n      {\n         sa.account = o.account;\n      } );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result account_whitelist_evaluator::do_evaluate(const account_whitelist_operation& o)\n{ try {\n   database& d = db();\n\n   listed_account = &o.account_to_list(d);\n   if( !d.get_global_properties().parameters.allow_non_member_whitelists )\n      FC_ASSERT( o.authorizing_account(d).is_lifetime_member(), \"The authorizing account must be a lifetime member.\" );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result account_whitelist_evaluator::do_apply(const account_whitelist_operation& o)\n{ try {\n   database& d = db();\n\n   d.modify(*listed_account, [&o](account_object& a) {\n      if( o.new_listing & o.white_listed )\n         a.whitelisting_accounts.insert(o.authorizing_account);\n      else\n         a.whitelisting_accounts.erase(o.authorizing_account);\n\n      if( o.new_listing & o.black_listed )\n         a.blacklisting_accounts.insert(o.authorizing_account);\n      else\n         a.blacklisting_accounts.erase(o.authorizing_account);\n   });\n\n   /** for tracking purposes only, this state is not needed to evaluate */\n   d.modify( o.authorizing_account(d), [&]( account_object& a ) {\n     if( o.new_listing & o.white_listed )\n        a.whitelisted_accounts.insert( o.account_to_list );\n     else\n        a.whitelisted_accounts.erase( o.account_to_list );\n\n     if( o.new_listing & o.black_listed )\n        a.blacklisted_accounts.insert( o.account_to_list );\n     else\n        a.blacklisted_accounts.erase( o.account_to_list );\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluator::operation_type& o)\n{ try {\n   database& d = db();\n\n   account = &d.get(o.account_to_upgrade);\n   FC_ASSERT(!account->is_lifetime_member());\n\n   return {};\n//} FC_CAPTURE_AND_RETHROW( (o) ) }\n} FC_RETHROW_EXCEPTIONS( error, \"Unable to upgrade account '${a}'\", (\"a\",o.account_to_upgrade(db()).name) ) }\n\nvoid_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o)\n{ try {\n   database& d = db();\n\n   d.modify(*account, [&](account_object& a) {\n      if( o.upgrade_to_lifetime_member )\n      {\n         // Upgrade to lifetime member. I don't care what the account was before.\n         a.statistics(d).process_fees(a, d);\n         a.membership_expiration_date = time_point_sec::maximum();\n         a.referrer = a.registrar = a.lifetime_referrer = a.get_id();\n         a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;\n      } else if( a.is_annual_member(d.head_block_time()) ) {\n         // Renew an annual subscription that's still in effect.\n         FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );\n         FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650),\n                   \"May not extend annual membership more than a decade into the future.\");\n         a.membership_expiration_date += fc::days(365);\n      } else {\n         // Upgrade from basic account.\n         FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );\n         a.statistics(d).process_fees(a, d);\n         assert(a.is_basic_account(d.head_block_time()));\n         a.referrer = a.get_id();\n         a.membership_expiration_date = d.head_block_time() + fc::days(365);\n      }\n   });\n\n   return {};\n} FC_RETHROW_EXCEPTIONS( error, \"Unable to upgrade account '${a}'\", (\"a\",o.account_to_upgrade(db()).name) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/account_object.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/uint128.hpp>\n\nnamespace graphene { namespace chain {\n\nshare_type cut_fee(share_type a, uint16_t p)\n{\n   if( a == 0 || p == 0 )\n      return 0;\n   if( p == GRAPHENE_100_PERCENT )\n      return a;\n\n   fc::uint128_t r = a.value;\n   r *= p;\n   r /= GRAPHENE_100_PERCENT;\n   return static_cast<uint64_t>(r);\n}\n\nvoid account_balance_object::adjust_balance(const asset& delta)\n{\n   assert(delta.asset_id == asset_type);\n   balance += delta.amount;\n   if( asset_type == asset_id_type() ) // CORE asset\n      maintenance_flag = true;\n}\n\nvoid account_statistics_object::process_fees(const account_object& a, database& d) const\n{\n   if( pending_fees > 0 || pending_vested_fees > 0 )\n   {\n      auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting)\n      {\n         // Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead.\n         // No need to check the registrar; registrars are required to be lifetime members.\n         if( account.referrer(d).is_basic_account(d.head_block_time()) )\n            d.modify( account, [](account_object& acc) {\n               acc.referrer = acc.lifetime_referrer;\n            });\n\n         share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage);\n         assert( network_cut <= core_fee_total );\n\n#ifndef NDEBUG\n         const auto& props = d.get_global_properties();\n\n         share_type reserveed = cut_fee(network_cut, props.parameters.reserve_percent_of_fee);\n         share_type accumulated = network_cut - reserveed;\n         assert( accumulated + reserveed == network_cut );\n#endif\n         share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage);\n         share_type referral = core_fee_total - network_cut - lifetime_cut;\n\n         d.modify( d.get_core_dynamic_data(), [network_cut](asset_dynamic_data_object& addo) {\n            addo.accumulated_fees += network_cut;\n         });\n\n         // Potential optimization: Skip some of this math and object lookups by special casing on the account type.\n         // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to\n         // it directly.\n         share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage);\n         share_type registrar_cut = referral - referrer_cut;\n\n         d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting);\n         d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting);\n         d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting);\n\n         assert( referrer_cut + registrar_cut + accumulated + reserveed + lifetime_cut == core_fee_total );\n      };\n\n      pay_out_fees(a, pending_fees, true);\n      pay_out_fees(a, pending_vested_fees, false);\n\n      d.modify(*this, [&](account_statistics_object& s) {\n         s.lifetime_fees_paid += pending_fees + pending_vested_fees;\n         s.pending_fees = 0;\n         s.pending_vested_fees = 0;\n      });\n   }\n}\n\nvoid account_statistics_object::pay_fee( share_type core_fee, share_type cashback_vesting_threshold )\n{\n   if( core_fee > cashback_vesting_threshold )\n      pending_fees += core_fee;\n   else\n      pending_vested_fees += core_fee;\n}\n\nset<account_id_type> account_member_index::get_account_members(const account_object& a)const\n{\n   set<account_id_type> result;\n   for( auto auth : a.owner.account_auths )\n      result.insert(auth.first);\n   for( auto auth : a.active.account_auths )\n      result.insert(auth.first);\n   return result;\n}\nset<public_key_type, pubkey_comparator> account_member_index::get_key_members(const account_object& a)const\n{\n   set<public_key_type, pubkey_comparator> result;\n   for( auto auth : a.owner.key_auths )\n      result.insert(auth.first);\n   for( auto auth : a.active.key_auths )\n      result.insert(auth.first);\n   result.insert( a.options.memo_key );\n   return result;\n}\nset<address> account_member_index::get_address_members(const account_object& a)const\n{\n   set<address> result;\n   for( auto auth : a.owner.address_auths )\n      result.insert(auth.first);\n   for( auto auth : a.active.address_auths )\n      result.insert(auth.first);\n   result.insert( a.options.memo_key );\n   return result;\n}\n\nvoid account_member_index::object_inserted(const object& obj)\n{\n    assert( dynamic_cast<const account_object*>(&obj) ); // for debug only\n    const account_object& a = static_cast<const account_object&>(obj);\n\n    auto account_members = get_account_members(a);\n    for( auto item : account_members )\n       account_to_account_memberships[item].insert(obj.id);\n\n    auto key_members = get_key_members(a);\n    for( auto item : key_members )\n       account_to_key_memberships[item].insert(obj.id);\n\n    auto address_members = get_address_members(a);\n    for( auto item : address_members )\n       account_to_address_memberships[item].insert(obj.id);\n}\n\nvoid account_member_index::object_removed(const object& obj)\n{\n    assert( dynamic_cast<const account_object*>(&obj) ); // for debug only\n    const account_object& a = static_cast<const account_object&>(obj);\n\n    auto key_members = get_key_members(a);\n    for( auto item : key_members )\n       account_to_key_memberships[item].erase( obj.id );\n\n    auto address_members = get_address_members(a);\n    for( auto item : address_members )\n       account_to_address_memberships[item].erase( obj.id );\n\n    auto account_members = get_account_members(a);\n    for( auto item : account_members )\n       account_to_account_memberships[item].erase( obj.id );\n}\n\nvoid account_member_index::about_to_modify(const object& before)\n{\n   before_key_members.clear();\n   before_account_members.clear();\n   assert( dynamic_cast<const account_object*>(&before) ); // for debug only\n   const account_object& a = static_cast<const account_object&>(before);\n   before_key_members     = get_key_members(a);\n   before_address_members = get_address_members(a);\n   before_account_members = get_account_members(a);\n}\n\nvoid account_member_index::object_modified(const object& after)\n{\n    assert( dynamic_cast<const account_object*>(&after) ); // for debug only\n    const account_object& a = static_cast<const account_object&>(after);\n\n    {\n       set<account_id_type> after_account_members = get_account_members(a);\n       vector<account_id_type> removed; removed.reserve(before_account_members.size());\n       std::set_difference(before_account_members.begin(), before_account_members.end(),\n                           after_account_members.begin(), after_account_members.end(),\n                           std::inserter(removed, removed.end()));\n\n       for( auto itr = removed.begin(); itr != removed.end(); ++itr )\n          account_to_account_memberships[*itr].erase(after.id);\n\n       vector<object_id_type> added; added.reserve(after_account_members.size());\n       std::set_difference(after_account_members.begin(), after_account_members.end(),\n                           before_account_members.begin(), before_account_members.end(),\n                           std::inserter(added, added.end()));\n\n       for( auto itr = added.begin(); itr != added.end(); ++itr )\n          account_to_account_memberships[*itr].insert(after.id);\n    }\n\n\n    {\n       set<public_key_type, pubkey_comparator> after_key_members = get_key_members(a);\n\n       vector<public_key_type> removed; removed.reserve(before_key_members.size());\n       std::set_difference(before_key_members.begin(), before_key_members.end(),\n                           after_key_members.begin(), after_key_members.end(),\n                           std::inserter(removed, removed.end()));\n\n       for( auto itr = removed.begin(); itr != removed.end(); ++itr )\n          account_to_key_memberships[*itr].erase(after.id);\n\n       vector<public_key_type> added; added.reserve(after_key_members.size());\n       std::set_difference(after_key_members.begin(), after_key_members.end(),\n                           before_key_members.begin(), before_key_members.end(),\n                           std::inserter(added, added.end()));\n\n       for( auto itr = added.begin(); itr != added.end(); ++itr )\n          account_to_key_memberships[*itr].insert(after.id);\n    }\n\n    {\n       set<address> after_address_members = get_address_members(a);\n\n       vector<address> removed; removed.reserve(before_address_members.size());\n       std::set_difference(before_address_members.begin(), before_address_members.end(),\n                           after_address_members.begin(), after_address_members.end(),\n                           std::inserter(removed, removed.end()));\n\n       for( auto itr = removed.begin(); itr != removed.end(); ++itr )\n          account_to_address_memberships[*itr].erase(after.id);\n\n       vector<address> added; added.reserve(after_address_members.size());\n       std::set_difference(after_address_members.begin(), after_address_members.end(),\n                           before_address_members.begin(), before_address_members.end(),\n                           std::inserter(added, added.end()));\n\n       for( auto itr = added.begin(); itr != added.end(); ++itr )\n          account_to_address_memberships[*itr].insert(after.id);\n    }\n\n}\n\nconst uint8_t  balances_by_account_index::bits = 20;\nconst uint64_t balances_by_account_index::mask = (1ULL << balances_by_account_index::bits) - 1;\n\nvoid balances_by_account_index::object_inserted( const object& obj )\n{\n   const auto& abo = dynamic_cast< const account_balance_object& >( obj );\n   while( balances.size() < (abo.owner.instance.value >> bits) + 1 )\n   {\n      balances.reserve( (abo.owner.instance.value >> bits) + 1 );\n      balances.resize( balances.size() + 1 );\n      balances.back().resize( 1ULL << bits );\n   }\n   balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask][abo.asset_type] = &abo;\n}\n\nvoid balances_by_account_index::object_removed( const object& obj )\n{\n   const auto& abo = dynamic_cast< const account_balance_object& >( obj );\n   if( balances.size() < (abo.owner.instance.value >> bits) + 1 ) return;\n   balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask].erase( abo.asset_type );\n}\n\nvoid balances_by_account_index::about_to_modify( const object& before )\n{\n   ids_being_modified.emplace( before.id );\n}\n\nvoid balances_by_account_index::object_modified( const object& after  )\n{\n   FC_ASSERT( ids_being_modified.top() == after.id, \"Modification of ID is not supported!\");\n   ids_being_modified.pop();\n}\n\nconst map< asset_id_type, const account_balance_object* >& balances_by_account_index::get_account_balances( const account_id_type& acct )const\n{\n   static const map< asset_id_type, const account_balance_object* > _empty;\n\n   if( balances.size() < (acct.instance.value >> bits) + 1 ) return _empty;\n   return balances[acct.instance.value >> bits][acct.instance.value & mask];\n}\n\nconst account_balance_object* balances_by_account_index::get_account_balance( const account_id_type& acct, const asset_id_type& asset )const\n{\n   if( balances.size() < (acct.instance.value >> bits) + 1 ) return nullptr;\n   const auto& mine = balances[acct.instance.value >> bits][acct.instance.value & mask];\n   const auto itr = mine.find( asset );\n   if( mine.end() == itr ) return nullptr;\n   return itr->second;\n}\n\n} } // graphene::chain\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::account_object,\n                    (graphene::db::object),\n                    (membership_expiration_date)(registrar)(referrer)(lifetime_referrer)\n                    (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)\n                    (name)(owner)(active)(options)(num_committee_voted)(statistics)\n                    (whitelisting_accounts)(blacklisting_accounts)\n                    (whitelisted_accounts)(blacklisted_accounts)\n                    (cashback_vb)\n                    (owner_special_authority)(active_special_authority)\n                    (top_n_control_flags)\n                    (allowed_assets)\n                    )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::account_balance_object,\n                    (graphene::db::object),\n                    (owner)(asset_type)(balance)(maintenance_flag) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::account_statistics_object,\n                    (graphene::chain::object),\n                    (owner)(name)\n                    (most_recent_op)\n                    (total_ops)(removed_ops)\n                    (total_core_in_orders)\n                    (total_core_inactive)(total_core_pob)(total_core_pol)\n                    (total_pob_value)(total_pol_value)\n                    (core_in_balance)\n                    (has_cashback_vb)\n                    (is_voting)\n                    (last_vote_time)\n                    (lifetime_fees_paid)\n                    (pending_fees)(pending_vested_fees)\n                  )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::account_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::account_balance_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::account_statistics_object )\n"
  },
  {
    "path": "libraries/chain/assert_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/assert_evaluator.hpp>\n#include <graphene/chain/block_summary_object.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <sstream>\n\nnamespace graphene { namespace chain {\n\nstruct predicate_evaluator\n{\n   typedef void result_type;\n   const database& db;\n\n   predicate_evaluator( const database& d ):db(d){}\n\n   void operator()( const  account_name_eq_lit_predicate& p )const\n   {\n      FC_ASSERT( p.account_id(db).name == p.name );\n   }\n   void operator()( const  asset_symbol_eq_lit_predicate& p )const\n   {\n      FC_ASSERT( p.asset_id(db).symbol == p.symbol );\n   }\n   void operator()( const block_id_predicate& p )const\n   {\n      FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id );\n   }\n};\n\nvoid_result assert_evaluator::do_evaluate( const assert_operation& o )\n{ try {\n   const database& _db = db();\n   uint32_t skip = _db.get_node_properties().skip_flags;\n   auto max_predicate_opcode = _db.get_global_properties().parameters.max_predicate_opcode;\n\n   if( skip & database::skip_assert_evaluation )\n      return void_result();\n\n   for( const auto& p : o.predicates )\n   {\n      FC_ASSERT( p.which() >= 0 );\n      FC_ASSERT( unsigned(p.which()) < max_predicate_opcode );\n      p.visit( predicate_evaluator( _db ) );\n   }\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result assert_evaluator::do_apply( const assert_operation& o )\n{ try {\n   // assert_operation is always a no-op\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/asset_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/asset_evaluator.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n#include <functional>\n\nnamespace graphene { namespace chain {\nnamespace detail {\n\n   // TODO review and remove code below and links to it after hf_1774\n   void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options)\n   {\n      if( block_time < HARDFORK_1774_TIME )\n      {\n         FC_ASSERT( !options.extensions.value.reward_percent.valid() ||\n                    *options.extensions.value.reward_percent < GRAPHENE_100_PERCENT,\n            \"Asset extension reward percent must be less than 100% till HARDFORK_1774_TIME!\");\n      }\n   }\n\n   void check_bitasset_options_hf_bsip74( const fc::time_point_sec& block_time, const bitasset_options& options)\n   {\n      // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:\n      FC_ASSERT( block_time >= HARDFORK_CORE_BSIP74_TIME\n            || !options.extensions.value.margin_call_fee_ratio.valid(),\n            \"A BitAsset's MCFR cannot be set before Hardfork BSIP74\" );\n   }\n\n   // TODO review and remove code below and links to it after HARDFORK_BSIP_81_TIME\n   void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options)\n   {\n      if (block_time < HARDFORK_BSIP_81_TIME) {\n         // Taker fees should not be set until activation of BSIP81\n         FC_ASSERT(!options.extensions.value.taker_fee_percent.valid(),\n                   \"Taker fee percent should not be defined before HARDFORK_BSIP_81_TIME\");\n      }\n   }\n\n   // TODO review and remove code below and links to it after HARDFORK_BSIP_48_75_TIME\n   void check_asset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const asset_options& options)\n   {\n      if ( !HARDFORK_BSIP_48_75_PASSED( block_time ) )\n      {\n         // new issuer permissions should not be set until activation of BSIP_48_75\n         FC_ASSERT( !(options.issuer_permissions & ~ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK),\n                    \"New asset issuer permission bits should not be set before HARDFORK_BSIP_48_75_TIME\" );\n         // Note: no check for flags here because we didn't check in the past\n      }\n   }\n\n   // TODO review and remove code below and links to it after HARDFORK_BSIP_48_75_TIME\n   void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const bitasset_options& options)\n   {\n      if ( !HARDFORK_BSIP_48_75_PASSED( block_time ) )\n      {\n         // new params should not be set until activation of BSIP_48_75\n         FC_ASSERT( !options.extensions.value.maintenance_collateral_ratio.valid(),\n                    \"Maintenance collateral ratio should not be defined by asset owner \"\n                    \"before HARDFORK_BSIP_48_75_TIME\" );\n         FC_ASSERT( !options.extensions.value.maximum_short_squeeze_ratio.valid(),\n                    \"Maximum short squeeze ratio should not be defined by asset owner \"\n                    \"before HARDFORK_BSIP_48_75_TIME\" );\n      }\n   }\n\n   // TODO review and remove code below and links to it after HARDFORK_BSIP_48_75_TIME\n   void check_asset_update_extensions_hf_bsip_48_75( const fc::time_point_sec& block_time,\n                                                     const asset_update_operation::ext& extensions )\n   {\n      if ( !HARDFORK_BSIP_48_75_PASSED( block_time ) )\n      {\n         // new extensions should not be set until activation of BSIP_48_75\n         FC_ASSERT( !extensions.new_precision.valid(),\n                    \"new_precision should not be set before HARDFORK_BSIP_48_75_TIME\" );\n         FC_ASSERT( !extensions.skip_core_exchange_rate.valid(),\n                    \"skip_core_exchange_rate should not be set before HARDFORK_BSIP_48_75_TIME\" );\n      }\n   }\n\n   // TODO review and remove code below and links to it after HARDFORK_BSIP_77_TIME\n   void check_asset_publish_feed_extensions_hf_bsip77( const fc::time_point_sec& block_time,\n                                                       const asset_publish_feed_operation::ext& extensions )\n   {\n      if ( !HARDFORK_BSIP_77_PASSED( block_time ) )\n      {\n         // new extensions should not be set until activation of BSIP_77\n         FC_ASSERT( !extensions.initial_collateral_ratio.valid(),\n                   \"Initial collateral ratio should not be defined before HARDFORK_BSIP_77_TIME\" );\n      }\n   }\n\n   // TODO review and remove code below and links to it after HARDFORK_BSIP_77_TIME\n   void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time, const bitasset_options& options)\n   {\n      if ( !HARDFORK_BSIP_77_PASSED( block_time ) ) {\n         // ICR should not be set until activation of BSIP77\n         FC_ASSERT(!options.extensions.value.initial_collateral_ratio.valid(),\n                   \"Initial collateral ratio should not be defined before HARDFORK_BSIP_77_TIME\");\n      }\n   }\n\n   void check_bitasset_options_hf_bsip87(const fc::time_point_sec& block_time, const bitasset_options& options)\n   {\n      // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:\n      FC_ASSERT( !options.extensions.value.force_settle_fee_percent.valid()\n                 || block_time >= HARDFORK_CORE_BSIP87_TIME,\n                 \"A BitAsset's FSFP cannot be set before Hardfork BSIP87\" );\n   }\n\n   void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time,\n                                                        const asset_claim_fees_operation& op)\n   {\n      // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:\n      FC_ASSERT( !op.extensions.value.claim_from_asset_id.valid() ||\n                 block_time >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME,\n                 \"Collateral-denominated fees are not yet active and therefore cannot be claimed.\" );\n   }\n\n} // graphene::chain::detail\n\nvoid_result asset_create_evaluator::do_evaluate( const asset_create_operation& op )\n{ try {\n\n   const database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   // Hardfork Checks:\n   detail::check_asset_options_hf_1774(now, op.common_options);\n   detail::check_asset_options_hf_bsip_48_75(now, op.common_options);\n   detail::check_asset_options_hf_bsip81(now, op.common_options);\n   if( op.bitasset_opts ) {\n      detail::check_bitasset_options_hf_bsip_48_75( now, *op.bitasset_opts );\n      detail::check_bitasset_options_hf_bsip74( now, *op.bitasset_opts ); // HF_REMOVABLE\n      detail::check_bitasset_options_hf_bsip77( now, *op.bitasset_opts ); // HF_REMOVABLE\n      detail::check_bitasset_options_hf_bsip87( now, *op.bitasset_opts ); // HF_REMOVABLE\n   }\n\n   // TODO move as many validations as possible to validate() if not triggered before hardfork\n   if( HARDFORK_BSIP_48_75_PASSED( now ) )\n   {\n      op.common_options.validate_flags( op.bitasset_opts.valid() );\n   }\n\n   const auto& chain_parameters = d.get_global_properties().parameters;\n   FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );\n   FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );\n\n   // Check that all authorities do exist\n   for( auto id : op.common_options.whitelist_authorities )\n      d.get_object(id);\n   for( auto id : op.common_options.blacklist_authorities )\n      d.get_object(id);\n\n   auto& asset_indx = d.get_index_type<asset_index>().indices().get<by_symbol>();\n   auto asset_symbol_itr = asset_indx.find( op.symbol );\n   FC_ASSERT( asset_symbol_itr == asset_indx.end() );\n\n   // This must remain due to \"BOND.CNY\" being allowed before this HF\n   if( now > HARDFORK_385_TIME )\n   {\n      auto dotpos = op.symbol.rfind( '.' );\n      if( dotpos != std::string::npos )\n      {\n         auto prefix = op.symbol.substr( 0, dotpos );\n         auto asset_symbol_itr = asset_indx.find( prefix );\n         FC_ASSERT( asset_symbol_itr != asset_indx.end(),\n                    \"Asset ${s} may only be created by issuer of asset ${p}, but asset ${p} has not been created\",\n                    (\"s\",op.symbol)(\"p\",prefix) );\n         FC_ASSERT( asset_symbol_itr->issuer == op.issuer, \"Asset ${s} may only be created by issuer of ${p}, ${i}\",\n                    (\"s\",op.symbol)(\"p\",prefix)(\"i\", op.issuer(d).name) );\n      }\n   }\n\n   if( op.bitasset_opts )\n   {\n      const asset_object& backing = op.bitasset_opts->short_backing_asset(d);\n      if( backing.is_market_issued() )\n      {\n         const asset_bitasset_data_object& backing_bitasset_data = backing.bitasset_data(d);\n         const asset_object& backing_backing = backing_bitasset_data.options.short_backing_asset(d);\n         FC_ASSERT( !backing_backing.is_market_issued(),\n                    \"May not create a bitasset backed by a bitasset backed by a bitasset.\" );\n         FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing_backing.get_id() == asset_id_type(),\n                    \"May not create a blockchain-controlled market asset which is not backed by CORE.\");\n      } else\n         FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing.get_id() == asset_id_type(),\n                    \"May not create a blockchain-controlled market asset which is not backed by CORE.\");\n      FC_ASSERT( op.bitasset_opts->feed_lifetime_sec > chain_parameters.block_interval &&\n                 op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval );\n   }\n\n   if( op.is_prediction_market )\n   {\n      FC_ASSERT( op.bitasset_opts );\n      FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid asset_create_evaluator::pay_fee()\n{\n   fee_is_odd = core_fee_paid.value & 1;\n   core_fee_paid -= core_fee_paid.value/2;\n   generic_evaluator::pay_fee();\n}\n\nobject_id_type asset_create_evaluator::do_apply( const asset_create_operation& op )\n{ try {\n   database& d = db();\n\n   bool hf_429 = fee_is_odd && d.head_block_time() > HARDFORK_CORE_429_TIME;\n\n   const asset_dynamic_data_object& dyn_asset =\n      d.create<asset_dynamic_data_object>( [hf_429,this]( asset_dynamic_data_object& a ) {\n         a.current_supply = 0;\n         a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0);\n      });\n\n   if( fee_is_odd && !hf_429 )\n   {\n      d.modify( d.get_core_dynamic_data(), []( asset_dynamic_data_object& dd ) {\n         dd.current_supply++;\n      });\n   }\n\n   asset_bitasset_data_id_type bit_asset_id;\n\n   auto next_asset_id = d.get_index_type<asset_index>().get_next_id();\n\n   if( op.bitasset_opts.valid() )\n      bit_asset_id = d.create<asset_bitasset_data_object>( [&op,next_asset_id]( asset_bitasset_data_object& a ) {\n            a.options = *op.bitasset_opts;\n            a.is_prediction_market = op.is_prediction_market;\n            a.asset_id = next_asset_id;\n         }).id;\n\n   const asset_object& new_asset =\n     d.create<asset_object>( [&op,next_asset_id,&dyn_asset,bit_asset_id]( asset_object& a ) {\n         a.issuer = op.issuer;\n         a.symbol = op.symbol;\n         a.precision = op.precision;\n         a.options = op.common_options;\n         if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 )\n            a.options.core_exchange_rate.quote.asset_id = next_asset_id;\n         else\n            a.options.core_exchange_rate.base.asset_id = next_asset_id;\n         a.dynamic_asset_data_id = dyn_asset.id;\n         if( op.bitasset_opts.valid() )\n            a.bitasset_data_id = bit_asset_id;\n      });\n   FC_ASSERT( new_asset.id == next_asset_id, \"Unexpected object database error, object id mismatch\" );\n\n   return new_asset.id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )\n{ try {\n   const database& d = db();\n\n   const asset_object& a = o.asset_to_issue.asset_id(d);\n   FC_ASSERT( o.issuer == a.issuer );\n   FC_ASSERT( !a.is_market_issued(), \"Cannot manually issue a market-issued asset.\" );\n\n   FC_ASSERT( !a.is_liquidity_pool_share_asset(), \"Cannot manually issue a liquidity pool share asset.\" );\n\n   FC_ASSERT( a.can_create_new_supply(), \"Can not create new supply\" );\n\n   to_account = &o.issue_to_account(d);\n   FC_ASSERT( is_authorized_asset( d, *to_account, a ) );\n\n   asset_dyn_data = &a.dynamic_asset_data_id(d);\n   FC_ASSERT( (asset_dyn_data->current_supply + o.asset_to_issue.amount) <= a.options.max_supply );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_issue_evaluator::do_apply( const asset_issue_operation& o )\n{ try {\n   db().adjust_balance( o.issue_to_account, o.asset_to_issue );\n\n   db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){\n        data.current_supply += o.asset_to_issue.amount;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& o )\n{ try {\n   const database& d = db();\n\n   const asset_object& a = o.amount_to_reserve.asset_id(d);\n   GRAPHENE_ASSERT(\n      !a.is_market_issued(),\n      asset_reserve_invalid_on_mia,\n      \"Cannot reserve ${sym} because it is a market-issued asset\",\n      (\"sym\", a.symbol)\n   );\n\n   from_account = fee_paying_account;\n   FC_ASSERT( is_authorized_asset( d, *from_account, a ) );\n\n   asset_dyn_data = &a.dynamic_asset_data_id(d);\n   if( !a.is_liquidity_pool_share_asset() )\n   {\n      FC_ASSERT( asset_dyn_data->current_supply >= o.amount_to_reserve.amount,\n                 \"Can not reserve an amount that is more than the current supply\" );\n   }\n   else\n   {\n      FC_ASSERT( asset_dyn_data->current_supply > o.amount_to_reserve.amount,\n                 \"The asset is a liquidity pool share asset thus can only reserve an amount \"\n                 \"that is less than the current supply\" );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_reserve_evaluator::do_apply( const asset_reserve_operation& o )\n{ try {\n   db().adjust_balance( o.payer, -o.amount_to_reserve );\n\n   db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){\n        data.current_supply -= o.amount_to_reserve.amount;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_fund_fee_pool_evaluator::do_evaluate(const asset_fund_fee_pool_operation& o)\n{ try {\n   database& d = db();\n\n   const asset_object& a = o.asset_id(d);\n\n   asset_dyn_data = &a.dynamic_asset_data_id(d);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_operation& o)\n{ try {\n   db().adjust_balance(o.from_account, -o.amount);\n\n   db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ) {\n      data.fee_pool += o.amount;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nstatic void validate_new_issuer( const database& d, const asset_object& a, account_id_type new_issuer )\n{ try {\n   FC_ASSERT(d.find_object(new_issuer));\n   if( a.is_market_issued() && new_issuer == GRAPHENE_COMMITTEE_ACCOUNT )\n   {\n      const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d);\n      if( backing.is_market_issued() )\n      {\n         const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d);\n         FC_ASSERT( backing_backing.get_id() == asset_id_type(),\n                    \"May not create a blockchain-controlled market asset which is not backed by CORE.\");\n      } else\n         FC_ASSERT( backing.get_id() == asset_id_type(),\n                    \"May not create a blockchain-controlled market asset which is not backed by CORE.\");\n   }\n} FC_CAPTURE_AND_RETHROW( (a)(new_issuer) ) }\n\nvoid_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)\n{ try {\n   const database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   // Hardfork Checks:\n   detail::check_asset_options_hf_1774(now, o.new_options);\n   detail::check_asset_options_hf_bsip_48_75(now, o.new_options);\n   detail::check_asset_options_hf_bsip81(now, o.new_options);\n   detail::check_asset_update_extensions_hf_bsip_48_75( now, o.extensions.value );\n\n   bool hf_bsip_48_75_passed = ( HARDFORK_BSIP_48_75_PASSED( now ) );\n\n   const asset_object& a = o.asset_to_update(d);\n   auto a_copy = a;\n   a_copy.options = o.new_options;\n   a_copy.validate();\n\n   if( o.new_issuer )\n   {\n      FC_ASSERT( now < HARDFORK_CORE_199_TIME,\n                 \"Since Hardfork #199, updating issuer requires the use of asset_update_issuer_operation.\");\n      validate_new_issuer( d, a, *o.new_issuer );\n   }\n\n   uint16_t enabled_issuer_permissions_mask = a.options.get_enabled_issuer_permissions_mask();\n   if( hf_bsip_48_75_passed && a.is_market_issued() )\n   {\n      bitasset_data = &a.bitasset_data(d);\n      if( bitasset_data->is_prediction_market )\n      {\n         // Note: if the global_settle permission was unset, it should be corrected\n         FC_ASSERT( a_copy.can_global_settle(),\n                    \"The global_settle permission should be enabled for prediction markets\" );\n         enabled_issuer_permissions_mask |= global_settle;\n      }\n   }\n\n   const auto& dyn_data = a.dynamic_asset_data_id(d);\n   if( dyn_data.current_supply != 0 )\n   {\n      // new issuer_permissions must be subset of old issuer permissions\n      FC_ASSERT(!(o.new_options.get_enabled_issuer_permissions_mask() & ~enabled_issuer_permissions_mask),\n                \"Cannot reinstate previously revoked issuer permissions on an asset if current supply is non-zero.\");\n      // precision can not be changed\n      FC_ASSERT( !o.extensions.value.new_precision.valid(),\n                 \"Cannot update precision if current supply is non-zero\" );\n\n      if( hf_bsip_48_75_passed ) // TODO review after hard fork, probably can assert unconditionally\n      {\n         FC_ASSERT( dyn_data.current_supply <= o.new_options.max_supply,\n                    \"Max supply should not be smaller than current supply\" );\n      }\n   }\n\n   // TODO move as many validations as possible to validate() if not triggered before hardfork\n   if( hf_bsip_48_75_passed )\n   {\n      o.new_options.validate_flags( a.is_market_issued() );\n   }\n\n   // changed flags must be subset of old issuer permissions\n   if( hf_bsip_48_75_passed )\n   {\n      // Note: if an invalid bit was set, it can be unset regardless of the permissions\n      uint16_t check_bits = ( a.is_market_issued() ? VALID_FLAGS_MASK : UIA_VALID_FLAGS_MASK );\n\n      FC_ASSERT( !((o.new_options.flags ^ a.options.flags) & check_bits & ~enabled_issuer_permissions_mask),\n                 \"Flag change is forbidden by issuer permissions\" );\n   }\n   else\n   {\n      FC_ASSERT( !((o.new_options.flags ^ a.options.flags) & ~a.options.issuer_permissions),\n                 \"Flag change is forbidden by issuer permissions\" );\n   }\n\n   asset_to_update = &a;\n   FC_ASSERT( o.issuer == a.issuer,\n              \"Incorrect issuer for asset! (${o.issuer} != ${a.issuer})\",\n              (\"o.issuer\", o.issuer)(\"a.issuer\", a.issuer) );\n\n   FC_ASSERT( a.can_update_max_supply() || a.options.max_supply == o.new_options.max_supply,\n              \"Can not update max supply\" );\n\n   if( o.extensions.value.new_precision.valid() )\n   {\n      FC_ASSERT( *o.extensions.value.new_precision != a.precision,\n                 \"Specified a new precision but it does not change\" );\n\n      if( a.is_market_issued() )\n      {\n         if( !bitasset_data )\n            bitasset_data = &asset_to_update->bitasset_data(d);\n         FC_ASSERT( !bitasset_data->is_prediction_market, \"Can not update precision of a prediction market\" );\n      }\n\n      // If any other asset is backed by this asset, this asset's precision can't be updated\n      const auto& idx = d.get_index_type<graphene::chain::asset_bitasset_data_index>()\n                         .indices().get<by_short_backing_asset>();\n      auto itr = idx.lower_bound( o.asset_to_update );\n      bool backing_another_asset = ( itr != idx.end() && itr->options.short_backing_asset == o.asset_to_update );\n      FC_ASSERT( !backing_another_asset,\n                 \"Asset ${a} is backed by this asset, can not update precision\",\n                 (\"a\",itr->asset_id) );\n   }\n\n   const auto& chain_parameters = d.get_global_properties().parameters;\n\n   FC_ASSERT( o.new_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );\n   for( auto id : o.new_options.whitelist_authorities )\n      d.get_object(id);\n   FC_ASSERT( o.new_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );\n   for( auto id : o.new_options.blacklist_authorities )\n      d.get_object(id);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((o)) }\n\nvoid_result asset_update_evaluator::do_apply(const asset_update_operation& o)\n{ try {\n   database& d = db();\n\n   // If we are now disabling force settlements, cancel all open force settlement orders\n   if( (o.new_options.flags & disable_force_settle) && asset_to_update->can_force_settle() )\n   {\n      const auto& idx = d.get_index_type<force_settlement_index>().indices().get<by_expiration>();\n      // Funky iteration code because we're removing objects as we go. We have to re-initialize itr every loop instead\n      // of simply incrementing it.\n      for( auto itr = idx.lower_bound(o.asset_to_update);\n           itr != idx.end() && itr->settlement_asset_id() == o.asset_to_update;\n           itr = idx.lower_bound(o.asset_to_update) )\n         d.cancel_settle_order(*itr);\n   }\n\n   // For market-issued assets, if core exchange rate changed, update flag in bitasset data\n   if( !o.extensions.value.skip_core_exchange_rate.valid() && asset_to_update->is_market_issued()\n          && asset_to_update->options.core_exchange_rate != o.new_options.core_exchange_rate )\n   {\n      const auto& bitasset = ( bitasset_data ? *bitasset_data : asset_to_update->bitasset_data(d) );\n      if( !bitasset.asset_cer_updated )\n      {\n         d.modify( bitasset, [](asset_bitasset_data_object& b)\n         {\n            b.asset_cer_updated = true;\n         });\n      }\n   }\n\n   d.modify(*asset_to_update, [&o](asset_object& a) {\n      if( o.new_issuer )\n         a.issuer = *o.new_issuer;\n      if( o.extensions.value.new_precision.valid() )\n         a.precision = *o.extensions.value.new_precision;\n      if( o.extensions.value.skip_core_exchange_rate.valid() )\n      {\n         const auto old_cer = a.options.core_exchange_rate;\n         a.options = o.new_options;\n         a.options.core_exchange_rate = old_cer;\n      }\n      else\n         a.options = o.new_options;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_update_issuer_evaluator::do_evaluate(const asset_update_issuer_operation& o)\n{ try {\n   database& d = db();\n\n   const asset_object& a = o.asset_to_update(d);\n\n   validate_new_issuer( d, a, o.new_issuer );\n\n   asset_to_update = &a;\n   FC_ASSERT( o.issuer == a.issuer,\n              \"Incorrect issuer for asset! (${o.issuer} != ${a.issuer})\",\n              (\"o.issuer\", o.issuer)(\"a.issuer\", a.issuer) );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((o)) }\n\nvoid_result asset_update_issuer_evaluator::do_apply(const asset_update_issuer_operation& o)\n{ try {\n   database& d = db();\n   d.modify(*asset_to_update, [&](asset_object& a) {\n      a.issuer = o.new_issuer;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n/****************\n * Loop through assets, looking for ones that are backed by the asset being changed. When found,\n * perform checks to verify validity\n *\n * @param d the database\n * @param op the bitasset update operation being performed\n * @param new_backing_asset\n * @param true if after hf 922/931 (if nothing triggers, this and the logic that depends on it\n *    should be removed).\n */\nvoid check_children_of_bitasset(const database& d, const asset_update_bitasset_operation& op,\n      const asset_object& new_backing_asset)\n{\n   // no need to do these checks if the new backing asset is CORE\n   if ( new_backing_asset.get_id() == asset_id_type() )\n      return;\n\n   // loop through all assets that have this asset as a backing asset\n   const auto& idx = d.get_index_type<graphene::chain::asset_bitasset_data_index>()\n         .indices()\n         .get<by_short_backing_asset>();\n   auto backed_range = idx.equal_range(op.asset_to_update);\n   std::for_each( backed_range.first, backed_range.second,\n         [&new_backing_asset, &d, &op](const asset_bitasset_data_object& bitasset_data)\n         {\n            const auto& child = bitasset_data.asset_id(d);\n            FC_ASSERT( child.get_id() != op.new_options.short_backing_asset,\n                  \"A BitAsset would be invalidated by changing this backing asset ('A' backed by 'B' backed by 'A').\" );\n\n            FC_ASSERT( child.issuer != GRAPHENE_COMMITTEE_ACCOUNT,\n                  \"A blockchain-controlled market asset would be invalidated by changing this backing asset.\" );\n\n            FC_ASSERT( !new_backing_asset.is_market_issued(),\n                  \"A non-blockchain controlled BitAsset would be invalidated by changing this backing asset.\");\n         } ); // end of lambda and std::for_each()\n} // check_children_of_bitasset\n\nvoid_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bitasset_operation& op)\n{ try {\n   const database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   // Hardfork Checks:\n   detail::check_bitasset_options_hf_bsip_48_75( now, op.new_options );\n   detail::check_bitasset_options_hf_bsip74( now, op.new_options ); // HF_REMOVABLE\n   detail::check_bitasset_options_hf_bsip77( now, op.new_options ); // HF_REMOVABLE\n   detail::check_bitasset_options_hf_bsip87( now, op.new_options ); // HF_REMOVABLE\n\n   const asset_object& asset_obj = op.asset_to_update(d);\n\n   FC_ASSERT( asset_obj.is_market_issued(), \"Cannot update BitAsset-specific settings on a non-BitAsset.\" );\n\n   FC_ASSERT( op.issuer == asset_obj.issuer, \"Only asset issuer can update bitasset_data of the asset.\" );\n\n   const asset_bitasset_data_object& current_bitasset_data = asset_obj.bitasset_data(d);\n\n   FC_ASSERT( !current_bitasset_data.has_settlement(),\n              \"Cannot update a bitasset after a global settlement has executed\" );\n\n   // TODO simplify code below when made sure operator==(optional,optional) works\n   if( !asset_obj.can_owner_update_mcr() )\n   {\n      // check if MCR will change\n      const auto& old_mcr = current_bitasset_data.options.extensions.value.maintenance_collateral_ratio;\n      const auto& new_mcr = op.new_options.extensions.value.maintenance_collateral_ratio;\n      bool mcr_changed = ( ( old_mcr.valid() != new_mcr.valid() )\n                           || ( old_mcr.valid() && *old_mcr != *new_mcr ) );\n      FC_ASSERT( !mcr_changed, \"No permission to update MCR\" );\n   }\n   if( !asset_obj.can_owner_update_icr() )\n   {\n      // check if ICR will change\n      const auto& old_icr = current_bitasset_data.options.extensions.value.initial_collateral_ratio;\n      const auto& new_icr = op.new_options.extensions.value.initial_collateral_ratio;\n      bool icr_changed = ( ( old_icr.valid() != new_icr.valid() )\n                           || ( old_icr.valid() && *old_icr != *new_icr ) );\n      FC_ASSERT( !icr_changed, \"No permission to update ICR\" );\n   }\n   if( !asset_obj.can_owner_update_mssr() )\n   {\n      // check if MSSR will change\n      const auto& old_mssr = current_bitasset_data.options.extensions.value.maximum_short_squeeze_ratio;\n      const auto& new_mssr = op.new_options.extensions.value.maximum_short_squeeze_ratio;\n      bool mssr_changed = ( ( old_mssr.valid() != new_mssr.valid() )\n                           || ( old_mssr.valid() && *old_mssr != *new_mssr ) );\n      FC_ASSERT( !mssr_changed, \"No permission to update MSSR\" );\n   }\n\n   // hf 922_931 is a consensus/logic change. This hf cannot be removed.\n   bool after_hf_core_922_931 = ( d.get_dynamic_global_properties().next_maintenance_time\n                                  > HARDFORK_CORE_922_931_TIME );\n\n   // Are we changing the backing asset?\n   if( op.new_options.short_backing_asset != current_bitasset_data.options.short_backing_asset )\n   {\n      const asset_dynamic_data_object& dyn = asset_obj.dynamic_asset_data_id(d);\n      FC_ASSERT( dyn.current_supply == 0,\n                 \"Cannot update a bitasset if there is already a current supply.\" );\n\n      FC_ASSERT( dyn.accumulated_collateral_fees == 0,\n                 \"Must claim collateral-denominated fees before changing backing asset.\" );\n\n      const asset_object& new_backing_asset = op.new_options.short_backing_asset(d); // check if the asset exists\n\n      if( after_hf_core_922_931 )\n      {\n         FC_ASSERT( op.new_options.short_backing_asset != asset_obj.get_id(),\n                    \"Cannot update an asset to be backed by itself.\" );\n\n         if( current_bitasset_data.is_prediction_market )\n         {\n            FC_ASSERT( asset_obj.precision == new_backing_asset.precision,\n                       \"The precision of the asset and backing asset must be equal.\" );\n         }\n\n         if( asset_obj.issuer == GRAPHENE_COMMITTEE_ACCOUNT )\n         {\n            if( new_backing_asset.is_market_issued() )\n            {\n               FC_ASSERT( new_backing_asset.bitasset_data(d).options.short_backing_asset == asset_id_type(),\n                          \"May not modify a blockchain-controlled market asset to be backed by an asset which is not \"\n                          \"backed by CORE.\" );\n\n               check_children_of_bitasset( d, op, new_backing_asset );\n            }\n            else\n            {\n               FC_ASSERT( new_backing_asset.get_id() == asset_id_type(),\n                          \"May not modify a blockchain-controlled market asset to be backed by an asset which is not \"\n                          \"market issued asset nor CORE.\" );\n            }\n         }\n         else\n         {\n            // not a committee issued asset\n\n            // If we're changing to a backing_asset that is not CORE, we need to look at any\n            // asset ( \"CHILD\" ) that has this one as a backing asset. If CHILD is committee-owned,\n            // the change is not allowed. If CHILD is user-owned, then this asset's backing\n            // asset must be either CORE or a UIA.\n            if ( new_backing_asset.get_id() != asset_id_type() ) // not backed by CORE\n            {\n               check_children_of_bitasset( d, op, new_backing_asset );\n            }\n\n         }\n\n         // Check if the new backing asset is itself backed by something. It must be CORE or a UIA\n         if ( new_backing_asset.is_market_issued() )\n         {\n            asset_id_type backing_backing_asset_id = new_backing_asset.bitasset_data(d).options.short_backing_asset;\n            FC_ASSERT( (backing_backing_asset_id == asset_id_type() || !backing_backing_asset_id(d).is_market_issued()),\n                  \"A BitAsset cannot be backed by a BitAsset that itself is backed by a BitAsset.\");\n         }\n      }\n   }\n\n   const auto& chain_parameters = d.get_global_properties().parameters;\n   if( after_hf_core_922_931 )\n   {\n      FC_ASSERT( op.new_options.feed_lifetime_sec > chain_parameters.block_interval,\n            \"Feed lifetime must exceed block interval.\" );\n      FC_ASSERT( op.new_options.force_settlement_delay_sec > chain_parameters.block_interval,\n            \"Force settlement delay must exceed block interval.\" );\n   }\n\n   bitasset_to_update = &current_bitasset_data;\n   asset_to_update = &asset_obj;\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\n/*******\n * @brief Apply requested changes to bitasset options\n *\n * This applies the requested changes to the bitasset object. It also cleans up the\n * releated feeds, and checks conditions that might necessitate a call to check_call_orders.\n * Called from asset_update_bitasset_evaluator::do_apply().\n *\n * @param op the requested operation\n * @param db the database\n * @param bdo the actual database object\n * @param asset_to_update the asset_object related to this bitasset_data_object\n *\n * @returns true if we should check call orders, such as if if the feed price is changed, or some\n *    cases after hf core-868-890, or if the margin_call_fee_ratio has changed, which affects the\n *    matching price of margin call orders.\n */\nstatic bool update_bitasset_object_options(\n      const asset_update_bitasset_operation& op, database& db,\n      asset_bitasset_data_object& bdo, const asset_object& asset_to_update )\n{\n   const fc::time_point_sec next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;\n   bool after_hf_core_868_890 = ( next_maint_time > HARDFORK_CORE_868_890_TIME );\n\n   // If the minimum number of feeds to calculate a median has changed, we need to recalculate the median\n   bool should_update_feeds = false;\n   if( op.new_options.minimum_feeds != bdo.options.minimum_feeds )\n      should_update_feeds = true;\n\n   // after hardfork core-868-890, we also should call update_median_feeds if the feed_lifetime_sec changed\n   if( after_hf_core_868_890\n         && op.new_options.feed_lifetime_sec != bdo.options.feed_lifetime_sec )\n   {\n      should_update_feeds = true;\n   }\n\n   // feeds must be reset if the backing asset is changed after hardfork core-868-890\n   bool backing_asset_changed = false;\n   bool is_witness_or_committee_fed = false;\n   if( after_hf_core_868_890\n         && op.new_options.short_backing_asset != bdo.options.short_backing_asset )\n   {\n      backing_asset_changed = true;\n      should_update_feeds = true;\n      if( asset_to_update.options.flags & ( witness_fed_asset | committee_fed_asset ) )\n         is_witness_or_committee_fed = true;\n   }\n\n   // TODO simplify code below when made sure operator==(optional,optional) works\n   // check if ICR will change\n   if( !should_update_feeds )\n   {\n      const auto& old_icr = bdo.options.extensions.value.initial_collateral_ratio;\n      const auto& new_icr = op.new_options.extensions.value.initial_collateral_ratio;\n      bool icr_changed = ( ( old_icr.valid() != new_icr.valid() )\n                           || ( old_icr.valid() && *old_icr != *new_icr ) );\n      should_update_feeds = icr_changed;\n   }\n   // check if MCR will change\n   if( !should_update_feeds )\n   {\n      const auto& old_mcr = bdo.options.extensions.value.maintenance_collateral_ratio;\n      const auto& new_mcr = op.new_options.extensions.value.maintenance_collateral_ratio;\n      bool mcr_changed = ( ( old_mcr.valid() != new_mcr.valid() )\n                           || ( old_mcr.valid() && *old_mcr != *new_mcr ) );\n      should_update_feeds = mcr_changed;\n   }\n   // check if MSSR will change\n   if( !should_update_feeds )\n   {\n      const auto& old_mssr = bdo.options.extensions.value.maximum_short_squeeze_ratio;\n      const auto& new_mssr = op.new_options.extensions.value.maximum_short_squeeze_ratio;\n      bool mssr_changed = ( ( old_mssr.valid() != new_mssr.valid() )\n                           || ( old_mssr.valid() && *old_mssr != *new_mssr ) );\n      should_update_feeds = mssr_changed;\n   }\n\n   // check if MCFR will change\n   const auto& old_mcfr = bdo.options.extensions.value.margin_call_fee_ratio;\n   const auto& new_mcfr = op.new_options.extensions.value.margin_call_fee_ratio;\n   const bool mcfr_changed = ( ( old_mcfr.valid() != new_mcfr.valid() )\n                               || ( old_mcfr.valid() && *old_mcfr != *new_mcfr ) );\n\n   // Apply changes to bitasset options\n   bdo.options = op.new_options;\n\n   // are we modifying the underlying? If so, reset the feeds\n   if( backing_asset_changed )\n   {\n      if( is_witness_or_committee_fed )\n      {\n         bdo.feeds.clear();\n      }\n      else\n      {\n         // for non-witness-feeding and non-committee-feeding assets, modify all feeds\n         // published by producers to nothing, since we can't simply remove them. For more information:\n         // https://github.com/bitshares/bitshares-core/pull/832#issuecomment-384112633\n         for( auto& current_feed : bdo.feeds )\n         {\n            current_feed.second.second.settlement_price = price();\n         }\n      }\n   }\n\n   bool feed_actually_changed = false;\n   if( should_update_feeds )\n   {\n      const auto old_feed = bdo.current_feed;\n      bdo.update_median_feeds( db.head_block_time(), next_maint_time );\n\n      // We need to call check_call_orders if the settlement price changes after hardfork core-868-890\n      feed_actually_changed = ( after_hf_core_868_890 && !old_feed.margin_call_params_equal( bdo.current_feed ) );\n   }\n\n   // Conditions under which a call to check_call_orders is needed in response to the updates applied here:\n   const bool retval = feed_actually_changed || mcfr_changed;\n\n   return retval;\n}\n\nvoid_result asset_update_bitasset_evaluator::do_apply(const asset_update_bitasset_operation& op)\n{\n   try\n   {\n      auto& db_conn = db();\n      const auto& asset_being_updated = (*asset_to_update);\n      bool to_check_call_orders = false;\n\n      db_conn.modify( *bitasset_to_update,\n                      [&op, &asset_being_updated, &to_check_call_orders, &db_conn]( asset_bitasset_data_object& bdo )\n      {\n         to_check_call_orders = update_bitasset_object_options( op, db_conn, bdo, asset_being_updated );\n      });\n\n      if( to_check_call_orders )\n         // Process margin calls, allow black swan, not for a new limit order\n         db_conn.check_call_orders( asset_being_updated, true, false, bitasset_to_update );\n\n      return void_result();\n\n   } FC_CAPTURE_AND_RETHROW( (op) )\n}\n\nvoid_result asset_update_feed_producers_evaluator::do_evaluate(const asset_update_feed_producers_evaluator::operation_type& o)\n{ try {\n   database& d = db();\n\n   FC_ASSERT( o.new_feed_producers.size() <= d.get_global_properties().parameters.maximum_asset_feed_publishers,\n              \"Cannot specify more feed producers than maximum allowed\" );\n\n   const asset_object& a = o.asset_to_update(d);\n\n   FC_ASSERT(a.is_market_issued(), \"Cannot update feed producers on a non-BitAsset.\");\n   FC_ASSERT(!(a.options.flags & committee_fed_asset), \"Cannot set feed producers on a committee-fed asset.\");\n   FC_ASSERT(!(a.options.flags & witness_fed_asset), \"Cannot set feed producers on a witness-fed asset.\");\n\n   FC_ASSERT( a.issuer == o.issuer, \"Only asset issuer can update feed producers of an asset\" );\n\n   asset_to_update = &a;\n\n   // Make sure all producers exist. Check these after asset because account lookup is more expensive\n   for( auto id : o.new_feed_producers )\n      d.get_object(id);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_update_feed_producers_evaluator::do_apply(const asset_update_feed_producers_evaluator::operation_type& o)\n{ try {\n   database& d = db();\n   const auto head_time = d.head_block_time();\n   const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;\n   const asset_bitasset_data_object& bitasset_to_update = asset_to_update->bitasset_data(d);\n   d.modify( bitasset_to_update, [&o,head_time,next_maint_time](asset_bitasset_data_object& a) {\n      //This is tricky because I have a set of publishers coming in, but a map of publisher to feed is stored.\n      //I need to update the map such that the keys match the new publishers, but not munge the old price feeds from\n      //publishers who are being kept.\n\n      // TODO possible performance optimization:\n      //      Since both the map and the set are ordered by account already, we can iterate through them only once\n      //      and avoid lookups while iterating by maintaining two iterators at same time.\n      //      However, this operation is not used much, and both the set and the map are small,\n      //      so likely we won't gain much with the optimization.\n\n      //First, remove any old publishers who are no longer publishers\n      for( auto itr = a.feeds.begin(); itr != a.feeds.end(); )\n      {\n         if( !o.new_feed_producers.count(itr->first) )\n            itr = a.feeds.erase(itr);\n         else\n            ++itr;\n      }\n      //Now, add any new publishers\n      for( const account_id_type acc : o.new_feed_producers )\n      {\n         a.feeds[acc];\n      }\n      a.update_median_feeds( head_time, next_maint_time );\n   });\n   // Process margin calls, allow black swan, not for a new limit order\n   d.check_call_orders( *asset_to_update, true, false, &bitasset_to_update );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_global_settle_evaluator::do_evaluate(const asset_global_settle_evaluator::operation_type& op)\n{ try {\n   const database& d = db();\n   asset_to_settle = &op.asset_to_settle(d);\n   FC_ASSERT( asset_to_settle->is_market_issued(), \"Can only globally settle market-issued assets\" );\n   FC_ASSERT( asset_to_settle->can_global_settle(), \"The global_settle permission of this asset is disabled\" );\n   FC_ASSERT( asset_to_settle->issuer == op.issuer, \"Only asset issuer can globally settle an asset\" );\n   FC_ASSERT( asset_to_settle->dynamic_data(d).current_supply > 0, \"Can not globally settle an asset with zero supply\" );\n\n   const asset_bitasset_data_object& _bitasset_data  = asset_to_settle->bitasset_data(d);\n   // if there is a settlement for this asset, then no further global settle may be taken\n   FC_ASSERT( !_bitasset_data.has_settlement(), \"This asset has settlement, cannot global settle again\" );\n\n   const auto& idx = d.get_index_type<call_order_index>().indices().get<by_collateral>();\n   FC_ASSERT( !idx.empty(), \"Internal error: no debt position found\" );\n   auto itr = idx.lower_bound( price::min( _bitasset_data.options.short_backing_asset, op.asset_to_settle ) );\n   FC_ASSERT( itr != idx.end() && itr->debt_type() == op.asset_to_settle, \"Internal error: no debt position found\" );\n   const call_order_object& least_collateralized_short = *itr;\n   FC_ASSERT(least_collateralized_short.get_debt() * op.settle_price <= least_collateralized_short.get_collateral(),\n             \"Cannot force settle at supplied price: least collateralized short lacks sufficient collateral to settle.\");\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result asset_global_settle_evaluator::do_apply(const asset_global_settle_evaluator::operation_type& op)\n{ try {\n   database& d = db();\n   d.globally_settle_asset( *asset_to_settle, op.settle_price );\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result asset_settle_evaluator::do_evaluate(const asset_settle_evaluator::operation_type& op)\n{ try {\n   const database& d = db();\n   asset_to_settle = &op.amount.asset_id(d);\n   FC_ASSERT(asset_to_settle->is_market_issued());\n   const auto& bitasset = asset_to_settle->bitasset_data(d);\n   FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() );\n   if( bitasset.is_prediction_market )\n      FC_ASSERT( bitasset.has_settlement(), \"global settlement must occur before force settling a prediction market\"  );\n   else if( bitasset.current_feed.settlement_price.is_null()\n            && ( d.head_block_time() <= HARDFORK_CORE_216_TIME // TODO check whether the HF check can be removed\n                 || !bitasset.has_settlement() ) )\n      FC_THROW_EXCEPTION(insufficient_feeds, \"Cannot force settle with no price feed.\");\n   FC_ASSERT( d.get_balance( op.account, op.amount.asset_id ) >= op.amount, \"Insufficient balance\" );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\noperation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op)\n{ try {\n   database& d = db();\n\n   const auto& bitasset = asset_to_settle->bitasset_data(d);\n   if( bitasset.has_settlement() )\n   {\n      const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d);\n\n      auto settled_amount = op.amount * bitasset.settlement_price; // round down, in favor of global settlement fund\n      if( op.amount.amount == mia_dyn.current_supply )\n         settled_amount.amount = bitasset.settlement_fund; // avoid rounding problems\n      else\n         FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund ); // should be strictly < except for PM with zero outcome\n\n      if( settled_amount.amount == 0 && !bitasset.is_prediction_market )\n      {\n         if( d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_184_TIME )\n            FC_THROW( \"Settle amount is too small to receive anything due to rounding\" );\n         // else do nothing. Before the hf, something for nothing issue (#184, variant F) could occur\n      }\n\n      asset pays = op.amount;\n      if( op.amount.amount != mia_dyn.current_supply\n            && settled_amount.amount != 0\n            && d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_342_TIME )\n      {\n         pays = settled_amount.multiply_and_round_up( bitasset.settlement_price );\n      }\n\n      d.adjust_balance( op.account, -pays );\n\n      if( settled_amount.amount > 0 )\n      {\n         d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){\n            obj.settlement_fund -= settled_amount.amount;\n         });\n\n         // The account who settles pays market fees to the issuer of the collateral asset after HF core-1780\n         //\n         // TODO Check whether the HF check can be removed after the HF.\n         //      Note: even if logically it can be removed, perhaps the removal will lead to a small\n         //            performance loss. Needs testing.\n         if( d.head_block_time() >= HARDFORK_CORE_1780_TIME )\n         {\n            auto issuer_fees = d.pay_market_fees( fee_paying_account, settled_amount.asset_id(d),\n                  settled_amount, false );\n            settled_amount -= issuer_fees;\n         }\n\n         if( settled_amount.amount > 0 )\n            d.adjust_balance( op.account, settled_amount );\n      }\n\n      d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){\n         obj.current_supply -= pays.amount;\n      });\n\n      return settled_amount;\n   }\n   else\n   {\n      d.adjust_balance( op.account, -op.amount );\n      return d.create<force_settlement_object>([&](force_settlement_object& s) {\n         s.owner = op.account;\n         s.balance = op.amount;\n         s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec;\n      }).id;\n   }\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_operation& o)\n{ try {\n   const database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   // TODO remove check after hard fork\n   detail::check_asset_publish_feed_extensions_hf_bsip77( now, o.extensions.value );\n\n   const asset_object& base = o.asset_id(d);\n   //Verify that this feed is for a market-issued asset and that asset is backed by the base\n   FC_ASSERT( base.is_market_issued(), \"Can only publish price feeds for market-issued assets\" );\n\n   const asset_bitasset_data_object& bitasset = base.bitasset_data(d);\n   if( bitasset.is_prediction_market || now <= HARDFORK_CORE_216_TIME )\n   {\n      FC_ASSERT( !bitasset.has_settlement(), \"No further feeds may be published after a settlement event\" );\n   }\n\n   // the settlement price must be quoted in terms of the backing asset\n   FC_ASSERT( o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset,\n              \"Quote asset type in settlement price should be same as backing asset of this asset\" );\n\n   if( now > HARDFORK_480_TIME )\n   {\n      if( !o.feed.core_exchange_rate.is_null() )\n      {\n         FC_ASSERT( o.feed.core_exchange_rate.quote.asset_id == asset_id_type(),\n                    \"Quote asset in core exchange rate should be CORE asset\" );\n      }\n   }\n   else\n   {\n      if( (!o.feed.settlement_price.is_null()) && (!o.feed.core_exchange_rate.is_null()) )\n      {\n         // Old buggy code, but we have to live with it\n         FC_ASSERT( o.feed.settlement_price.quote.asset_id == o.feed.core_exchange_rate.quote.asset_id, \"Bad feed\" );\n      }\n   }\n\n   //Verify that the publisher is authoritative to publish a feed\n   if( base.options.flags & witness_fed_asset )\n   {\n      FC_ASSERT( d.get(GRAPHENE_WITNESS_ACCOUNT).active.account_auths.count(o.publisher),\n                 \"Only active witnesses are allowed to publish price feeds for this asset\" );\n   }\n   else if( base.options.flags & committee_fed_asset )\n   {\n      FC_ASSERT( d.get(GRAPHENE_COMMITTEE_ACCOUNT).active.account_auths.count(o.publisher),\n                 \"Only active committee members are allowed to publish price feeds for this asset\" );\n   }\n   else\n   {\n      FC_ASSERT( bitasset.feeds.count(o.publisher),\n                 \"The account is not in the set of allowed price feed producers of this asset\" );\n   }\n\n   asset_ptr = &base;\n   bitasset_ptr = &bitasset;\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((o)) }\n\nvoid_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_operation& o)\n{ try {\n\n   database& d = db();\n   const auto head_time = d.head_block_time();\n   const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;\n\n   const asset_object& base = *asset_ptr;\n   const asset_bitasset_data_object& bad = *bitasset_ptr;\n\n   auto old_feed = bad.current_feed;\n   // Store medians for this asset\n   d.modify( bad , [&o,head_time,next_maint_time](asset_bitasset_data_object& a) {\n      a.feeds[o.publisher] = make_pair( head_time, price_feed_with_icr( o.feed,\n                                                      o.extensions.value.initial_collateral_ratio ) );\n      a.update_median_feeds( head_time, next_maint_time );\n   });\n\n   if( !old_feed.margin_call_params_equal(bad.current_feed) )\n   {\n      // Check whether need to revive the asset and proceed if need\n      if( bad.has_settlement() // has globally settled, implies head_block_time > HARDFORK_CORE_216_TIME\n          && !bad.current_feed.settlement_price.is_null() ) // has a valid feed\n      {\n         bool should_revive = false;\n         const auto& mia_dyn = base.dynamic_asset_data_id(d);\n         if( mia_dyn.current_supply == 0 ) // if current supply is zero, revive the asset\n            should_revive = true;\n         else // if current supply is not zero, when collateral ratio of settlement fund is greater than MCR, revive the asset\n         {\n            if( next_maint_time <= HARDFORK_CORE_1270_TIME )\n            {\n               // before core-1270 hard fork, calculate call_price and compare to median feed\n               if( ~price::call_price( asset(mia_dyn.current_supply, o.asset_id),\n                                       asset(bad.settlement_fund, bad.options.short_backing_asset),\n                                       bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price )\n                  should_revive = true;\n            }\n            else\n            {\n               // after core-1270 hard fork, calculate collateralization and compare to maintenance_collateralization\n               if( price( asset( bad.settlement_fund, bad.options.short_backing_asset ),\n                          asset( mia_dyn.current_supply, o.asset_id ) ) > bad.current_maintenance_collateralization )\n                  should_revive = true;\n            }\n         }\n         if( should_revive )\n            d.revive_bitasset(base);\n      }\n      // Process margin calls, allow black swan, not for a new limit order\n      d.check_call_orders( base, true, false, bitasset_ptr );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((o)) }\n\n\n/***\n * @brief evaluator for asset_claim_fees operation\n *\n * Checks that we are able to claim fees denominated in asset Y (the amount_to_claim asset),\n * from some container asset X which is presumed to have accumulated the fees we wish to claim.\n * The container asset is either explicitly named in the extensions, or else assumed as the same\n * asset as the amount_to_claim asset. Evaluation fails if either (a) operation issuer is not\n * the same as the container_asset issuer, or (b) container_asset has no fee bucket for\n * amount_to_claim asset, or (c) accumulated fees are insufficient to cover amount claimed.\n */\nvoid_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o )\n{ try {\n   const database& d = db();\n\n   detail::check_asset_claim_fees_hardfork_87_74_collatfee(d.head_block_time(), o); // HF_REMOVABLE\n\n   container_asset = o.extensions.value.claim_from_asset_id.valid() ?\n      &(*o.extensions.value.claim_from_asset_id)(d) : &o.amount_to_claim.asset_id(d);\n\n   FC_ASSERT( container_asset->issuer == o.issuer, \"Asset fees may only be claimed by the issuer\" );\n   FC_ASSERT( container_asset->can_accumulate_fee(d,o.amount_to_claim),\n              \"Asset ${a} (${id}) is not backed by asset (${fid}) and does not hold it as fees.\",\n              (\"a\",container_asset->symbol)(\"id\",container_asset->id)(\"fid\",o.amount_to_claim.asset_id) );\n\n   container_ddo = &container_asset->dynamic_asset_data_id(d);\n\n   if (container_asset->get_id() == o.amount_to_claim.asset_id) {\n      FC_ASSERT( o.amount_to_claim.amount <= container_ddo->accumulated_fees,\n                 \"Attempt to claim more fees than have accumulated within asset ${a} (${id}). \"\n                 \"Asset DDO: ${ddo}. Fee claim: ${claim}.\", (\"a\",container_asset->symbol)\n                 (\"id\",container_asset->id)(\"ddo\",*container_ddo)(\"claim\",o.amount_to_claim) );\n   } else {\n      FC_ASSERT( o.amount_to_claim.amount <= container_ddo->accumulated_collateral_fees,\n                 \"Attempt to claim more backing-asset fees than have accumulated within asset ${a} (${id}) \"\n                 \"backed by (${fid}). Asset DDO: ${ddo}. Fee claim: ${claim}.\", (\"a\",container_asset->symbol)\n                 (\"id\",container_asset->id)(\"fid\",o.amount_to_claim.asset_id)(\"ddo\",*container_ddo)\n                 (\"claim\",o.amount_to_claim) );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\n/***\n * @brief apply asset_claim_fees operation\n */\nvoid_result asset_claim_fees_evaluator::do_apply( const asset_claim_fees_operation& o )\n{ try {\n   database& d = db();\n\n   if ( container_asset->get_id() == o.amount_to_claim.asset_id ) {\n      d.modify( *container_ddo, [&o]( asset_dynamic_data_object& _addo  ) {\n         _addo.accumulated_fees -= o.amount_to_claim.amount;\n      });\n   } else {\n      d.modify( *container_ddo, [&o]( asset_dynamic_data_object& _addo  ) {\n         _addo.accumulated_collateral_fees -= o.amount_to_claim.amount;\n      });\n   }\n\n   d.adjust_balance( o.issuer, o.amount_to_claim );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\nvoid_result asset_claim_pool_evaluator::do_evaluate( const asset_claim_pool_operation& o )\n{ try {\n    FC_ASSERT( o.asset_id(db()).issuer == o.issuer, \"Asset fee pool may only be claimed by the issuer\" );\n\n    return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result asset_claim_pool_evaluator::do_apply( const asset_claim_pool_operation& o )\n{ try {\n    database& d = db();\n\n    const asset_object& a = o.asset_id(d);\n    const asset_dynamic_data_object& addo = a.dynamic_asset_data_id(d);\n    FC_ASSERT( o.amount_to_claim.amount <= addo.fee_pool, \"Attempt to claim more fees than is available\", (\"addo\",addo) );\n\n    d.modify( addo, [&o]( asset_dynamic_data_object& _addo  ) {\n        _addo.fee_pool -= o.amount_to_claim.amount;\n    });\n\n    d.adjust_balance( o.issuer, o.amount_to_claim );\n\n    return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/asset_object.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/uint128.hpp>\n\nusing namespace graphene::chain;\n\nshare_type asset_bitasset_data_object::max_force_settlement_volume(share_type current_supply) const\n{\n   if( options.maximum_force_settlement_volume == 0 )\n      return 0;\n   if( options.maximum_force_settlement_volume == GRAPHENE_100_PERCENT )\n      return current_supply + force_settled_volume;\n\n   fc::uint128_t volume = current_supply.value;\n   volume += force_settled_volume.value;\n   volume *= options.maximum_force_settlement_volume;\n   volume /= GRAPHENE_100_PERCENT;\n   return static_cast<uint64_t>(volume);\n}\n\nvoid graphene::chain::asset_bitasset_data_object::update_median_feeds( time_point_sec current_time,\n                                                                       time_point_sec next_maintenance_time )\n{\n   bool after_core_hardfork_1270 = ( next_maintenance_time > HARDFORK_CORE_1270_TIME ); // call price caching issue\n   current_feed_publication_time = current_time;\n   vector<std::reference_wrapper<const price_feed_with_icr>> current_feeds;\n   // find feeds that were alive at current_time\n   for( const pair<account_id_type, pair<time_point_sec,price_feed_with_icr>>& f : feeds )\n   {\n      if( (current_time - f.second.first).to_seconds() < options.feed_lifetime_sec &&\n          f.second.first != time_point_sec() )\n      {\n         current_feeds.emplace_back(f.second.second);\n         current_feed_publication_time = std::min(current_feed_publication_time, f.second.first);\n      }\n   }\n\n   // If there are no valid feeds, or the number available is less than the minimum to calculate a median...\n   if( current_feeds.size() < options.minimum_feeds )\n   {\n      //... don't calculate a median, and set a null feed\n      feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance\n      current_feed_publication_time = current_time;\n      current_feed = price_feed_with_icr();\n      if( after_core_hardfork_1270 )\n      {\n         // update data derived from MCR, ICR and etc\n         refresh_cache();\n      }\n      return;\n   }\n   if( current_feeds.size() == 1 )\n   {\n      if( current_feed.core_exchange_rate != current_feeds.front().get().core_exchange_rate )\n         feed_cer_updated = true;\n      current_feed = current_feeds.front();\n      // Note: perhaps can defer updating current_maintenance_collateralization for better performance\n      if( after_core_hardfork_1270 )\n      {\n         const auto& exts = options.extensions.value;\n         if( exts.maintenance_collateral_ratio.valid() )\n            current_feed.maintenance_collateral_ratio = *exts.maintenance_collateral_ratio;\n         if( exts.maximum_short_squeeze_ratio.valid() )\n            current_feed.maximum_short_squeeze_ratio = *exts.maximum_short_squeeze_ratio;\n         if( exts.initial_collateral_ratio.valid() )\n            current_feed.initial_collateral_ratio = *exts.initial_collateral_ratio;\n         // update data derived from MCR, ICR and etc\n         refresh_cache();\n      }\n      return;\n   }\n\n   // *** Begin Median Calculations ***\n   price_feed_with_icr median_feed;\n   const auto median_itr = current_feeds.begin() + current_feeds.size() / 2;\n#define CALCULATE_MEDIAN_VALUE(r, data, field_name) \\\n   std::nth_element( current_feeds.begin(), median_itr, current_feeds.end(), \\\n                     [](const price_feed_with_icr& a, const price_feed_with_icr& b) { \\\n      return a.field_name < b.field_name; \\\n   }); \\\n   median_feed.field_name = median_itr->get().field_name;\n\n#define CHECK_AND_CALCULATE_MEDIAN_VALUE(r, data, field_name) \\\n   if( options.extensions.value.field_name.valid() ) { \\\n      median_feed.field_name = *options.extensions.value.field_name; \\\n   } else { \\\n      CALCULATE_MEDIAN_VALUE(r, data, field_name); \\\n   }\n\n   BOOST_PP_SEQ_FOR_EACH( CALCULATE_MEDIAN_VALUE, ~, (settlement_price)(core_exchange_rate) )\n   BOOST_PP_SEQ_FOR_EACH( CHECK_AND_CALCULATE_MEDIAN_VALUE, ~,\n                          (maintenance_collateral_ratio)(maximum_short_squeeze_ratio)(initial_collateral_ratio) )\n#undef CHECK_AND_CALCULATE_MEDIAN_VALUE\n#undef CALCULATE_MEDIAN_VALUE\n   // *** End Median Calculations ***\n\n   if( current_feed.core_exchange_rate != median_feed.core_exchange_rate )\n      feed_cer_updated = true;\n   current_feed = median_feed;\n   // Note: perhaps can defer updating current_maintenance_collateralization for better performance\n   if( after_core_hardfork_1270 )\n   {\n      // update data derived from MCR, ICR and etc\n      refresh_cache();\n   }\n}\n\nvoid asset_bitasset_data_object::refresh_cache()\n{\n   current_maintenance_collateralization = current_feed.maintenance_collateralization();\n   if( current_feed.initial_collateral_ratio > current_feed.maintenance_collateral_ratio ) // if ICR is above MCR\n      current_initial_collateralization = current_feed.calculate_initial_collateralization();\n   else // if ICR is not above MCR\n      current_initial_collateralization = current_maintenance_collateralization;\n}\n\nprice price_feed_with_icr::calculate_initial_collateralization()const\n{\n   if( settlement_price.is_null() )\n      return price();\n   return ~settlement_price * ratio_type( initial_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );\n}\n\nasset asset_object::amount_from_string(string amount_string) const\n{ try {\n   bool negative_found = false;\n   bool decimal_found = false;\n   for( const char c : amount_string )\n   {\n      if( isdigit( c ) )\n         continue;\n\n      if( c == '-' && !negative_found )\n      {\n         negative_found = true;\n         continue;\n      }\n\n      if( c == '.' && !decimal_found )\n      {\n         decimal_found = true;\n         continue;\n      }\n\n      FC_THROW( (amount_string) );\n   }\n\n   share_type satoshis = 0;\n\n   share_type scaled_precision = asset::scaled_precision( precision );\n\n   const auto decimal_pos = amount_string.find( '.' );\n   const string lhs = amount_string.substr( negative_found, decimal_pos );\n   if( !lhs.empty() )\n      satoshis += fc::safe<int64_t>(std::stoll(lhs)) *= scaled_precision;\n\n   if( decimal_found )\n   {\n      const size_t max_rhs_size = std::to_string( scaled_precision.value ).substr( 1 ).size();\n\n      string rhs = amount_string.substr( decimal_pos + 1 );\n      FC_ASSERT( rhs.size() <= max_rhs_size );\n\n      while( rhs.size() < max_rhs_size )\n         rhs += '0';\n\n      if( !rhs.empty() )\n         satoshis += std::stoll( rhs );\n   }\n\n   FC_ASSERT( satoshis <= GRAPHENE_MAX_SHARE_SUPPLY );\n\n   if( negative_found )\n      satoshis *= -1;\n\n   return amount(satoshis);\n} FC_CAPTURE_AND_RETHROW( (amount_string) ) }\n\nstring asset_object::amount_to_string(share_type amount) const\n{\n   share_type scaled_precision = asset::scaled_precision( precision );\n\n   string result = fc::to_string(amount.value / scaled_precision.value);\n   auto decimals = abs( amount.value % scaled_precision.value );\n   if( decimals )\n   {\n      if( amount < 0 && result == \"0\" )\n         result = \"-0\";\n      result += \".\" + fc::to_string(scaled_precision.value + decimals).erase(0,1);\n   }\n   return result;\n}\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_dynamic_data_object, (graphene::db::object),\n                    (current_supply)(confidential_supply)(accumulated_fees)(accumulated_collateral_fees)(fee_pool) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_bitasset_data_object, (graphene::db::object),\n                    (asset_id)\n                    (feeds)\n                    (current_feed)\n                    (current_feed_publication_time)\n                    (current_maintenance_collateralization)\n                    (current_initial_collateralization)\n                    (options)\n                    (force_settled_volume)\n                    (is_prediction_market)\n                    (settlement_price)\n                    (settlement_fund)\n                    (asset_cer_updated)\n                    (feed_cer_updated)\n                  )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::price_feed_with_icr )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object )\n"
  },
  {
    "path": "libraries/chain/balance_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/balance_evaluator.hpp>\n#include <graphene/protocol/pts_address.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result balance_claim_evaluator::do_evaluate(const balance_claim_operation& op)\n{\n   database& d = db();\n   balance = &op.balance_to_claim(d);\n\n   GRAPHENE_ASSERT(\n             op.balance_owner_key == balance->owner ||\n             pts_address(op.balance_owner_key, false, 56) == balance->owner ||\n             pts_address(op.balance_owner_key, true, 56) == balance->owner ||\n             pts_address(op.balance_owner_key, false, 0) == balance->owner ||\n             pts_address(op.balance_owner_key, true, 0) == balance->owner,\n             balance_claim_owner_mismatch,\n             \"Balance owner key was specified as '${op}' but balance's actual owner is '${bal}'\",\n             (\"op\", op.balance_owner_key)\n             (\"bal\", balance->owner)\n             );\n\n   FC_ASSERT(op.total_claimed.asset_id == balance->asset_type());\n\n   if( balance->is_vesting_balance() )\n   {\n      GRAPHENE_ASSERT(\n         balance->vesting_policy->is_withdraw_allowed(\n            { balance->balance,\n              d.head_block_time(),\n              op.total_claimed } ),\n         balance_claim_invalid_claim_amount,\n         \"Attempted to claim ${c} from a vesting balance with ${a} available\",\n         (\"c\", op.total_claimed)(\"a\", balance->available(d.head_block_time()))\n         );\n      GRAPHENE_ASSERT(\n         d.head_block_time() - balance->last_claim_date >= fc::days(1),\n         balance_claim_claimed_too_often,\n         \"Genesis vesting balances may not be claimed more than once per day.\"\n         );\n      return {};\n   }\n\n   FC_ASSERT(op.total_claimed == balance->balance);\n   return {};\n}\n\n/**\n * @note the fee is always 0 for this particular operation because once the\n * balance is claimed it frees up memory and it cannot be used to spam the network\n */\nvoid_result balance_claim_evaluator::do_apply(const balance_claim_operation& op)\n{\n   database& d = db();\n\n   if( balance->is_vesting_balance() && op.total_claimed < balance->balance )\n      d.modify(*balance, [&](balance_object& b) {\n         b.vesting_policy->on_withdraw({b.balance, d.head_block_time(), op.total_claimed});\n         b.balance -= op.total_claimed;\n         b.last_claim_date = d.head_block_time();\n      });\n   else\n      d.remove(*balance);\n\n   d.adjust_balance(op.deposit_to_account, op.total_claimed);\n   return {};\n}\n} } // namespace graphene::chain\n"
  },
  {
    "path": "libraries/chain/block_database.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/block_database.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <fc/io/raw.hpp>\n#include <boost/endian/buffers.hpp>\n\nnamespace graphene { namespace chain {\n\nstruct index_entry\n{\n   index_entry() {\n      block_pos = 0;\n      block_size = 0;\n   };\n   boost::endian::little_uint64_buf_t block_pos;\n   boost::endian::little_uint32_buf_t block_size;\n   block_id_type                      block_id;\n};\n }}\nFC_REFLECT( graphene::chain::index_entry, (block_pos)(block_size)(block_id) );\n\nnamespace graphene { namespace chain {\n\nvoid block_database::open( const fc::path& dbdir )\n{ try {\n   fc::create_directories(dbdir);\n   _block_num_to_pos.exceptions(std::ios_base::failbit | std::ios_base::badbit);\n   _blocks.exceptions(std::ios_base::failbit | std::ios_base::badbit);\n\n   _index_filename = dbdir / \"index\";\n   if( !fc::exists( _index_filename ) )\n   {\n     _block_num_to_pos.open( _index_filename.generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);\n     _blocks.open( (dbdir/\"blocks\").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);\n   }\n   else\n   {\n     _block_num_to_pos.open( _index_filename.generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );\n     _blocks.open( (dbdir/\"blocks\").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );\n   }\n} FC_CAPTURE_AND_RETHROW( (dbdir) ) }\n\nbool block_database::is_open()const\n{\n  return _blocks.is_open();\n}\n\nvoid block_database::close()\n{\n  _blocks.close();\n  _block_num_to_pos.close();\n}\n\nvoid block_database::flush()\n{\n  _blocks.flush();\n  _block_num_to_pos.flush();\n}\n\nvoid block_database::store( const block_id_type& _id, const signed_block& b )\n{\n   block_id_type id = _id;\n   if( id == block_id_type() )\n   {\n      id = b.id();\n      elog( \"id argument of block_database::store() was not initialized for block ${id}\", (\"id\", id) );\n   }\n   _block_num_to_pos.seekp( sizeof( index_entry ) * int64_t(block_header::num_from_id(id)) );\n   index_entry e;\n   _blocks.seekp( 0, _blocks.end );\n   auto vec = fc::raw::pack( b );\n   e.block_pos  = _blocks.tellp();\n   e.block_size = vec.size();\n   e.block_id   = id;\n   _blocks.write( vec.data(), vec.size() );\n   _block_num_to_pos.write( (char*)&e, sizeof(e) );\n}\n\nvoid block_database::remove( const block_id_type& id )\n{ try {\n   index_entry e;\n   int64_t index_pos = sizeof(e) * int64_t(block_header::num_from_id(id));\n   _block_num_to_pos.seekg( 0, _block_num_to_pos.end );\n   if ( _block_num_to_pos.tellg() <= index_pos )\n      FC_THROW_EXCEPTION(fc::key_not_found_exception, \"Block ${id} not contained in block database\", (\"id\", id));\n\n   _block_num_to_pos.seekg( index_pos );\n   _block_num_to_pos.read( (char*)&e, sizeof(e) );\n\n   if( e.block_id == id )\n   {\n      e.block_size = 0;\n      _block_num_to_pos.seekp( sizeof(e) * int64_t(block_header::num_from_id(id)) );\n      _block_num_to_pos.write( (char*)&e, sizeof(e) );\n   }\n} FC_CAPTURE_AND_RETHROW( (id) ) }\n\nbool block_database::contains( const block_id_type& id )const\n{\n   if( id == block_id_type() )\n      return false;\n\n   index_entry e;\n   int64_t index_pos = sizeof(e) * int64_t(block_header::num_from_id(id));\n   _block_num_to_pos.seekg( 0, _block_num_to_pos.end );\n   if ( _block_num_to_pos.tellg() < int64_t(index_pos + sizeof(e)) )\n      return false;\n   _block_num_to_pos.seekg( index_pos );\n   _block_num_to_pos.read( (char*)&e, sizeof(e) );\n\n   return e.block_id == id && e.block_size.value() > 0;\n}\n\nblock_id_type block_database::fetch_block_id( uint32_t block_num )const\n{\n   assert( block_num != 0 );\n   index_entry e;\n   int64_t index_pos = sizeof(e) * int64_t(block_num);\n   _block_num_to_pos.seekg( 0, _block_num_to_pos.end );\n   if ( _block_num_to_pos.tellg() <= index_pos )\n      FC_THROW_EXCEPTION(fc::key_not_found_exception, \"Block number ${block_num} not contained in block database\", (\"block_num\", block_num));\n\n   _block_num_to_pos.seekg( index_pos );\n   _block_num_to_pos.read( (char*)&e, sizeof(e) );\n\n   FC_ASSERT( e.block_id != block_id_type(), \"Empty block_id in block_database (maybe corrupt on disk?)\" );\n   return e.block_id;\n}\n\noptional<signed_block> block_database::fetch_optional( const block_id_type& id )const\n{\n   try\n   {\n      index_entry e;\n      int64_t index_pos = sizeof(e) * int64_t(block_header::num_from_id(id));\n      _block_num_to_pos.seekg( 0, _block_num_to_pos.end );\n      if ( _block_num_to_pos.tellg() <= index_pos )\n         return {};\n\n      _block_num_to_pos.seekg( index_pos );\n      _block_num_to_pos.read( (char*)&e, sizeof(e) );\n\n      if( e.block_id != id ) return optional<signed_block>();\n\n      vector<char> data( e.block_size.value() );\n      _blocks.seekg( e.block_pos.value() );\n      if (e.block_size.value())\n         _blocks.read( data.data(), e.block_size.value() );\n      auto result = fc::raw::unpack<signed_block>(data);\n      FC_ASSERT( result.id() == e.block_id );\n      return result;\n   }\n   catch (const fc::exception&)\n   {\n   }\n   catch (const std::exception&)\n   {\n   }\n   return optional<signed_block>();\n}\n\noptional<signed_block> block_database::fetch_by_number( uint32_t block_num )const\n{\n   try\n   {\n      index_entry e;\n      int64_t index_pos = sizeof(e) * int64_t(block_num);\n      _block_num_to_pos.seekg( 0, _block_num_to_pos.end );\n      if ( _block_num_to_pos.tellg() <= index_pos )\n         return {};\n\n      _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg );\n      _block_num_to_pos.read( (char*)&e, sizeof(e) );\n\n      vector<char> data( e.block_size.value() );\n      _blocks.seekg( e.block_pos.value() );\n      _blocks.read( data.data(), e.block_size.value() );\n      auto result = fc::raw::unpack<signed_block>(data);\n      FC_ASSERT( result.id() == e.block_id );\n      return result;\n   }\n   catch (const fc::exception&)\n   {\n   }\n   catch (const std::exception&)\n   {\n   }\n   return optional<signed_block>();\n}\n\noptional<index_entry> block_database::last_index_entry()const {\n   try\n   {\n      index_entry e;\n\n      _block_num_to_pos.seekg( 0, _block_num_to_pos.end );\n      std::streampos pos = _block_num_to_pos.tellg();\n      if( pos < long(sizeof(index_entry)) )\n         return optional<index_entry>();\n\n      pos -= pos % sizeof(index_entry);\n\n      _blocks.seekg( 0, _block_num_to_pos.end );\n      const std::streampos blocks_size = _blocks.tellg();\n      while( pos > 0 )\n      {\n         pos -= sizeof(index_entry);\n         _block_num_to_pos.seekg( pos );\n         _block_num_to_pos.read( (char*)&e, sizeof(e) );\n         if( _block_num_to_pos.gcount() == sizeof(e) && e.block_size.value() > 0\n                && int64_t(e.block_pos.value() + e.block_size.value()) <= blocks_size )\n            try\n            {\n               vector<char> data( e.block_size.value() );\n               _blocks.seekg( e.block_pos.value() );\n               _blocks.read( data.data(), e.block_size.value() );\n               if( _blocks.gcount() == long(e.block_size.value()) )\n               {\n                  const signed_block block = fc::raw::unpack<signed_block>(data);\n                  if( block.id() == e.block_id )\n                     return e;\n               }\n            }\n            catch (const fc::exception&)\n            {\n            }\n            catch (const std::exception&)\n            {\n            }\n         fc::resize_file( _index_filename, pos );\n      }\n   }\n   catch (const fc::exception&)\n   {\n   }\n   catch (const std::exception&)\n   {\n   }\n   return optional<index_entry>();\n}\n\noptional<signed_block> block_database::last()const\n{\n   optional<index_entry> entry = last_index_entry();\n   if( entry.valid() ) return fetch_by_number( block_header::num_from_id(entry->block_id) );\n   return optional<signed_block>();\n}\n\noptional<block_id_type> block_database::last_id()const\n{\n   optional<index_entry> entry = last_index_entry();\n   if( entry.valid() ) return entry->block_id;\n   return optional<block_id_type>();\n}\n\nsize_t block_database::blocks_current_position()const\n{\n   return (size_t)_blocks.tellg();\n}\n\nsize_t block_database::total_block_size()const\n{\n   _blocks.seekg( 0, _blocks.end );\n   return (size_t)_blocks.tellg();\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/buyback.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/buyback.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid evaluate_buyback_account_options( const database& db, const buyback_account_options& bbo )\n{\n   const asset_object& a = bbo.asset_to_buy(db);\n   GRAPHENE_ASSERT( a.issuer == bbo.asset_to_buy_issuer,\n      account_create_buyback_incorrect_issuer, \"Incorrect asset issuer specified in buyback_account_options\", (\"asset\", a)(\"bbo\", bbo) );\n   GRAPHENE_ASSERT( !a.buyback_account.valid(),\n      account_create_buyback_already_exists, \"Cannot create buyback for asset which already has buyback\", (\"asset\", a)(\"bbo\", bbo) );\n   // TODO:  Replace with chain parameter #554\n   GRAPHENE_ASSERT( bbo.markets.size() < GRAPHENE_DEFAULT_MAX_BUYBACK_MARKETS,\n      account_create_buyback_too_many_markets, \"Too many buyback markets\", (\"asset\", a)(\"bbo\", bbo) );\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/committee_member_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/committee_member_evaluator.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/protocol/vote.hpp>\n#include <graphene/chain/transaction_evaluation_state.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result committee_member_create_evaluator::do_evaluate( const committee_member_create_operation& op )\n{ try {\n   FC_ASSERT(db().get(op.committee_member_account).is_lifetime_member());\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nobject_id_type committee_member_create_evaluator::do_apply( const committee_member_create_operation& op )\n{ try {\n   vote_id_type vote_id;\n   db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) {\n      vote_id = vote_id_type(vote_id_type::committee, p.next_available_vote_id++);\n   });\n\n   const auto& new_del_object = db().create<committee_member_object>( [&]( committee_member_object& obj ){\n         obj.committee_member_account   = op.committee_member_account;\n         obj.vote_id            = vote_id;\n         obj.url                = op.url;\n   });\n   return new_del_object.id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result committee_member_update_evaluator::do_evaluate( const committee_member_update_operation& op )\n{ try {\n   FC_ASSERT(db().get(op.committee_member).committee_member_account == op.committee_member_account);\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result committee_member_update_evaluator::do_apply( const committee_member_update_operation& op )\n{ try {\n   database& _db = db();\n   _db.modify(\n      _db.get(op.committee_member),\n      [&]( committee_member_object& com )\n      {\n         if( op.new_url.valid() )\n            com.url = *op.new_url;\n      });\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result committee_member_update_global_parameters_evaluator::do_evaluate(\n        const committee_member_update_global_parameters_operation& o)\n{ try {\n   FC_ASSERT(trx_state->_is_proposed_trx);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result committee_member_update_global_parameters_evaluator::do_apply(\n        const committee_member_update_global_parameters_operation& o)\n{ try {\n   db().modify(db().get_global_properties(), [&o](global_property_object& p) {\n      p.pending_parameters = o.new_parameters;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/confidential_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/protocol/confidential.hpp>\n#include <graphene/chain/confidential_evaluator.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/fba_accumulator_id.hpp>\n#include <graphene/chain/hardfork.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )\n{ try {\n   const auto& d = db();\n\n   const auto& atype = o.amount.asset_id(db()); \n   FC_ASSERT( atype.allow_confidential() );\n   FC_ASSERT( !atype.is_transfer_restricted() );\n   FC_ASSERT( !(atype.options.flags & white_list) );\n\n   for( const auto& out : o.outputs )\n   {\n      for( const auto& a : out.owner.account_auths )\n         a.first(d); // verify all accounts exist and are valid\n   }\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\nvoid_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o ) \n{ try {\n   db().adjust_balance( o.from, -o.amount ); \n\n   const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db());  // verify fee is a legit asset \n   db().modify( add, [&]( asset_dynamic_data_object& obj ){\n      obj.confidential_supply += o.amount.amount;\n      FC_ASSERT( obj.confidential_supply >= 0 );\n   });\n   for( const auto& out : o.outputs )\n   {\n      db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){\n          obj.asset_id   = o.amount.asset_id;\n          obj.owner      = out.owner;\n          obj.commitment = out.commitment;\n      });\n   }\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid transfer_to_blind_evaluator::pay_fee()\n{\n   if( db().head_block_time() >= HARDFORK_563_TIME )\n      pay_fba_fee( fba_accumulator_id_transfer_to_blind );\n   else\n      generic_evaluator::pay_fee();\n}\n\nvoid_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )\n{ try {\n   const auto& d = db();\n   o.fee.asset_id(d);  // verify fee is a legit asset \n   const auto& bbi = d.get_index_type<blinded_balance_index>();\n   const auto& cidx = bbi.indices().get<by_commitment>();\n   for( const auto& in : o.inputs )\n   {\n      auto itr = cidx.find( in.commitment );\n      FC_ASSERT( itr != cidx.end() );\n      FC_ASSERT( itr->asset_id == o.fee.asset_id );\n      FC_ASSERT( itr->owner == in.owner );\n   }\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o ) \n{ try {\n   db().adjust_balance( o.fee_payer(), o.fee ); \n   db().adjust_balance( o.to, o.amount ); \n   const auto& bbi = db().get_index_type<blinded_balance_index>();\n   const auto& cidx = bbi.indices().get<by_commitment>();\n   for( const auto& in : o.inputs )\n   {\n      auto itr = cidx.find( in.commitment );\n      FC_ASSERT( itr != cidx.end() );\n      db().remove( *itr );\n   }\n   const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db());  // verify fee is a legit asset \n   db().modify( add, [&]( asset_dynamic_data_object& obj ){\n      obj.confidential_supply -= o.amount.amount + o.fee.amount;\n      FC_ASSERT( obj.confidential_supply >= 0 );\n   });\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid transfer_from_blind_evaluator::pay_fee()\n{\n   if( db().head_block_time() >= HARDFORK_563_TIME )\n      pay_fba_fee( fba_accumulator_id_transfer_from_blind );\n   else\n      generic_evaluator::pay_fee();\n}\n\nvoid_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )\n{ try {\n   const auto& d = db();\n   o.fee.asset_id(db());  // verify fee is a legit asset \n   const auto& bbi = db().get_index_type<blinded_balance_index>();\n   const auto& cidx = bbi.indices().get<by_commitment>();\n   for( const auto& out : o.outputs )\n   {\n      for( const auto& a : out.owner.account_auths )\n         a.first(d); // verify all accounts exist and are valid\n   }\n   for( const auto& in : o.inputs )\n   {\n      auto itr = cidx.find( in.commitment );\n      GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, \"\", (\"commitment\",in.commitment) );\n      FC_ASSERT( itr->asset_id == o.fee.asset_id );\n      FC_ASSERT( itr->owner == in.owner );\n   }\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o ) \n{ try {\n   db().adjust_balance( o.fee_payer(), o.fee ); // deposit the fee to the temp account\n   const auto& bbi = db().get_index_type<blinded_balance_index>();\n   const auto& cidx = bbi.indices().get<by_commitment>();\n   for( const auto& in : o.inputs )\n   {\n      auto itr = cidx.find( in.commitment );\n      GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, \"\", (\"commitment\",in.commitment) );\n      db().remove( *itr );\n   }\n   for( const auto& out : o.outputs )\n   {\n      db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){\n          obj.asset_id   = o.fee.asset_id;\n          obj.owner      = out.owner;\n          obj.commitment = out.commitment;\n      });\n   }\n   const auto& add = o.fee.asset_id(db()).dynamic_asset_data_id(db());  \n   db().modify( add, [&]( asset_dynamic_data_object& obj ){\n      obj.confidential_supply -= o.fee.amount;\n      FC_ASSERT( obj.confidential_supply >= 0 );\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid blind_transfer_evaluator::pay_fee()\n{\n   if( db().head_block_time() >= HARDFORK_563_TIME )\n      pay_fba_fee( fba_accumulator_id_blind_transfer );\n   else\n      generic_evaluator::pay_fee();\n}\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/custom_authority_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/custom_authority_evaluator.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork_visitor.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result custom_authority_create_evaluator::do_evaluate(const custom_authority_create_operation& op)\n{ try {\n   const database& d = db();\n   auto now = d.head_block_time();\n   FC_ASSERT(HARDFORK_BSIP_40_PASSED(now), \"Custom active authorities are not yet enabled\");\n\n   op.account(d);\n\n   const auto& config = d.get_global_properties().parameters.extensions.value.custom_authority_options;\n   FC_ASSERT(config.valid(), \"Cannot use custom authorities yet: global configuration not set\");\n   FC_ASSERT(op.valid_to > now, \"Custom authority expiration must be in the future\");\n   FC_ASSERT((op.valid_to - now).to_seconds() <= config->max_custom_authority_lifetime_seconds,\n             \"Custom authority lifetime exceeds maximum limit\");\n\n   bool operation_forked_in = hardfork_visitor(now).visit((operation::tag_type)op.operation_type.value);\n   FC_ASSERT(operation_forked_in, \"Cannot create custom authority for operation which is not valid yet\");\n\n   auto restriction_count = restriction::restriction_count(op.restrictions);\n   FC_ASSERT(restriction_count <= config->max_custom_authority_restrictions,\n             \"Custom authority has more than the maximum number of restrictions\");\n\n   for (const auto& account_weight_pair : op.auth.account_auths)\n      account_weight_pair.first(d);\n\n   const auto& index = d.get_index_type<custom_authority_index>().indices().get<by_account_custom>();\n   auto range = index.equal_range(op.account);\n   FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account,\n             \"Cannot create custom authority: account already has maximum number\");\n   range = index.equal_range(boost::make_tuple(op.account, op.operation_type));\n   FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account_op,\n             \"Cannot create custom authority: account already has maximum number for this operation type\");\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((op)) }\n\nobject_id_type custom_authority_create_evaluator::do_apply(const custom_authority_create_operation& op)\n{ try {\n   database& d = db();\n\n   return d.create<custom_authority_object>([&op] (custom_authority_object& obj) mutable {\n      obj.account = op.account;\n      obj.enabled = op.enabled;\n      obj.valid_from = op.valid_from;\n      obj.valid_to = op.valid_to;\n      obj.operation_type = op.operation_type;\n      obj.auth = op.auth;\n      std::for_each(op.restrictions.begin(), op.restrictions.end(), [&obj](const restriction& r) mutable {\n         obj.restrictions.insert(std::make_pair(obj.restriction_counter++, r));\n      });\n   }).id;\n} FC_CAPTURE_AND_RETHROW((op)) }\n\nvoid_result custom_authority_update_evaluator::do_evaluate(const custom_authority_update_operation& op)\n{ try {\n   const database& d = db();\n   auto now = d.head_block_time();\n   old_object = &op.authority_to_update(d);\n   FC_ASSERT(old_object->account == op.account, \"Cannot update a different account's custom authority\");\n\n   if (op.new_enabled)\n      FC_ASSERT(*op.new_enabled != old_object->enabled,\n                \"Custom authority update specifies an enabled flag, but flag is not changed\");\n\n   const auto& config = d.get_global_properties().parameters.extensions.value.custom_authority_options;\n   auto valid_from = old_object->valid_from;\n   auto valid_to = old_object->valid_to;\n   if (op.new_valid_from) {\n      FC_ASSERT(*op.new_valid_from != old_object->valid_from,\n                \"Custom authority update specifies a new valid from date, but date is not changed\");\n      valid_from = *op.new_valid_from;\n   }\n   if (op.new_valid_to) {\n      FC_ASSERT(*op.new_valid_to != old_object->valid_to,\n                \"Custom authority update specifies a new valid to date, but date is not changed\");\n      FC_ASSERT(*op.new_valid_to > now, \"Custom authority expiration must be in the future\");\n      FC_ASSERT((*op.new_valid_to - now).to_seconds() <= config->max_custom_authority_lifetime_seconds,\n                \"Custom authority lifetime exceeds maximum limit\");\n      valid_to = *op.new_valid_to;\n   }\n   FC_ASSERT(valid_from < valid_to, \"Custom authority validity begin date must be before expiration date\");\n\n   if (op.new_auth) {\n      FC_ASSERT(*op.new_auth != old_object->auth,\n                \"Custom authority update specifies a new authentication authority, but authority is not changed\");\n      for (const auto& account_weight_pair : op.new_auth->account_auths)\n         account_weight_pair.first(d);\n   }\n\n   std::for_each(op.restrictions_to_remove.begin(), op.restrictions_to_remove.end(), [this](uint16_t id) {\n      FC_ASSERT(old_object->restrictions.count(id) == 1, \"Cannot remove restriction ID ${I}: ID not found\",\n                (\"I\", id));\n   });\n   if (!op.restrictions_to_add.empty()) {\n      // Sanity check\n      if (!old_object->restrictions.empty())\n         FC_ASSERT((--old_object->restrictions.end())->first < old_object->restriction_counter,\n                   \"LOGIC ERROR: Restriction counter overlaps restrictions. Please report this error.\");\n      FC_ASSERT(old_object->restriction_counter + op.restrictions_to_add.size() > old_object->restriction_counter,\n                \"Unable to add restrictions: causes wraparound of restriction IDs\");\n   }\n\n   // Add up the restriction counts for all old restrictions not being removed, and all new ones\n   size_t restriction_count = 0;\n   for (const auto& restriction_pair : old_object->restrictions)\n      if (op.restrictions_to_remove.count(restriction_pair.first) == 0)\n         restriction_count += restriction_pair.second.restriction_count();\n   restriction_count += restriction::restriction_count(op.restrictions_to_add);\n   // Check restriction count against limit\n   FC_ASSERT(restriction_count <= config->max_custom_authority_restrictions,\n             \"Cannot update custom authority: updated authority would exceed the maximum number of restrictions\");\n\n   get_restriction_predicate(op.restrictions_to_add, old_object->operation_type);\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((op)) }\n\nvoid_result custom_authority_update_evaluator::do_apply(const custom_authority_update_operation& op)\n{ try {\n   database& d = db();\n\n   d.modify(*old_object, [&op](custom_authority_object& obj) {\n      if (op.new_enabled) obj.enabled = *op.new_enabled;\n      if (op.new_valid_from) obj.valid_from = *op.new_valid_from;\n      if (op.new_valid_to) obj.valid_to = *op.new_valid_to;\n      if (op.new_auth) obj.auth = *op.new_auth;\n\n      std::for_each(op.restrictions_to_remove.begin(), op.restrictions_to_remove.end(), [&obj](auto id) mutable {\n         obj.restrictions.erase(id);\n      });\n      std::for_each(op.restrictions_to_add.begin(), op.restrictions_to_add.end(), [&obj](const auto& r) mutable {\n         obj.restrictions.insert(std::make_pair(obj.restriction_counter++, r));\n      });\n\n      // Clear the predicate cache\n      obj.clear_predicate_cache();\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((op)) }\n\nvoid_result custom_authority_delete_evaluator::do_evaluate(const custom_authority_delete_operation& op)\n{ try {\n   const database& d = db();\n\n   old_object = &op.authority_to_delete(d);\n   FC_ASSERT(old_object->account == op.account, \"Cannot delete a different account's custom authority\");\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((op)) }\n\nvoid_result custom_authority_delete_evaluator::do_apply(const custom_authority_delete_operation& op)\n{ try {\n   database& d = db();\n\n   d.remove(*old_object);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW((op)) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/database.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"db_balance.cpp\"\n#include \"db_block.cpp\"\n#include \"db_debug.cpp\"\n#include \"db_getter.cpp\"\n#include \"db_init.cpp\"\n#include \"db_maint.cpp\"\n#include \"db_management.cpp\"\n#include \"db_market.cpp\"\n#include \"db_update.cpp\"\n#include \"db_witness_schedule.cpp\"\n#include \"db_notify.cpp\""
  },
  {
    "path": "libraries/chain/db_balance.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <boost/range/algorithm.hpp>\n\nnamespace graphene { namespace chain {\n\nasset database::get_balance(account_id_type owner, asset_id_type asset_id) const\n{\n   auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index<balances_by_account_index>();\n   auto abo = index.get_account_balance( owner, asset_id );\n   if( !abo )\n      return asset(0, asset_id);\n   return abo->get_balance();\n}\n\nasset database::get_balance(const account_object& owner, const asset_object& asset_obj) const\n{\n   return get_balance(owner.get_id(), asset_obj.get_id());\n}\n\nstring database::to_pretty_string( const asset& a )const\n{\n   return a.asset_id(*this).amount_to_pretty_string(a.amount);\n}\n\nvoid database::adjust_balance(account_id_type account, asset delta )\n{ try {\n   if( delta.amount == 0 )\n      return;\n\n   auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index<balances_by_account_index>();\n   auto abo = index.get_account_balance( account, delta.asset_id );\n   if( !abo )\n   {\n      FC_ASSERT( delta.amount > 0, \"Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}\", \n                 (\"a\",account(*this).name)\n                 (\"b\",to_pretty_string(asset(0,delta.asset_id)))\n                 (\"r\",to_pretty_string(-delta)));\n      create<account_balance_object>([account,&delta](account_balance_object& b) {\n         b.owner = account;\n         b.asset_type = delta.asset_id;\n         b.balance = delta.amount.value;\n         if( b.asset_type == asset_id_type() ) // CORE asset\n            b.maintenance_flag = true;\n      });\n   } else {\n      if( delta.amount < 0 )\n         FC_ASSERT( abo->get_balance() >= -delta, \"Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}\",\n                    (\"a\",account(*this).name)(\"b\",to_pretty_string(abo->get_balance()))(\"r\",to_pretty_string(-delta)));\n      modify(*abo, [delta](account_balance_object& b) {\n         b.adjust_balance(delta);\n      });\n   }\n\n} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }\n\nnamespace detail {\n\n   /**\n    * Used as a key to search vesting_balance_object in the index\n   */\n   struct vbo_mfs_key\n   {\n      account_id_type   account_id;\n      asset_id_type     asset_id;\n\n      vbo_mfs_key(const account_id_type& account, const asset_id_type& asset):\n         account_id(account),\n         asset_id(asset)\n      {}\n\n      bool operator()(const vbo_mfs_key& k, const vesting_balance_object& vbo)const\n      {\n         return ( vbo.balance_type == vesting_balance_type::market_fee_sharing ) &&\n                  ( k.asset_id == vbo.balance.asset_id ) &&\n                  ( k.account_id == vbo.owner );\n      }\n\n      uint64_t operator()(const vbo_mfs_key& k)const\n      {\n         return vbo_mfs_hash(k.account_id, k.asset_id);\n      }\n   };\n} //detail\n\nasset database::get_market_fee_vesting_balance(const account_id_type &account_id, const asset_id_type &asset_id)\n{\n   auto& vesting_balances = get_index_type<vesting_balance_index>().indices().get<by_vesting_type>();\n   const auto& key = detail::vbo_mfs_key{account_id, asset_id};\n   auto vbo_it = vesting_balances.find(key, key, key);\n\n   if( vbo_it == vesting_balances.end() )\n   {\n      return asset(0, asset_id);\n   }\n   return vbo_it->balance;\n}\n\nvoid database::deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta)\n{ try {\n   FC_ASSERT( delta.amount >= 0, \"Invalid negative value for balance\");\n\n   if( delta.amount == 0 )\n      return;\n\n   auto& vesting_balances = get_index_type<vesting_balance_index>().indices().get<by_vesting_type>();\n   const auto& key = detail::vbo_mfs_key{account_id, delta.asset_id};\n   auto vbo_it = vesting_balances.find(key, key, key);\n\n   auto block_time = head_block_time();\n\n   if( vbo_it == vesting_balances.end() )\n   {\n      create<vesting_balance_object>([&account_id, &delta](vesting_balance_object &vbo) {\n         vbo.owner = account_id;\n         vbo.balance = delta;\n         vbo.balance_type = vesting_balance_type::market_fee_sharing;\n         vbo.policy = instant_vesting_policy{};\n      });\n   } else {\n      modify( *vbo_it, [&block_time, &delta]( vesting_balance_object& vbo )\n      {\n         vbo.deposit_vested(block_time, delta);\n      });\n   }\n} FC_CAPTURE_AND_RETHROW( (account_id)(delta) ) }\n\noptional< vesting_balance_id_type > database::deposit_lazy_vesting(\n   const optional< vesting_balance_id_type >& ovbid,\n   share_type amount, uint32_t req_vesting_seconds,\n   vesting_balance_type balance_type,\n   account_id_type req_owner,\n   bool require_vesting )\n{\n   if( amount == 0 )\n      return optional< vesting_balance_id_type >();\n\n   fc::time_point_sec now = head_block_time();\n\n   while( true )\n   {\n      if( !ovbid.valid() )\n         break;\n      const vesting_balance_object& vbo = (*ovbid)(*this);\n      if( vbo.owner != req_owner )\n         break;\n      if( !vbo.policy.is_type< cdd_vesting_policy >() )\n         break;\n      if( vbo.policy.get< cdd_vesting_policy >().vesting_seconds != req_vesting_seconds )\n         break;\n      modify( vbo, [&]( vesting_balance_object& _vbo )\n      {\n         if( require_vesting )\n            _vbo.deposit(now, amount);\n         else\n            _vbo.deposit_vested(now, amount);\n      } );\n      return optional< vesting_balance_id_type >();\n   }\n\n   const vesting_balance_object& vbo = create< vesting_balance_object >( [&]( vesting_balance_object& _vbo )\n   {\n      _vbo.owner = req_owner;\n      _vbo.balance = amount;\n      _vbo.balance_type = balance_type;\n\n      cdd_vesting_policy policy;\n      policy.vesting_seconds = req_vesting_seconds;\n      policy.coin_seconds_earned = require_vesting ? 0 : amount.value * policy.vesting_seconds;\n      policy.coin_seconds_earned_last_update = now;\n\n      _vbo.policy = policy;\n   } );\n\n   return vbo.id;\n}\n\nvoid database::deposit_cashback(const account_object& acct, share_type amount, bool require_vesting)\n{\n   // If we don't have a VBO, or if it has the wrong maturity\n   // due to a policy change, cut it loose.\n\n   if( amount == 0 )\n      return;\n\n   if( acct.get_id() == GRAPHENE_COMMITTEE_ACCOUNT || acct.get_id() == GRAPHENE_WITNESS_ACCOUNT ||\n       acct.get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT || acct.get_id() == GRAPHENE_NULL_ACCOUNT ||\n       acct.get_id() == GRAPHENE_TEMP_ACCOUNT )\n   {\n      // The blockchain's accounts do not get cashback; it simply goes to the reserve pool.\n      modify( get_core_dynamic_data(), [amount](asset_dynamic_data_object& d) {\n         d.current_supply -= amount;\n      });\n      return;\n   }\n\n   optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(\n      acct.cashback_vb,\n      amount,\n      get_global_properties().parameters.cashback_vesting_period_seconds,\n      vesting_balance_type::cashback,\n      acct.id,\n      require_vesting );\n\n   if( new_vbid.valid() )\n   {\n      modify( acct, [&new_vbid]( account_object& _acct )\n      {\n         _acct.cashback_vb = *new_vbid;\n      } );\n      modify( acct.statistics( *this ), []( account_statistics_object& aso )\n      {\n         aso.has_cashback_vb = true;\n      } );\n   }\n\n   return;\n}\n\nvoid database::deposit_witness_pay(const witness_object& wit, share_type amount)\n{\n   if( amount == 0 )\n      return;\n\n   optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(\n      wit.pay_vb,\n      amount,\n      get_global_properties().parameters.witness_pay_vesting_seconds,\n      vesting_balance_type::witness,\n      wit.witness_account,\n      true );\n\n   if( new_vbid.valid() )\n   {\n      modify( wit, [&]( witness_object& _wit )\n      {\n         _wit.pay_vb = *new_vbid;\n      } );\n   }\n\n   return;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_block.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/db_with.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/block_summary_object.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/transaction_history_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/witness_schedule_object.hpp>\n\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/thread/parallel.hpp>\n\nnamespace graphene { namespace chain {\n\nbool database::is_known_block( const block_id_type& id )const\n{\n   return _fork_db.is_known_block(id) || _block_id_to_block.contains(id);\n}\n/**\n * Only return true *if* the transaction has not expired or been invalidated. If this\n * method is called with a VERY old transaction we will return false, they should\n * query things by blocks if they are that old.\n */\nbool database::is_known_transaction( const transaction_id_type& id )const\n{\n   const auto& trx_idx = get_index_type<transaction_index>().indices().get<by_trx_id>();\n   return trx_idx.find( id ) != trx_idx.end();\n}\n\nblock_id_type  database::get_block_id_for_num( uint32_t block_num )const\n{ try {\n   return _block_id_to_block.fetch_block_id( block_num );\n} FC_CAPTURE_AND_RETHROW( (block_num) ) }\n\noptional<signed_block> database::fetch_block_by_id( const block_id_type& id )const\n{\n   auto b = _fork_db.fetch_block( id );\n   if( !b )\n      return _block_id_to_block.fetch_optional(id);\n   return b->data;\n}\n\noptional<signed_block> database::fetch_block_by_number( uint32_t num )const\n{\n   auto results = _fork_db.fetch_block_by_number(num);\n   if( results.size() == 1 )\n      return results[0]->data;\n   else\n      return _block_id_to_block.fetch_by_number(num);\n}\n\nconst signed_transaction& database::get_recent_transaction(const transaction_id_type& trx_id) const\n{\n   auto& index = get_index_type<transaction_index>().indices().get<by_trx_id>();\n   auto itr = index.find(trx_id);\n   FC_ASSERT(itr != index.end());\n   return itr->trx;\n}\n\nstd::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of_fork) const\n{\n  pair<fork_database::branch_type, fork_database::branch_type> branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork);\n  if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) )\n  {\n     edump( (head_of_fork)\n            (head_block_id())\n            (branches.first.size())\n            (branches.second.size()) );\n     assert(branches.first.back()->previous_id() == branches.second.back()->previous_id());\n  }\n  std::vector<block_id_type> result;\n  for (const item_ptr& fork_block : branches.second)\n    result.emplace_back(fork_block->id);\n  result.emplace_back(branches.first.back()->previous_id());\n  return result;\n}\n\n/**\n * Push block \"may fail\" in which case every partial change is unwound.  After\n * push block is successful the block is appended to the chain database on disk.\n *\n * @return true if we switched forks as a result of this push.\n */\nbool database::push_block(const signed_block& new_block, uint32_t skip)\n{\n//   idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous));\n   bool result;\n   detail::with_skip_flags( *this, skip, [&]()\n   {\n      detail::without_pending_transactions( *this, std::move(_pending_tx),\n      [&]()\n      {\n         result = _push_block(new_block);\n      });\n   });\n   return result;\n}\n\nbool database::_push_block(const signed_block& new_block)\n{ try {\n   uint32_t skip = get_node_properties().skip_flags;\n\n   const auto now = fc::time_point::now().sec_since_epoch();\n   if( _fork_db.head() && new_block.timestamp.sec_since_epoch() > now - 86400 )\n   {\n      // verify that the block signer is in the current set of active witnesses.\n      shared_ptr<fork_item> prev_block = _fork_db.fetch_block( new_block.previous );\n      GRAPHENE_ASSERT( prev_block, unlinkable_block_exception, \"block does not link to known chain\" );\n      if( prev_block->scheduled_witnesses && !(skip&(skip_witness_schedule_check|skip_witness_signature)) )\n         verify_signing_witness( new_block, *prev_block );\n   }\n\n   const shared_ptr<fork_item> new_head = _fork_db.push_block(new_block);\n   //If the head block from the longest chain does not build off of the current head, we need to switch forks.\n   if( new_head->data.previous != head_block_id() )\n   {\n      //If the newly pushed block is the same height as head, we get head back in new_head\n      //Only switch forks if new_head is actually higher than head\n      if( new_head->data.block_num() > head_block_num() )\n      {\n         wlog( \"Switching to fork: ${id}\", (\"id\",new_head->data.id()) );\n         auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id());\n\n         // pop blocks until we hit the forked block\n         while( head_block_id() != branches.second.back()->data.previous )\n         {\n            ilog( \"popping block #${n} ${id}\", (\"n\",head_block_num())(\"id\",head_block_id()) );\n            pop_block();\n         }\n\n         // push all blocks on the new fork\n         for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr )\n         {\n               ilog( \"pushing block from fork #${n} ${id}\", (\"n\",(*ritr)->data.block_num())(\"id\",(*ritr)->id) );\n               optional<fc::exception> except;\n               try {\n                  undo_database::session session = _undo_db.start_undo_session();\n                  apply_block( (*ritr)->data, skip );\n                  update_witnesses( **ritr );\n                  _block_id_to_block.store( (*ritr)->id, (*ritr)->data );\n                  session.commit();\n               }\n               catch ( const fc::exception& e ) { except = e; }\n               if( except )\n               {\n                  wlog( \"exception thrown while switching forks ${e}\", (\"e\",except->to_detail_string() ) );\n                  // remove the rest of branches.first from the fork_db, those blocks are invalid\n                  while( ritr != branches.first.rend() )\n                  {\n                     ilog( \"removing block from fork_db #${n} ${id}\", (\"n\",(*ritr)->data.block_num())(\"id\",(*ritr)->id) );\n                     _fork_db.remove( (*ritr)->id );\n                     ++ritr;\n                  }\n                  _fork_db.set_head( branches.second.front() );\n\n                  // pop all blocks from the bad fork\n                  while( head_block_id() != branches.second.back()->data.previous )\n                  {\n                     ilog( \"popping block #${n} ${id}\", (\"n\",head_block_num())(\"id\",head_block_id()) );\n                     pop_block();\n                  }\n\n                  ilog( \"Switching back to fork: ${id}\", (\"id\",branches.second.front()->data.id()) );\n                  // restore all blocks from the good fork\n                  for( auto ritr2 = branches.second.rbegin(); ritr2 != branches.second.rend(); ++ritr2 )\n                  {\n                     ilog( \"pushing block #${n} ${id}\", (\"n\",(*ritr2)->data.block_num())(\"id\",(*ritr2)->id) );\n                     auto session = _undo_db.start_undo_session();\n                     apply_block( (*ritr2)->data, skip );\n                     _block_id_to_block.store( (*ritr2)->id, (*ritr2)->data );\n                     session.commit();\n                  }\n                  throw *except;\n               }\n         }\n         return true;\n      }\n      else return false;\n   }\n\n   try {\n      auto session = _undo_db.start_undo_session();\n      apply_block(new_block, skip);\n      if( new_block.timestamp.sec_since_epoch() > now - 86400 )\n         update_witnesses( *new_head );\n      _block_id_to_block.store(new_block.id(), new_block);\n      session.commit();\n   } catch ( const fc::exception& e ) {\n      elog(\"Failed to push new block:\\n${e}\", (\"e\", e.to_detail_string()));\n      _fork_db.remove( new_block.id() );\n      throw;\n   }\n\n   return false;\n} FC_CAPTURE_AND_RETHROW( (new_block) ) }\n\nvoid database::verify_signing_witness( const signed_block& new_block, const fork_item& fork_entry )const\n{\n   FC_ASSERT( new_block.timestamp >= fork_entry.next_block_time );\n   uint32_t slot_num = ( new_block.timestamp - fork_entry.next_block_time ).to_seconds() / block_interval();\n   uint64_t index = ( fork_entry.next_block_aslot + slot_num ) % fork_entry.scheduled_witnesses->size();\n   const auto& scheduled_witness = (*fork_entry.scheduled_witnesses)[index];\n   FC_ASSERT( new_block.witness == scheduled_witness.first, \"Witness produced block at wrong time\",\n              (\"block witness\",new_block.witness)(\"scheduled\",scheduled_witness)(\"slot_num\",slot_num) );\n   FC_ASSERT( new_block.validate_signee( scheduled_witness.second ) );\n}\n\nvoid database::update_witnesses( fork_item& fork_entry )const\n{\n   if( fork_entry.scheduled_witnesses ) return;\n\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n   fork_entry.next_block_aslot = dpo.current_aslot + 1;\n   fork_entry.next_block_time = get_slot_time( 1 );\n\n   const witness_schedule_object& wso = get_witness_schedule_object();\n   fork_entry.scheduled_witnesses = std::make_shared< vector< pair< witness_id_type, public_key_type > > >();\n   fork_entry.scheduled_witnesses->reserve( wso.current_shuffled_witnesses.size() );\n   for( size_t i = 0; i < wso.current_shuffled_witnesses.size(); ++i )\n   {\n       const auto& witness = wso.current_shuffled_witnesses[i](*this);\n       fork_entry.scheduled_witnesses->emplace_back( wso.current_shuffled_witnesses[i], witness.signing_key );\n   }\n}\n\n/**\n * Attempts to push the transaction into the pending queue\n *\n * When called to push a locally generated transaction, set the skip_block_size_check bit on the skip argument. This\n * will allow the transaction to be pushed even if it causes the pending block size to exceed the maximum block size.\n * Although the transaction will probably not propagate further now, as the peers are likely to have their pending\n * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending\n * queues.\n */\nprocessed_transaction database::push_transaction( const precomputable_transaction& trx, uint32_t skip )\n{ try {\n   // see https://github.com/bitshares/bitshares-core/issues/1573\n   FC_ASSERT( fc::raw::pack_size( trx ) < (1024 * 1024), \"Transaction exceeds maximum transaction size.\" );\n   processed_transaction result;\n   detail::with_skip_flags( *this, skip, [&]()\n   {\n      result = _push_transaction( trx );\n   } );\n   return result;\n} FC_CAPTURE_AND_RETHROW( (trx) ) }\n\nprocessed_transaction database::_push_transaction( const precomputable_transaction& trx )\n{\n   // If this is the first transaction pushed after applying a block, start a new undo session.\n   // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.\n   if( !_pending_tx_session.valid() )\n      _pending_tx_session = _undo_db.start_undo_session();\n\n   // Create a temporary undo session as a child of _pending_tx_session.\n   // The temporary session will be discarded by the destructor if\n   // _apply_transaction fails.  If we make it to merge(), we\n   // apply the changes.\n\n   auto temp_session = _undo_db.start_undo_session();\n   auto processed_trx = _apply_transaction( trx );\n   _pending_tx.push_back(processed_trx);\n\n   // notify_changed_objects();\n   // The transaction applied successfully. Merge its changes into the pending block session.\n   temp_session.merge();\n\n   // notify anyone listening to pending transactions\n   notify_on_pending_transaction( trx );\n   return processed_trx;\n}\n\nprocessed_transaction database::validate_transaction( const signed_transaction& trx )\n{\n   auto session = _undo_db.start_undo_session();\n   return _apply_transaction( trx );\n}\n\nclass push_proposal_nesting_guard {\npublic:\n   push_proposal_nesting_guard( uint32_t& nesting_counter, const database& db )\n      : orig_value(nesting_counter), counter(nesting_counter)\n   {\n      FC_ASSERT( counter < db.get_global_properties().active_witnesses.size() * 2, \"Max proposal nesting depth exceeded!\" );\n      counter++;\n   }\n   ~push_proposal_nesting_guard()\n   {\n      if( --counter != orig_value )\n         elog( \"Unexpected proposal nesting count value: ${n} != ${o}\", (\"n\",counter)(\"o\",orig_value) );\n   }\nprivate:\n    const uint32_t  orig_value;\n    uint32_t& counter;\n};\n\nprocessed_transaction database::push_proposal(const proposal_object& proposal)\n{ try {\n   transaction_evaluation_state eval_state(this);\n   eval_state._is_proposed_trx = true;\n\n   eval_state.operation_results.reserve(proposal.proposed_transaction.operations.size());\n   processed_transaction ptrx(proposal.proposed_transaction);\n   eval_state._trx = &ptrx;\n   size_t old_applied_ops_size = _applied_ops.size();\n\n   try {\n      push_proposal_nesting_guard guard( _push_proposal_nesting_depth, *this );\n      if( _undo_db.size() >= _undo_db.max_size() )\n         _undo_db.set_max_size( _undo_db.size() + 1 );\n      auto session = _undo_db.start_undo_session(true);\n      for( auto& op : proposal.proposed_transaction.operations )\n         eval_state.operation_results.emplace_back(apply_operation(eval_state, op));\n      remove(proposal);\n      session.merge();\n   } catch ( const fc::exception& e ) {\n      if( head_block_time() <= HARDFORK_483_TIME )\n      {\n         for( size_t i=old_applied_ops_size,n=_applied_ops.size(); i<n; i++ )\n         {\n            ilog( \"removing failed operation from applied_ops: ${op}\", (\"op\", *(_applied_ops[i])) );\n            _applied_ops[i].reset();\n         }\n      }\n      else\n      {\n         _applied_ops.resize( old_applied_ops_size );\n      }\n      wlog( \"${e}\", (\"e\",e.to_detail_string() ) );\n      throw;\n   }\n\n   ptrx.operation_results = std::move(eval_state.operation_results);\n   return ptrx;\n} FC_CAPTURE_AND_RETHROW( (proposal) ) }\n\nsigned_block database::generate_block(\n   fc::time_point_sec when,\n   witness_id_type witness_id,\n   const fc::ecc::private_key& block_signing_private_key,\n   uint32_t skip /* = 0 */\n   )\n{ try {\n   signed_block result;\n   detail::with_skip_flags( *this, skip, [&]()\n   {\n      result = _generate_block( when, witness_id, block_signing_private_key );\n   } );\n   return result;\n} FC_CAPTURE_AND_RETHROW() }\n\nsigned_block database::_generate_block(\n   fc::time_point_sec when,\n   witness_id_type witness_id,\n   const fc::ecc::private_key& block_signing_private_key\n   )\n{\n   try {\n   uint32_t skip = get_node_properties().skip_flags;\n   uint32_t slot_num = get_slot_at_time( when );\n   FC_ASSERT( slot_num > 0 );\n   witness_id_type scheduled_witness = get_scheduled_witness( slot_num );\n   FC_ASSERT( scheduled_witness == witness_id );\n\n   //\n   // The following code throws away existing pending_tx_session and\n   // rebuilds it by re-applying pending transactions.\n   //\n   // This rebuild is necessary because pending transactions' validity\n   // and semantics may have changed since they were received, because\n   // time-based semantics are evaluated based on the current block\n   // time.  These changes can only be reflected in the database when\n   // the value of the \"when\" variable is known, which means we need to\n   // re-apply pending transactions in this method.\n   //\n\n   // pop pending state (reset to head block state)\n   _pending_tx_session.reset();\n\n   // Check witness signing key\n   if( !(skip & skip_witness_signature) )\n   {\n      // Note: if this check failed (which won't happen in normal situations),\n      // we would have temporarily broken the invariant that\n      // _pending_tx_session is the result of applying _pending_tx.\n      // In this case, when the node received a new block,\n      // the push_block() call will re-create the _pending_tx_session.\n      FC_ASSERT( witness_id(*this).signing_key == block_signing_private_key.get_public_key() );\n   }\n\n   static const size_t max_partial_block_header_size = fc::raw::pack_size( signed_block_header() )\n                                                       - fc::raw::pack_size( witness_id_type() ) // witness_id\n                                                       + 3; // max space to store size of transactions (out of block header),\n                                                            // +3 means 3*7=21 bits so it's practically safe\n   const size_t max_block_header_size = max_partial_block_header_size + fc::raw::pack_size( witness_id );\n   auto maximum_block_size = get_global_properties().parameters.maximum_block_size;\n   size_t total_block_size = max_block_header_size;\n\n   signed_block pending_block;\n\n   _pending_tx_session = _undo_db.start_undo_session();\n\n   uint64_t postponed_tx_count = 0;\n   for( const processed_transaction& tx : _pending_tx )\n   {\n      size_t new_total_size = total_block_size + fc::raw::pack_size( tx );\n\n      // postpone transaction if it would make block too big\n      if( new_total_size > maximum_block_size )\n      {\n         postponed_tx_count++;\n         continue;\n      }\n\n      try\n      {\n         auto temp_session = _undo_db.start_undo_session();\n         processed_transaction ptx = _apply_transaction( tx );\n\n         // We have to recompute pack_size(ptx) because it may be different\n         // than pack_size(tx) (i.e. if one or more results increased\n         // their size)\n         new_total_size = total_block_size + fc::raw::pack_size( ptx );\n         // postpone transaction if it would make block too big\n         if( new_total_size > maximum_block_size )\n         {\n            postponed_tx_count++;\n            continue;\n         }\n\n         temp_session.merge();\n\n         total_block_size = new_total_size;\n         pending_block.transactions.push_back( ptx );\n      }\n      catch ( const fc::exception& e )\n      {\n         // Do nothing, transaction will not be re-applied\n         wlog( \"Transaction was not processed while generating block due to ${e}\", (\"e\", e) );\n         wlog( \"The transaction was ${t}\", (\"t\", tx) );\n      }\n   }\n   if( postponed_tx_count > 0 )\n   {\n      wlog( \"Postponed ${n} transactions due to block size limit\", (\"n\", postponed_tx_count) );\n   }\n\n   _pending_tx_session.reset();\n\n   // We have temporarily broken the invariant that\n   // _pending_tx_session is the result of applying _pending_tx, as\n   // _pending_tx now consists of the set of postponed transactions.\n   // However, the push_block() call below will re-create the\n   // _pending_tx_session.\n\n   pending_block.previous = head_block_id();\n   pending_block.timestamp = when;\n   pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();\n   pending_block.witness = witness_id;\n\n   if( !(skip & skip_witness_signature) )\n      pending_block.sign( block_signing_private_key );\n\n   push_block( pending_block, skip | skip_transaction_signatures ); // skip authority check when pushing self-generated blocks\n\n   return pending_block;\n} FC_CAPTURE_AND_RETHROW( (witness_id) ) }\n\n/**\n * Removes the most recent block from the database and\n * undoes any changes it made.\n */\nvoid database::pop_block()\n{ try {\n   _pending_tx_session.reset();\n   auto fork_db_head = _fork_db.head();\n   FC_ASSERT( fork_db_head, \"Trying to pop() from empty fork database!?\" );\n   if( fork_db_head->id == head_block_id() )\n      _fork_db.pop_block();\n   else\n   {\n      fork_db_head = _fork_db.fetch_block( head_block_id() );\n      FC_ASSERT( fork_db_head, \"Trying to pop() block that's not in fork database!?\" );\n   }\n   pop_undo();\n   _popped_tx.insert( _popped_tx.begin(), fork_db_head->data.transactions.begin(), fork_db_head->data.transactions.end() );\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid database::clear_pending()\n{ try {\n   assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );\n   _pending_tx.clear();\n   _pending_tx_session.reset();\n} FC_CAPTURE_AND_RETHROW() }\n\nuint32_t database::push_applied_operation( const operation& op )\n{\n   _applied_ops.emplace_back(op);\n   operation_history_object& oh = *(_applied_ops.back());\n   oh.block_num    = _current_block_num;\n   oh.trx_in_block = _current_trx_in_block;\n   oh.op_in_trx    = _current_op_in_trx;\n   oh.virtual_op   = _current_virtual_op++;\n   return _applied_ops.size() - 1;\n}\nvoid database::set_applied_operation_result( uint32_t op_id, const operation_result& result )\n{\n   assert( op_id < _applied_ops.size() );\n   if( _applied_ops[op_id] )\n      _applied_ops[op_id]->result = result;\n   else\n   {\n      elog( \"Could not set operation result (head_block_num=${b})\", (\"b\", head_block_num()) );\n   }\n}\n\nconst vector<optional< operation_history_object > >& database::get_applied_operations() const\n{\n   return _applied_ops;\n}\n\n//////////////////// private methods ////////////////////\n\nvoid database::apply_block( const signed_block& next_block, uint32_t skip )\n{\n   auto block_num = next_block.block_num();\n   if( _checkpoints.size() && _checkpoints.rbegin()->second != block_id_type() )\n   {\n      auto itr = _checkpoints.find( block_num );\n      if( itr != _checkpoints.end() )\n         FC_ASSERT( next_block.id() == itr->second, \"Block did not match checkpoint\", (\"checkpoint\",*itr)(\"block_id\",next_block.id()) );\n\n      if( _checkpoints.rbegin()->first >= block_num )\n         skip = ~0;// WE CAN SKIP ALMOST EVERYTHING\n   }\n\n   detail::with_skip_flags( *this, skip, [&]()\n   {\n      _apply_block( next_block );\n   } );\n   return;\n}\n\nvoid database::_apply_block( const signed_block& next_block )\n{ try {\n   uint32_t next_block_num = next_block.block_num();\n   uint32_t skip = get_node_properties().skip_flags;\n   _applied_ops.clear();\n\n   if( !(skip & skip_block_size_check) )\n   {\n      FC_ASSERT( fc::raw::pack_size(next_block) <= get_global_properties().parameters.maximum_block_size );\n   }\n\n   FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(),\n              \"\",\n              (\"next_block.transaction_merkle_root\",next_block.transaction_merkle_root)\n              (\"calc\",next_block.calculate_merkle_root())\n              (\"next_block\",next_block)\n              (\"id\",next_block.id()) );\n\n   const witness_object& signing_witness = validate_block_header(skip, next_block);\n   const auto& global_props = get_global_properties();\n   const auto& dynamic_global_props = get_dynamic_global_properties();\n   bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp);\n\n   // trx_in_block starts from 0.\n   // For real operations which are explicitly included in a transaction, op_in_trx starts from 0, virtual_op is 0.\n   // For virtual operations that are derived directly from a real operation,\n   //     use the real operation's (block_num,trx_in_block,op_in_trx), virtual_op starts from 1.\n   // For virtual operations created after processed all transactions,\n   //     trx_in_block = the_block.trsanctions.size(), op_in_trx is 0, virtual_op starts from 0.\n   _current_block_num    = next_block_num;\n   _current_trx_in_block = 0;\n\n   _issue_453_affected_assets.clear();\n\n   for( const auto& trx : next_block.transactions )\n   {\n      /* We do not need to push the undo state for each transaction\n       * because they either all apply and are valid or the\n       * entire block fails to apply.  We only need an \"undo\" state\n       * for transactions when validating broadcast transactions or\n       * when building a block.\n       */\n      apply_transaction( trx, skip );\n      ++_current_trx_in_block;\n   }\n\n   _current_op_in_trx    = 0;\n   _current_virtual_op   = 0;\n\n   const uint32_t missed = update_witness_missed_blocks( next_block );\n   update_global_dynamic_data( next_block, missed );\n   update_signing_witness(signing_witness, next_block);\n   update_last_irreversible_block();\n\n   process_tickets();\n\n   // Are we at the maintenance interval?\n   if( maint_needed )\n      perform_chain_maintenance(next_block, global_props);\n\n   create_block_summary(next_block);\n   clear_expired_transactions();\n   clear_expired_proposals();\n   clear_expired_orders();\n   clear_expired_htlcs();\n   update_expired_feeds();       // this will update expired feeds and some core exchange rates\n   update_core_exchange_rates(); // this will update remaining core exchange rates\n   update_withdraw_permissions();\n\n   // n.b., update_maintenance_flag() happens this late\n   // because get_slot_time() / get_slot_at_time() is needed above\n   // TODO:  figure out if we could collapse this function into\n   // update_global_dynamic_data() as perhaps these methods only need\n   // to be called for header validation?\n   update_maintenance_flag( maint_needed );\n   update_witness_schedule();\n   if( !_node_property_object.debug_updates.empty() )\n      apply_debug_updates();\n\n   // notify observers that the block has been applied\n   notify_applied_block( next_block ); //emit\n   _applied_ops.clear();\n\n   notify_changed_objects();\n} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) )  }\n\n\n\nprocessed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip)\n{\n   processed_transaction result;\n   detail::with_skip_flags( *this, skip, [&]()\n   {\n      result = _apply_transaction(trx);\n   });\n   return result;\n}\n\nprocessed_transaction database::_apply_transaction(const signed_transaction& trx)\n{ try {\n   uint32_t skip = get_node_properties().skip_flags;\n\n   trx.validate();\n\n   auto& trx_idx = get_mutable_index_type<transaction_index>();\n   const chain_id_type& chain_id = get_chain_id();\n   if( !(skip & skip_transaction_dupe_check) )\n   {\n      GRAPHENE_ASSERT( trx_idx.indices().get<by_trx_id>().find(trx.id()) == trx_idx.indices().get<by_trx_id>().end(),\n                       duplicate_transaction,\n                       \"Transaction '${txid}' is already in the database\",\n                       (\"txid\",trx.id()) );\n   }\n   transaction_evaluation_state eval_state(this);\n   const chain_parameters& chain_parameters = get_global_properties().parameters;\n   eval_state._trx = &trx;\n\n   if( !(skip & skip_transaction_signatures) )\n   {\n      bool allow_non_immediate_owner = ( head_block_time() >= HARDFORK_CORE_584_TIME );\n      auto get_active = [this]( account_id_type id ) { return &id(*this).active; };\n      auto get_owner  = [this]( account_id_type id ) { return &id(*this).owner;  };\n      auto get_custom = [this]( account_id_type id, const operation& op, rejected_predicate_map* rejects ) {\n         return get_viable_custom_authorities(id, op, rejects);\n      };\n\n      trx.verify_authority(chain_id, get_active, get_owner, get_custom, allow_non_immediate_owner,\n                           MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()),\n                           get_global_properties().parameters.max_authority_depth);\n   }\n\n   //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is\n   //expired, and TaPoS makes no sense as no blocks exist.\n   if( BOOST_LIKELY(head_block_num() > 0) )\n   {\n      if( !(skip & skip_tapos_check) )\n      {\n         const auto& tapos_block_summary = block_summary_id_type( trx.ref_block_num )(*this);\n\n         //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration\n         FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1].value() );\n      }\n\n      fc::time_point_sec now = head_block_time();\n\n      FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, \"\",\n                 (\"trx.expiration\",trx.expiration)(\"now\",now)(\"max_til_exp\",chain_parameters.maximum_time_until_expiration));\n      FC_ASSERT( now <= trx.expiration, \"\", (\"now\",now)(\"trx.exp\",trx.expiration) );\n      if ( !(skip & skip_block_size_check ) ) // don't waste time on replay\n         FC_ASSERT( head_block_time() <= HARDFORK_CORE_1573_TIME\n               || trx.get_packed_size() <= chain_parameters.maximum_transaction_size,\n               \"Transaction exceeds maximum transaction size.\" );\n   }\n\n   //Insert transaction into unique transactions database.\n   if( !(skip & skip_transaction_dupe_check) )\n   {\n      create<transaction_history_object>([&trx](transaction_history_object& transaction) {\n         transaction.trx_id = trx.id();\n         transaction.trx = trx;\n      });\n   }\n\n   eval_state.operation_results.reserve(trx.operations.size());\n\n   //Finally process the operations\n   processed_transaction ptrx(trx);\n   _current_op_in_trx = 0;\n   for( const auto& op : ptrx.operations )\n   {\n      _current_virtual_op = 0;\n      eval_state.operation_results.emplace_back(apply_operation(eval_state, op));\n      ++_current_op_in_trx;\n   }\n   ptrx.operation_results = std::move(eval_state.operation_results);\n\n   return ptrx;\n} FC_CAPTURE_AND_RETHROW( (trx) ) }\n\noperation_result database::apply_operation(transaction_evaluation_state& eval_state, const operation& op)\n{ try {\n   int i_which = op.which();\n   uint64_t u_which = uint64_t( i_which );\n   FC_ASSERT( i_which >= 0, \"Negative operation tag in operation ${op}\", (\"op\",op) );\n   FC_ASSERT( u_which < _operation_evaluators.size(), \"No registered evaluator for operation ${op}\", (\"op\",op) );\n   unique_ptr<op_evaluator>& eval = _operation_evaluators[ u_which ];\n   FC_ASSERT( eval, \"No registered evaluator for operation ${op}\", (\"op\",op) );\n   auto op_id = push_applied_operation( op );\n   auto result = eval->evaluate( eval_state, op, true );\n   set_applied_operation_result( op_id, result );\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nconst witness_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const\n{\n   FC_ASSERT( head_block_id() == next_block.previous, \"\", (\"head_block_id\",head_block_id())(\"next.prev\",next_block.previous) );\n   FC_ASSERT( head_block_time() < next_block.timestamp, \"\", (\"head_block_time\",head_block_time())(\"next\",next_block.timestamp)(\"blocknum\",next_block.block_num()) );\n   const witness_object& witness = next_block.witness(*this);\n\n   if( !(skip&skip_witness_signature) ) \n      FC_ASSERT( next_block.validate_signee( witness.signing_key ) );\n\n   if( !(skip&skip_witness_schedule_check) )\n   {\n      uint32_t slot_num = get_slot_at_time( next_block.timestamp );\n      FC_ASSERT( slot_num > 0 );\n\n      witness_id_type scheduled_witness = get_scheduled_witness( slot_num );\n\n      FC_ASSERT( next_block.witness == scheduled_witness, \"Witness produced block at wrong time\",\n                 (\"block witness\",next_block.witness)(\"scheduled\",scheduled_witness)(\"slot_num\",slot_num) );\n   }\n\n   return witness;\n}\n\nvoid database::create_block_summary(const signed_block& next_block)\n{\n   block_summary_id_type sid(next_block.block_num() & 0xffff );\n   modify( sid(*this), [&](block_summary_object& p) {\n         p.block_id = next_block.id();\n   });\n}\n\nvoid database::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts )\n{\n   for( const auto& i : checkpts )\n      _checkpoints[i.first] = i.second;\n}\n\nbool database::before_last_checkpoint()const\n{\n   return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());\n}\n\n\nstatic const uint32_t skip_expensive = database::skip_transaction_signatures | database::skip_witness_signature\n                                       | database::skip_merkle_check | database::skip_transaction_dupe_check;\n\ntemplate<typename Trx>\nvoid database::_precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const\n{\n   for( size_t i = 0; i < count; ++i, ++trx )\n   {\n      trx->validate(); // TODO - parallelize wrt confidential operations\n      if ( !(skip & skip_block_size_check) )\n         trx->get_packed_size();\n      if( !(skip&skip_transaction_dupe_check) )\n         trx->id();\n      if( !(skip&skip_transaction_signatures) )\n         trx->get_signature_keys( get_chain_id() );\n   }\n}\n\nfc::future<void> database::precompute_parallel( const signed_block& block, const uint32_t skip )const\n{ try {\n   std::vector<fc::future<void>> workers;\n   if( !block.transactions.empty() )\n   {\n      if( (skip & skip_expensive) == skip_expensive )\n         _precompute_parallel( &block.transactions[0], block.transactions.size(), skip );\n      else\n      {\n         uint32_t chunks = fc::asio::default_io_service_scope::get_num_threads();\n         uint32_t chunk_size = ( block.transactions.size() + chunks - 1 ) / chunks;\n         workers.reserve( chunks + 1 );\n         for( size_t base = 0; base < block.transactions.size(); base += chunk_size )\n            workers.push_back( fc::do_parallel( [this,&block,base,chunk_size,skip] () {\n               _precompute_parallel( &block.transactions[base],\n                                     base + chunk_size < block.transactions.size() ? chunk_size : block.transactions.size() - base,\n                                     skip );\n            }) );\n      }\n   }\n\n   if( !(skip&skip_witness_signature) )\n      workers.push_back( fc::do_parallel( [&block] () { block.signee(); } ) );\n   if( !(skip&skip_merkle_check) )\n      block.calculate_merkle_root();\n   block.id();\n\n   if( workers.empty() )\n      return fc::future< void >( fc::promise< void >::create( true ) );\n\n   auto first = workers.begin();\n   auto worker = first;\n   while( ++worker != workers.end() )\n      worker->wait();\n   return *first;\n} FC_LOG_AND_RETHROW() }\n\nfc::future<void> database::precompute_parallel( const precomputable_transaction& trx )const\n{\n   return fc::do_parallel([this,&trx] () {\n      _precompute_parallel( &trx, 1, skip_nothing );\n   });\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_debug.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n *  This method dumps the state of the blockchain in a semi-human readable form for the\n *  purpose of tracking down funds and mismatches in currency allocation\n */\nvoid database::debug_dump()\n{\n   const auto& db = *this;\n   const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db);\n\n   const auto& balance_index = db.get_index_type<account_balance_index>().indices();\n   const auto& statistics_index = db.get_index_type<account_stats_index>().indices();\n   const auto& bids = db.get_index_type<collateral_bid_index>().indices();\n   const auto& settle_index = db.get_index_type<force_settlement_index>().indices();\n   const auto& htlcs = db.get_index_type<htlc_index>().indices();\n   map<asset_id_type,share_type> total_balances;\n   map<asset_id_type,share_type> total_debts;\n   share_type core_in_orders;\n   share_type reported_core_in_orders;\n\n   for( const account_balance_object& a : balance_index )\n   {\n    //  idump((\"balance\")(a));\n      total_balances[a.asset_type] += a.balance;\n   }\n   for( const force_settlement_object& s : settle_index )\n   {\n      total_balances[s.balance.asset_id] += s.balance.amount;\n   }\n   for( const vesting_balance_object& vbo : db.get_index_type< vesting_balance_index >().indices() )\n      total_balances[ vbo.balance.asset_id ] += vbo.balance.amount;\n   for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() )\n      total_balances[ asset_id_type() ] += fba.accumulated_fba_fees;\n   for( const account_statistics_object& s : statistics_index )\n   {\n    //  idump((\"statistics\")(s));\n      reported_core_in_orders += s.total_core_in_orders;\n   }\n   for( const collateral_bid_object& b : bids )\n      total_balances[b.inv_swan_price.base.asset_id] += b.inv_swan_price.base.amount;\n   for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )\n   {\n //     idump((\"limit_order\")(o));\n      auto for_sale = o.amount_for_sale();\n      if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount;\n      total_balances[for_sale.asset_id] += for_sale.amount;\n   }\n   for( const call_order_object& o : db.get_index_type<call_order_index>().indices() )\n   {\n//      idump((\"call_order\")(o));\n      auto col = o.get_collateral();\n      if( col.asset_id == asset_id_type() ) core_in_orders += col.amount;\n      total_balances[col.asset_id] += col.amount;\n      total_debts[o.get_debt().asset_id] += o.get_debt().amount;\n   }\n   for( const asset_object& asset_obj : db.get_index_type<asset_index>().indices() )\n   {\n      total_balances[asset_obj.id] += asset_obj.dynamic_asset_data_id(db).accumulated_fees;\n      total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;\n//      edump((total_balances[asset_obj.id])(asset_obj.dynamic_asset_data_id(db).current_supply ) );\n   }\n   for( const auto& htlc : htlcs )\n      total_balances[htlc.transfer.asset_id] += htlc.transfer.amount;\n\n   if( total_balances[asset_id_type()].value != core_asset_data.current_supply.value )\n   {\n      FC_THROW( \"computed balance of CORE mismatch\",\n                (\"computed value\",total_balances[asset_id_type()].value)\n                (\"current supply\",core_asset_data.current_supply.value) );\n   }\n\n\n   /*\n   const auto& vbidx = db.get_index_type<simple_index<vesting_balance_object>>();\n   for( const auto& s : vbidx )\n   {\n//      idump((\"vesting_balance\")(s));\n   }\n   */\n}\n\nvoid debug_apply_update( database& db, const fc::variant_object& vo )\n{\n   static const uint8_t\n      db_action_nil = 0,\n      db_action_create = 1,\n      db_action_write = 2,\n      db_action_update = 3,\n      db_action_delete = 4;\n\n   // \"_action\" : \"create\"   object must not exist, unspecified fields take defaults\n   // \"_action\" : \"write\"    object may exist, is replaced entirely, unspecified fields take defaults\n   // \"_action\" : \"update\"   object must exist, unspecified fields don't change\n   // \"_action\" : \"delete\"   object must exist, will be deleted\n\n   // if _action is unspecified:\n   // - delete if object contains only ID field\n   // - otherwise, write\n\n   object_id_type oid;\n   uint8_t action = db_action_nil;\n   auto it_id = vo.find(\"id\");\n   FC_ASSERT( it_id != vo.end() );\n\n   from_variant( it_id->value(), oid );\n   action = ( vo.size() == 1 ) ? db_action_delete : db_action_write;\n\n   from_variant( vo[\"id\"], oid );\n   if( vo.size() == 1 )\n      action = db_action_delete;\n   auto it_action = vo.find(\"_action\" );\n   if( it_action != vo.end() )\n   {\n      const std::string& str_action = it_action->value().get_string();\n      if( str_action == \"create\" )\n         action = db_action_create;\n      else if( str_action == \"write\" )\n         action = db_action_write;\n      else if( str_action == \"update\" )\n         action = db_action_update;\n      else if( str_action == \"delete\" )\n         action = db_action_delete;\n   }\n\n   auto& idx = db.get_index( oid );\n\n   switch( action )\n   {\n      case db_action_create:\n         FC_ASSERT( false );\n         break;\n      case db_action_write:\n         db.modify( db.get_object( oid ), [&]( object& obj )\n         {\n            idx.object_default( obj );\n            idx.object_from_variant( vo, obj, GRAPHENE_MAX_NESTED_OBJECTS );\n         } );\n         break;\n      case db_action_update:\n         db.modify( db.get_object( oid ), [&]( object& obj )\n         {\n            idx.object_from_variant( vo, obj, GRAPHENE_MAX_NESTED_OBJECTS );\n         } );\n         break;\n      case db_action_delete:\n         db.remove( db.get_object( oid ) );\n         break;\n      default:\n         FC_ASSERT( false );\n   }\n}\n\nvoid database::apply_debug_updates()\n{\n   block_id_type head_id = head_block_id();\n   auto it = _node_property_object.debug_updates.find( head_id );\n   if( it == _node_property_object.debug_updates.end() )\n      return;\n   for( const fc::variant_object& update : it->second )\n      debug_apply_update( *this, update );\n}\n\nvoid database::debug_update( const fc::variant_object& update )\n{\n   block_id_type head_id = head_block_id();\n   auto it = _node_property_object.debug_updates.find( head_id );\n   if( it == _node_property_object.debug_updates.end() )\n      it = _node_property_object.debug_updates.emplace( head_id, std::vector< fc::variant_object >() ).first;\n   it->second.emplace_back( update );\n\n   optional<signed_block> head_block = fetch_block_by_id( head_id );\n   FC_ASSERT( head_block.valid() );\n\n   // What the last block does has been changed by adding to node_property_object, so we have to re-apply it\n   pop_block();\n   push_block( *head_block );\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_getter.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/chain_property_object.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n\nnamespace graphene { namespace chain {\n\nconst asset_object& database::get_core_asset() const\n{\n   return *_p_core_asset_obj;\n}\n\nconst asset_dynamic_data_object& database::get_core_dynamic_data() const\n{\n   return *_p_core_dynamic_data_obj;\n}\n\nconst global_property_object& database::get_global_properties()const\n{\n   return *_p_global_prop_obj;\n}\n\nconst chain_property_object& database::get_chain_properties()const\n{\n   return *_p_chain_property_obj;\n}\n\nconst dynamic_global_property_object& database::get_dynamic_global_properties() const\n{\n   return *_p_dyn_global_prop_obj;\n}\n\nconst fee_schedule&  database::current_fee_schedule()const\n{\n   return get_global_properties().parameters.get_current_fees();\n}\n\ntime_point_sec database::head_block_time()const\n{\n   return get_dynamic_global_properties().time;\n}\n\nuint32_t database::head_block_num()const\n{\n   return get_dynamic_global_properties().head_block_number;\n}\n\nblock_id_type database::head_block_id()const\n{\n   return get_dynamic_global_properties().head_block_id;\n}\n\ndecltype( chain_parameters::block_interval ) database::block_interval( )const\n{\n   return get_global_properties().parameters.block_interval;\n}\n\nconst chain_id_type& database::get_chain_id( )const\n{\n   return get_chain_properties().chain_id;\n}\n\nconst node_property_object& database::get_node_properties()const\n{\n   return _node_property_object;\n}\n\nnode_property_object& database::node_properties()\n{\n   return _node_property_object;\n}\n\nvector<authority> database::get_viable_custom_authorities(\n      account_id_type account, const operation &op,\n      rejected_predicate_map* rejected_authorities) const\n{\n   const auto& index = get_index_type<custom_authority_index>().indices().get<by_account_custom>();\n   auto range = index.equal_range(boost::make_tuple(account, unsigned_int(op.which()), true));\n\n   auto is_valid = [now=head_block_time()](const custom_authority_object& auth) { return auth.is_valid(now); };\n   vector<std::reference_wrapper<const custom_authority_object>> valid_auths;\n   std::copy_if(range.first, range.second, std::back_inserter(valid_auths), is_valid);\n\n   vector<authority> results;\n   for (const auto& cust_auth : valid_auths) {\n      try {\n         auto result = cust_auth.get().get_predicate()(op);\n         if (result.success)\n            results.emplace_back(cust_auth.get().auth);\n         else if (rejected_authorities != nullptr)\n            rejected_authorities->insert(std::make_pair(cust_auth.get().id, std::move(result)));\n      } catch (fc::exception& e) {\n         if (rejected_authorities != nullptr)\n            rejected_authorities->insert(std::make_pair(cust_auth.get().id, std::move(e)));\n      }\n   }\n\n   return results;\n}\n\nuint32_t database::last_non_undoable_block_num() const\n{\n   //see https://github.com/bitshares/bitshares-core/issues/377\n   /*\n   There is a case when a value of undo_db.size() is greater then head_block_num(),\n   and as result we get a wrong value for last_non_undoable_block_num.\n   To resolve it we should take into account a number of active_sessions in calculations of\n   last_non_undoable_block_num (active sessions are related to a new block which is under generation).\n   */\n   return head_block_num() - ( _undo_db.size() - _undo_db.active_sessions() );\n}\n\nconst account_statistics_object& database::get_account_stats_by_owner( account_id_type owner )const\n{\n   return account_statistics_id_type(owner.instance)(*this);\n}\n\nconst witness_schedule_object& database::get_witness_schedule_object()const\n{\n   return *_p_witness_schedule_obj;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_init.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/fba_accumulator_id.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/block_summary_object.hpp>\n#include <graphene/chain/budget_record_object.hpp>\n#include <graphene/chain/buyback_object.hpp>\n#include <graphene/chain/chain_property_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/liquidity_pool_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/special_authority_object.hpp>\n#include <graphene/chain/ticket_object.hpp>\n#include <graphene/chain/transaction_history_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/witness_schedule_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n\n#include <graphene/chain/account_evaluator.hpp>\n#include <graphene/chain/asset_evaluator.hpp>\n#include <graphene/chain/assert_evaluator.hpp>\n#include <graphene/chain/balance_evaluator.hpp>\n#include <graphene/chain/committee_member_evaluator.hpp>\n#include <graphene/chain/confidential_evaluator.hpp>\n#include <graphene/chain/custom_evaluator.hpp>\n#include <graphene/chain/liquidity_pool_evaluator.hpp>\n#include <graphene/chain/market_evaluator.hpp>\n#include <graphene/chain/proposal_evaluator.hpp>\n#include <graphene/chain/ticket_evaluator.hpp>\n#include <graphene/chain/transfer_evaluator.hpp>\n#include <graphene/chain/vesting_balance_evaluator.hpp>\n#include <graphene/chain/withdraw_permission_evaluator.hpp>\n#include <graphene/chain/witness_evaluator.hpp>\n#include <graphene/chain/worker_evaluator.hpp>\n#include <graphene/chain/htlc_evaluator.hpp>\n#include <graphene/chain/custom_authority_evaluator.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include <boost/algorithm/string.hpp>\n\nnamespace graphene { namespace chain {\n\n// C++ requires that static class variables declared and initialized\n// in headers must also have a definition in a single source file,\n// else linker errors will occur [1].\n//\n// The purpose of this source file is to collect such definitions in\n// a single place.\n//\n// [1] http://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char\n\nconst uint8_t account_object::space_id;\nconst uint8_t account_object::type_id;\n\nconst uint8_t asset_object::space_id;\nconst uint8_t asset_object::type_id;\n\nconst uint8_t block_summary_object::space_id;\nconst uint8_t block_summary_object::type_id;\n\nconst uint8_t call_order_object::space_id;\nconst uint8_t call_order_object::type_id;\n\nconst uint8_t committee_member_object::space_id;\nconst uint8_t committee_member_object::type_id;\n\nconst uint8_t force_settlement_object::space_id;\nconst uint8_t force_settlement_object::type_id;\n\nconst uint8_t global_property_object::space_id;\nconst uint8_t global_property_object::type_id;\n\nconst uint8_t limit_order_object::space_id;\nconst uint8_t limit_order_object::type_id;\n\nconst uint8_t operation_history_object::space_id;\nconst uint8_t operation_history_object::type_id;\n\nconst uint8_t proposal_object::space_id;\nconst uint8_t proposal_object::type_id;\n\nconst uint8_t transaction_history_object::space_id;\nconst uint8_t transaction_history_object::type_id;\n\nconst uint8_t vesting_balance_object::space_id;\nconst uint8_t vesting_balance_object::type_id;\n\nconst uint8_t withdraw_permission_object::space_id;\nconst uint8_t withdraw_permission_object::type_id;\n\nconst uint8_t witness_object::space_id;\nconst uint8_t witness_object::type_id;\n\nconst uint8_t worker_object::space_id;\nconst uint8_t worker_object::type_id;\n\nconst uint8_t htlc_object::space_id;\nconst uint8_t htlc_object::type_id;\n\nconst uint8_t custom_authority_object::space_id;\nconst uint8_t custom_authority_object::type_id;\n\nconst uint8_t ticket_object::space_id;\nconst uint8_t ticket_object::type_id;\n\nvoid database::initialize_evaluators()\n{\n   _operation_evaluators.resize(255);\n   register_evaluator<account_create_evaluator>();\n   register_evaluator<account_update_evaluator>();\n   register_evaluator<account_upgrade_evaluator>();\n   register_evaluator<account_whitelist_evaluator>();\n   register_evaluator<committee_member_create_evaluator>();\n   register_evaluator<committee_member_update_evaluator>();\n   register_evaluator<committee_member_update_global_parameters_evaluator>();\n   register_evaluator<custom_evaluator>();\n   register_evaluator<asset_create_evaluator>();\n   register_evaluator<asset_issue_evaluator>();\n   register_evaluator<asset_reserve_evaluator>();\n   register_evaluator<asset_update_evaluator>();\n   register_evaluator<asset_update_bitasset_evaluator>();\n   register_evaluator<asset_update_feed_producers_evaluator>();\n   register_evaluator<asset_settle_evaluator>();\n   register_evaluator<asset_global_settle_evaluator>();\n   register_evaluator<assert_evaluator>();\n   register_evaluator<limit_order_create_evaluator>();\n   register_evaluator<limit_order_cancel_evaluator>();\n   register_evaluator<call_order_update_evaluator>();\n   register_evaluator<bid_collateral_evaluator>();\n   register_evaluator<transfer_evaluator>();\n   register_evaluator<override_transfer_evaluator>();\n   register_evaluator<asset_fund_fee_pool_evaluator>();\n   register_evaluator<asset_publish_feeds_evaluator>();\n   register_evaluator<proposal_create_evaluator>();\n   register_evaluator<proposal_update_evaluator>();\n   register_evaluator<proposal_delete_evaluator>();\n   register_evaluator<vesting_balance_create_evaluator>();\n   register_evaluator<vesting_balance_withdraw_evaluator>();\n   register_evaluator<witness_create_evaluator>();\n   register_evaluator<witness_update_evaluator>();\n   register_evaluator<withdraw_permission_create_evaluator>();\n   register_evaluator<withdraw_permission_claim_evaluator>();\n   register_evaluator<withdraw_permission_update_evaluator>();\n   register_evaluator<withdraw_permission_delete_evaluator>();\n   register_evaluator<worker_create_evaluator>();\n   register_evaluator<balance_claim_evaluator>();\n   register_evaluator<transfer_to_blind_evaluator>();\n   register_evaluator<transfer_from_blind_evaluator>();\n   register_evaluator<blind_transfer_evaluator>();\n   register_evaluator<asset_claim_fees_evaluator>();\n   register_evaluator<asset_update_issuer_evaluator>();\n   register_evaluator<asset_claim_pool_evaluator>();\n   register_evaluator<htlc_create_evaluator>();\n   register_evaluator<htlc_redeem_evaluator>();\n   register_evaluator<htlc_extend_evaluator>();\n   register_evaluator<custom_authority_create_evaluator>();\n   register_evaluator<custom_authority_update_evaluator>();\n   register_evaluator<custom_authority_delete_evaluator>();\n   register_evaluator<ticket_create_evaluator>();\n   register_evaluator<ticket_update_evaluator>();\n   register_evaluator<liquidity_pool_create_evaluator>();\n   register_evaluator<liquidity_pool_delete_evaluator>();\n   register_evaluator<liquidity_pool_deposit_evaluator>();\n   register_evaluator<liquidity_pool_withdraw_evaluator>();\n   register_evaluator<liquidity_pool_exchange_evaluator>();\n}\n\nvoid database::initialize_indexes()\n{\n   reset_indexes();\n   _undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY );\n\n   //Protocol object indexes\n   add_index< primary_index<asset_index, 13> >(); // 8192 assets per chunk\n   add_index< primary_index<force_settlement_index> >();\n\n   add_index< primary_index<account_index, 20> >(); // ~1 million accounts per chunk\n   add_index< primary_index<committee_member_index, 8> >(); // 256 members per chunk\n   add_index< primary_index<witness_index, 10> >(); // 1024 witnesses per chunk\n   add_index< primary_index<limit_order_index > >();\n   add_index< primary_index<call_order_index > >();\n   add_index< primary_index<proposal_index > >();\n   add_index< primary_index<withdraw_permission_index > >();\n   add_index< primary_index<vesting_balance_index> >();\n   add_index< primary_index<worker_index> >();\n   add_index< primary_index<balance_index> >();\n   add_index< primary_index<blinded_balance_index> >();\n   add_index< primary_index< htlc_index> >();\n   add_index< primary_index< custom_authority_index> >();\n   add_index< primary_index<ticket_index> >();\n   add_index< primary_index<liquidity_pool_index> >();\n\n   //Implementation object indexes\n   add_index< primary_index<transaction_index                             > >();\n\n   auto bal_idx = add_index< primary_index<account_balance_index          > >();\n   bal_idx->add_secondary_index<balances_by_account_index>();\n\n   add_index< primary_index<asset_bitasset_data_index,                 13 > >(); // 8192\n   add_index< primary_index<simple_index<global_property_object          >> >();\n   add_index< primary_index<simple_index<dynamic_global_property_object  >> >();\n   add_index< primary_index<account_stats_index,                       20 > >(); // 1 Mi\n   add_index< primary_index<simple_index<asset_dynamic_data_object       >> >();\n   add_index< primary_index<simple_index<block_summary_object            >> >();\n   add_index< primary_index<simple_index<chain_property_object          > > >();\n   add_index< primary_index<simple_index<witness_schedule_object        > > >();\n   add_index< primary_index<simple_index<budget_record_object           > > >();\n   add_index< primary_index< special_authority_index                      > >();\n   add_index< primary_index< buyback_index                                > >();\n   add_index< primary_index<collateral_bid_index                          > >();\n   add_index< primary_index< simple_index< fba_accumulator_object       > > >();\n}\n\nvoid database::init_genesis(const genesis_state_type& genesis_state)\n{ try {\n   FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), \"Must initialize genesis timestamp.\" );\n   FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % GRAPHENE_DEFAULT_BLOCK_INTERVAL == 0,\n              \"Genesis timestamp must be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL.\" );\n   FC_ASSERT(genesis_state.initial_witness_candidates.size() > 0,\n             \"Cannot start a chain with zero witnesses.\");\n   FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(),\n             \"initial_active_witnesses is larger than the number of candidate witnesses.\");\n\n   _undo_db.disable();\n   struct auth_inhibitor {\n      auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)\n      { db.node_properties().skip_flags |= skip_transaction_signatures; }\n      ~auth_inhibitor()\n      { db.node_properties().skip_flags = old_flags; }\n   private:\n      database& db;\n      uint32_t old_flags;\n   } inhibitor(*this);\n\n   transaction_evaluation_state genesis_eval_state(this);\n\n   // Create blockchain accounts\n   fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")));\n   create<account_balance_object>([](account_balance_object& b) {\n      b.balance = GRAPHENE_MAX_SHARE_SUPPLY;\n   });\n   const account_object& committee_account =\n      create<account_object>( [&](account_object& n) {\n         n.membership_expiration_date = time_point_sec::maximum();\n         n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n         n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n         n.owner.weight_threshold = 1;\n         n.active.weight_threshold = 1;\n         n.name = \"committee-account\";\n         n.statistics = create<account_statistics_object>( [&n](account_statistics_object& s){\n                           s.owner = n.id;\n                           s.name = n.name;\n                           s.core_in_balance = GRAPHENE_MAX_SHARE_SUPPLY;\n                        }).id;\n      });\n   FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT);\n   FC_ASSERT(create<account_object>([this](account_object& a) {\n       a.name = \"witness-account\";\n       a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){\n                         s.owner = a.id;\n                         s.name = a.name;\n                      }).id;\n       a.owner.weight_threshold = 1;\n       a.active.weight_threshold = 1;\n       a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT;\n       a.membership_expiration_date = time_point_sec::maximum();\n       a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n       a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n   }).get_id() == GRAPHENE_WITNESS_ACCOUNT);\n   FC_ASSERT(create<account_object>([this](account_object& a) {\n       a.name = \"relaxed-committee-account\";\n       a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){\n                         s.owner = a.id;\n                         s.name = a.name;\n                      }).id;\n       a.owner.weight_threshold = 1;\n       a.active.weight_threshold = 1;\n       a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;\n       a.membership_expiration_date = time_point_sec::maximum();\n       a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n       a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n   }).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT);\n   FC_ASSERT(create<account_object>([this](account_object& a) {\n       a.name = \"null-account\";\n       a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){\n                         s.owner = a.id;\n                         s.name = a.name;\n                      }).id;\n       a.owner.weight_threshold = 1;\n       a.active.weight_threshold = 1;\n       a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT;\n       a.membership_expiration_date = time_point_sec::maximum();\n       a.network_fee_percentage = 0;\n       a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;\n   }).get_id() == GRAPHENE_NULL_ACCOUNT);\n   FC_ASSERT(create<account_object>([this](account_object& a) {\n       a.name = \"temp-account\";\n       a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){\n                         s.owner = a.id;\n                         s.name = a.name;\n                      }).id;\n       a.owner.weight_threshold = 0;\n       a.active.weight_threshold = 0;\n       a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT;\n       a.membership_expiration_date = time_point_sec::maximum();\n       a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n       a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n   }).get_id() == GRAPHENE_TEMP_ACCOUNT);\n   FC_ASSERT(create<account_object>([this](account_object& a) {\n       a.name = \"proxy-to-self\";\n       a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){\n                         s.owner = a.id;\n                         s.name = a.name;\n                      }).id;\n       a.owner.weight_threshold = 1;\n       a.active.weight_threshold = 1;\n       a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT;\n       a.membership_expiration_date = time_point_sec::maximum();\n       a.network_fee_percentage = 0;\n       a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;\n   }).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT);\n\n   // Create more special accounts\n   while( true )\n   {\n      uint64_t id = get_index<account_object>().get_next_id().instance();\n      if( id >= genesis_state.immutable_parameters.num_special_accounts )\n         break;\n      const account_object& acct = create<account_object>([this,id](account_object& a) {\n          a.name = \"special-account-\" + std::to_string(id);\n          a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){\n                            s.owner = a.id;\n                            s.name = a.name;\n                         }).id;\n          a.owner.weight_threshold = 1;\n          a.active.weight_threshold = 1;\n          a.registrar = a.lifetime_referrer = a.referrer = account_id_type(id);\n          a.membership_expiration_date = time_point_sec::maximum();\n          a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n          a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n      });\n      FC_ASSERT( acct.get_id() == account_id_type(id) );\n      remove( acct );\n   }\n\n   // Create core asset\n   const asset_dynamic_data_object& dyn_asset =\n      create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {\n         a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      });\n   const asset_object& core_asset =\n     create<asset_object>( [&genesis_state,&dyn_asset]( asset_object& a ) {\n         a.symbol = GRAPHENE_SYMBOL;\n         a.options.max_supply = genesis_state.max_core_supply;\n         a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;\n         a.options.flags = 0;\n         a.options.issuer_permissions = 0;\n         a.issuer = GRAPHENE_NULL_ACCOUNT;\n         a.options.core_exchange_rate.base.amount = 1;\n         a.options.core_exchange_rate.base.asset_id = asset_id_type(0);\n         a.options.core_exchange_rate.quote.amount = 1;\n         a.options.core_exchange_rate.quote.asset_id = asset_id_type(0);\n         a.dynamic_asset_data_id = dyn_asset.id;\n      });\n   FC_ASSERT( dyn_asset.id == asset_dynamic_data_id_type() );\n   FC_ASSERT( asset_id_type(core_asset.id) == asset().asset_id );\n   FC_ASSERT( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) );\n   _p_core_asset_obj = &core_asset;\n   _p_core_dynamic_data_obj = &dyn_asset;\n   // Create more special assets\n   while( true )\n   {\n      uint64_t id = get_index<asset_object>().get_next_id().instance();\n      if( id >= genesis_state.immutable_parameters.num_special_assets )\n         break;\n      const asset_dynamic_data_object& dyn_asset =\n         create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {\n            a.current_supply = 0;\n         });\n      const asset_object& asset_obj = create<asset_object>( [id,&dyn_asset]( asset_object& a ) {\n         a.symbol = \"SPECIAL\" + std::to_string( id );\n         a.options.max_supply = 0;\n         a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;\n         a.options.flags = 0;\n         a.options.issuer_permissions = 0;\n         a.issuer = GRAPHENE_NULL_ACCOUNT;\n         a.options.core_exchange_rate.base.amount = 1;\n         a.options.core_exchange_rate.base.asset_id = asset_id_type(0);\n         a.options.core_exchange_rate.quote.amount = 1;\n         a.options.core_exchange_rate.quote.asset_id = asset_id_type(0);\n         a.dynamic_asset_data_id = dyn_asset.id;\n      });\n      FC_ASSERT( asset_obj.get_id() == asset_id_type(id) );\n      remove( asset_obj );\n   }\n\n   chain_id_type chain_id = genesis_state.compute_chain_id();\n\n   // Create global properties\n   _p_global_prop_obj = & create<global_property_object>([&genesis_state](global_property_object& p) {\n       p.parameters = genesis_state.initial_parameters;\n       // Set fees to zero initially, so that genesis initialization needs not pay them\n       // We'll fix it at the end of the function\n       p.parameters.get_mutable_fees().zero_all_fees();\n\n   });\n   _p_dyn_global_prop_obj = & create<dynamic_global_property_object>([&genesis_state](dynamic_global_property_object& p) {\n      p.time = genesis_state.initial_timestamp;\n      p.dynamic_flags = 0;\n      p.witness_budget = 0;\n      p.recent_slots_filled = std::numeric_limits<fc::uint128_t>::max();\n   });\n\n   FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, \"min_witness_count must be odd\" );\n   FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, \"min_committee_member_count must be odd\" );\n\n   _p_chain_property_obj = & create<chain_property_object>([chain_id,&genesis_state](chain_property_object& p)\n   {\n      p.chain_id = chain_id;\n      p.immutable_parameters = genesis_state.immutable_parameters;\n   } );\n   for (uint32_t i = 0; i <= 0x10000; i++)\n      create<block_summary_object>( [&]( block_summary_object&) {});\n\n   // Create initial accounts\n   for( const auto& account : genesis_state.initial_accounts )\n   {\n      account_create_operation cop;\n      cop.name = account.name;\n      cop.registrar = GRAPHENE_TEMP_ACCOUNT;\n      cop.owner = authority(1, account.owner_key, 1);\n      if( account.active_key == public_key_type() )\n      {\n         cop.active = cop.owner;\n         cop.options.memo_key = account.owner_key;\n      }\n      else\n      {\n         cop.active = authority(1, account.active_key, 1);\n         cop.options.memo_key = account.active_key;\n      }\n      account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());\n\n      if( account.is_lifetime_member )\n      {\n          account_upgrade_operation op;\n          op.account_to_upgrade = account_id;\n          op.upgrade_to_lifetime_member = true;\n          apply_operation(genesis_eval_state, op);\n      }\n   }\n\n   // Helper function to get account ID by name\n   const auto& accounts_by_name = get_index_type<account_index>().indices().get<by_name>();\n   auto get_account_id = [&accounts_by_name](const string& name) {\n      auto itr = accounts_by_name.find(name);\n      FC_ASSERT(itr != accounts_by_name.end(),\n                \"Unable to find account '${acct}'. Did you forget to add a record for it to initial_accounts?\",\n                (\"acct\", name));\n      return itr->get_id();\n   };\n\n   // Helper function to get asset ID by symbol\n   const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();\n   const auto get_asset_id = [&assets_by_symbol](const string& symbol) {\n      auto itr = assets_by_symbol.find(symbol);\n      FC_ASSERT(itr != assets_by_symbol.end(),\n                \"Unable to find asset '${sym}'. Did you forget to add a record for it to initial_assets?\",\n                (\"sym\", symbol));\n      return itr->get_id();\n   };\n\n   map<asset_id_type, share_type> total_supplies;\n   map<asset_id_type, share_type> total_debts;\n\n   // Create initial assets\n   for( const genesis_state_type::initial_asset_type& asset : genesis_state.initial_assets )\n   {\n      asset_id_type new_asset_id = get_index_type<asset_index>().get_next_id();\n      total_supplies[ new_asset_id ] = 0;\n\n      asset_dynamic_data_id_type dynamic_data_id;\n      optional<asset_bitasset_data_id_type> bitasset_data_id;\n      if( asset.is_bitasset )\n      {\n         int collateral_holder_number = 0;\n         total_debts[ new_asset_id ] = 0;\n         for( const auto& collateral_rec : asset.collateral_records )\n         {\n            account_create_operation cop;\n            cop.name = asset.symbol + \"-collateral-holder-\" + std::to_string(collateral_holder_number);\n            boost::algorithm::to_lower(cop.name);\n            cop.registrar = GRAPHENE_TEMP_ACCOUNT;\n            cop.owner = authority(1, collateral_rec.owner, 1);\n            cop.active = cop.owner;\n            account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get<object_id_type>();\n\n            modify( owner_account_id(*this).statistics(*this), [&collateral_rec]( account_statistics_object& o ) {\n               o.total_core_in_orders = collateral_rec.collateral;\n            });\n\n            create<call_order_object>([&](call_order_object& c) {\n               c.borrower = owner_account_id;\n               c.collateral = collateral_rec.collateral;\n               c.debt = collateral_rec.debt;\n               c.call_price = price::call_price(chain::asset(c.debt, new_asset_id),\n                                                chain::asset(c.collateral, core_asset.id),\n                                                GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n            });\n\n            total_supplies[ asset_id_type(0) ] += collateral_rec.collateral;\n            total_debts[ new_asset_id ] += collateral_rec.debt;\n            ++collateral_holder_number;\n         }\n\n         bitasset_data_id = create<asset_bitasset_data_object>([&core_asset,new_asset_id](asset_bitasset_data_object& b) {\n            b.options.short_backing_asset = core_asset.id;\n            b.options.minimum_feeds = GRAPHENE_DEFAULT_MINIMUM_FEEDS;\n            b.asset_id = new_asset_id;\n         }).id;\n      }\n\n      dynamic_data_id = create<asset_dynamic_data_object>([&asset](asset_dynamic_data_object& d) {\n         d.accumulated_fees = asset.accumulated_fees;\n      }).id;\n\n      total_supplies[ new_asset_id ] += asset.accumulated_fees;\n\n      create<asset_object>([&](asset_object& a) {\n         a.symbol = asset.symbol;\n         a.options.description = asset.description;\n         a.precision = asset.precision;\n         string issuer_name = asset.issuer_name;\n         a.issuer = get_account_id(issuer_name);\n         a.options.max_supply = asset.max_supply;\n         a.options.flags = witness_fed_asset;\n         a.options.issuer_permissions = charge_market_fee | override_authority | white_list | transfer_restricted | disable_confidential |\n                                       ( asset.is_bitasset ? disable_force_settle | global_settle | witness_fed_asset | committee_fed_asset : 0 );\n         a.dynamic_asset_data_id = dynamic_data_id;\n         a.bitasset_data_id = bitasset_data_id;\n      });\n   }\n\n   // Create initial balances\n   share_type total_allocation;\n   for( const auto& handout : genesis_state.initial_balances )\n   {\n      const auto asset_id = get_asset_id(handout.asset_symbol);\n      create<balance_object>([&handout,total_allocation,asset_id](balance_object& b) {\n         b.balance = asset(handout.amount, asset_id);\n         b.owner = handout.owner;\n      });\n\n      total_supplies[ asset_id ] += handout.amount;\n   }\n\n   // Create initial vesting balances\n   for( const genesis_state_type::initial_vesting_balance_type& vest : genesis_state.initial_vesting_balances )\n   {\n      const auto asset_id = get_asset_id(vest.asset_symbol);\n      create<balance_object>([&](balance_object& b) {\n         b.owner = vest.owner;\n         b.balance = asset(vest.amount, asset_id);\n\n         linear_vesting_policy policy;\n         policy.begin_timestamp = vest.begin_timestamp;\n         policy.vesting_cliff_seconds = 0;\n         policy.vesting_duration_seconds = vest.vesting_duration_seconds;\n         policy.begin_balance = vest.begin_balance;\n\n         b.vesting_policy = std::move(policy);\n      });\n\n      total_supplies[ asset_id ] += vest.amount;\n   }\n\n   if( total_supplies[ asset_id_type(0) ] > 0 )\n   {\n       adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));\n   }\n   else\n   {\n       total_supplies[ asset_id_type(0) ] = GRAPHENE_MAX_SHARE_SUPPLY;\n   }\n\n   const auto& idx = get_index_type<asset_index>().indices().get<by_symbol>();\n   auto it = idx.begin();\n   bool has_imbalanced_assets = false;\n\n   while( it != idx.end() )\n   {\n      if( it->bitasset_data_id.valid() )\n      {\n         auto supply_itr = total_supplies.find( it->id );\n         auto debt_itr = total_debts.find( it->id );\n         FC_ASSERT( supply_itr != total_supplies.end() );\n         FC_ASSERT( debt_itr != total_debts.end() );\n         if( supply_itr->second != debt_itr->second )\n         {\n            has_imbalanced_assets = true;\n            elog( \"Genesis for asset ${aname} is not balanced\\n\"\n                  \"   Debt is ${debt}\\n\"\n                  \"   Supply is ${supply}\\n\",\n                  (\"aname\", it->symbol)\n                  (\"debt\", debt_itr->second)\n                  (\"supply\", supply_itr->second)\n                );\n         }\n      }\n      ++it;\n   }\n   FC_ASSERT( !has_imbalanced_assets );\n\n   // Save tallied supplies\n   for( const auto& item : total_supplies )\n   {\n       const auto asset_id = item.first;\n       const auto total_supply = item.second;\n\n       modify( get( asset_id ), [ & ]( asset_object& asset ) {\n           modify( get( asset.dynamic_asset_data_id ), [ & ]( asset_dynamic_data_object& asset_data ) {\n               asset_data.current_supply = total_supply;\n           } );\n       } );\n   }\n\n   // Create special witness account\n   const witness_object& wit = create<witness_object>([&](witness_object& w) {});\n   FC_ASSERT( wit.id == GRAPHENE_NULL_WITNESS );\n   remove(wit);\n\n   // Create initial witnesses\n   std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(),\n                 [&](const genesis_state_type::initial_witness_type& witness) {\n      witness_create_operation op;\n      op.witness_account = get_account_id(witness.owner_name);\n      op.block_signing_key = witness.block_signing_key;\n      apply_operation(genesis_eval_state, op);\n   });\n\n   // Create initial committee members\n   std::for_each(genesis_state.initial_committee_candidates.begin(), genesis_state.initial_committee_candidates.end(),\n                 [&](const genesis_state_type::initial_committee_member_type& member) {\n      committee_member_create_operation op;\n      op.committee_member_account = get_account_id(member.owner_name);\n      apply_operation(genesis_eval_state, op);\n   });\n\n   // Create initial workers\n   std::for_each(genesis_state.initial_worker_candidates.begin(), genesis_state.initial_worker_candidates.end(),\n                  [&](const genesis_state_type::initial_worker_type& worker)\n   {\n       worker_create_operation op;\n       op.owner = get_account_id(worker.owner_name);\n       op.work_begin_date = genesis_state.initial_timestamp;\n       op.work_end_date = time_point_sec::maximum();\n       op.daily_pay = worker.daily_pay;\n       op.name = \"Genesis-Worker-\" + worker.owner_name;\n       op.initializer = vesting_balance_worker_initializer{uint16_t(0)};\n\n       apply_operation(genesis_eval_state, std::move(op));\n   });\n\n   // Set active witnesses\n   modify(get_global_properties(), [&genesis_state](global_property_object& p) {\n      for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i )\n      {\n         p.active_witnesses.insert(witness_id_type(i));\n      }\n   });\n\n   // Enable fees\n   modify(get_global_properties(), [&genesis_state](global_property_object& p) {\n      p.parameters.get_mutable_fees() = genesis_state.initial_parameters.get_current_fees();\n   });\n\n   // Create witness scheduler\n   _p_witness_schedule_obj = & create<witness_schedule_object>([this]( witness_schedule_object& wso )\n   {\n      for( const witness_id_type& wid : get_global_properties().active_witnesses )\n         wso.current_shuffled_witnesses.push_back( wid );\n   });\n\n   // Create FBA counters\n   create<fba_accumulator_object>([&]( fba_accumulator_object& acc )\n   {\n      FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_to_blind ) );\n      acc.accumulated_fba_fees = 0;\n#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET\n      acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;\n#endif\n   });\n\n   create<fba_accumulator_object>([&]( fba_accumulator_object& acc )\n   {\n      FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_blind_transfer ) );\n      acc.accumulated_fba_fees = 0;\n#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET\n      acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;\n#endif\n   });\n\n   create<fba_accumulator_object>([&]( fba_accumulator_object& acc )\n   {\n      FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_from_blind ) );\n      acc.accumulated_fba_fees = 0;\n#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET\n      acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;\n#endif\n   });\n\n   FC_ASSERT( get_index<fba_accumulator_object>().get_next_id() == fba_accumulator_id_type( fba_accumulator_id_count ) );\n\n   //debug_dump();\n\n   _undo_db.enable();\n} FC_CAPTURE_AND_RETHROW() }\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_maint.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <fc/uint128.hpp>\n\n#include <graphene/protocol/market.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/fba_accumulator_id.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/budget_record_object.hpp>\n#include <graphene/chain/buyback_object.hpp>\n#include <graphene/chain/chain_property_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/special_authority_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/vote_count.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n\nnamespace graphene { namespace chain {\n\ntemplate<class Index>\nvector<std::reference_wrapper<const typename Index::object_type>> database::sort_votable_objects(size_t count) const\n{\n   using ObjectType = typename Index::object_type;\n   const auto& all_objects = get_index_type<Index>().indices();\n   count = std::min(count, all_objects.size());\n   vector<std::reference_wrapper<const ObjectType>> refs;\n   refs.reserve(all_objects.size());\n   std::transform(all_objects.begin(), all_objects.end(),\n                  std::back_inserter(refs),\n                  [](const ObjectType& o) { return std::cref(o); });\n   std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),\n                   [this](const ObjectType& a, const ObjectType& b)->bool {\n      share_type oa_vote = _vote_tally_buffer[a.vote_id];\n      share_type ob_vote = _vote_tally_buffer[b.vote_id];\n      if( oa_vote != ob_vote )\n         return oa_vote > ob_vote;\n      return a.vote_id < b.vote_id;\n   });\n\n   refs.resize(count, refs.front());\n   return refs;\n}\n\ntemplate<class Type>\nvoid database::perform_account_maintenance(Type tally_helper)\n{\n   const auto& bal_idx = get_index_type< account_balance_index >().indices().get< by_maintenance_flag >();\n   if( bal_idx.begin() != bal_idx.end() )\n   {\n      auto bal_itr = bal_idx.rbegin();\n      while( bal_itr->maintenance_flag )\n      {\n         const account_balance_object& bal_obj = *bal_itr;\n\n         modify( get_account_stats_by_owner( bal_obj.owner ), [&bal_obj](account_statistics_object& aso) {\n            aso.core_in_balance = bal_obj.balance;\n         });\n\n         modify( bal_obj, []( account_balance_object& abo ) {\n            abo.maintenance_flag = false;\n         });\n\n         bal_itr = bal_idx.rbegin();\n      }\n   }\n\n   const auto& stats_idx = get_index_type< account_stats_index >().indices().get< by_maintenance_seq >();\n   auto stats_itr = stats_idx.lower_bound( true );\n\n   while( stats_itr != stats_idx.end() )\n   {\n      const account_statistics_object& acc_stat = *stats_itr;\n      const account_object& acc_obj = acc_stat.owner( *this );\n      ++stats_itr;\n\n      if( acc_stat.has_some_core_voting() )\n         tally_helper( acc_obj, acc_stat );\n\n      if( acc_stat.has_pending_fees() )\n         acc_stat.process_fees( acc_obj, *this );\n   }\n\n}\n\n/// @brief A visitor for @ref worker_type which calls pay_worker on the worker within\nstruct worker_pay_visitor\n{\n   private:\n      share_type pay;\n      database& db;\n\n   public:\n      worker_pay_visitor(share_type pay, database& db)\n         : pay(pay), db(db) {}\n\n      typedef void result_type;\n      template<typename W>\n      void operator()(W& worker)const\n      {\n         worker.pay_worker(pay, db);\n      }\n};\n\nvoid database::update_worker_votes()\n{\n   const auto& idx = get_index_type<worker_index>().indices().get<by_account>();\n   auto itr = idx.begin();\n   auto itr_end = idx.end();\n   bool allow_negative_votes = (head_block_time() < HARDFORK_607_TIME);\n   while( itr != itr_end )\n   {\n      modify( *itr, [this,allow_negative_votes]( worker_object& obj )\n      {\n         obj.total_votes_for = _vote_tally_buffer[obj.vote_for];\n         obj.total_votes_against = allow_negative_votes ? _vote_tally_buffer[obj.vote_against] : 0;\n      });\n      ++itr;\n   }\n}\n\nvoid database::pay_workers( share_type& budget )\n{\n   const auto head_time = head_block_time();\n//   ilog(\"Processing payroll! Available budget is ${b}\", (\"b\", budget));\n   vector<std::reference_wrapper<const worker_object>> active_workers;\n   // TODO optimization: add by_expiration index to avoid iterating through all objects\n   get_index_type<worker_index>().inspect_all_objects([head_time, &active_workers](const object& o) {\n      const worker_object& w = static_cast<const worker_object&>(o);\n      if( w.is_active(head_time) && w.approving_stake() > 0 )\n         active_workers.emplace_back(w);\n   });\n\n   // worker with more votes is preferred\n   // if two workers exactly tie for votes, worker with lower ID is preferred\n   std::sort(active_workers.begin(), active_workers.end(), [](const worker_object& wa, const worker_object& wb) {\n      share_type wa_vote = wa.approving_stake();\n      share_type wb_vote = wb.approving_stake();\n      if( wa_vote != wb_vote )\n         return wa_vote > wb_vote;\n      return wa.id < wb.id;\n   });\n\n   const auto last_budget_time = get_dynamic_global_properties().last_budget_time;\n   const auto passed_time_ms = head_time - last_budget_time;\n   const auto passed_time_count = passed_time_ms.count();\n   const auto day_count = fc::days(1).count();\n   for( uint32_t i = 0; i < active_workers.size() && budget > 0; ++i )\n   {\n      const worker_object& active_worker = active_workers[i];\n      share_type requested_pay = active_worker.daily_pay;\n\n      // Note: if there is a good chance that passed_time_count == day_count,\n      //       for better performance, can avoid the 128 bit calculation by adding a check.\n      //       Since it's not the case on BitShares mainnet, we're not using a check here.\n      fc::uint128_t pay = requested_pay.value;\n      pay *= passed_time_count;\n      pay /= day_count;\n      requested_pay = static_cast<uint64_t>(pay);\n\n      share_type actual_pay = std::min(budget, requested_pay);\n      //ilog(\" ==> Paying ${a} to worker ${w}\", (\"w\", active_worker.id)(\"a\", actual_pay));\n      modify(active_worker, [&](worker_object& w) {\n         w.worker.visit(worker_pay_visitor(actual_pay, *this));\n      });\n\n      budget -= actual_pay;\n   }\n}\n\nvoid database::update_active_witnesses()\n{ try {\n   assert( _witness_count_histogram_buffer.size() > 0 );\n   share_type stake_target = (_total_voting_stake[1]-_witness_count_histogram_buffer[0]) / 2;\n\n   /// accounts that vote for 0 or 1 witness do not get to express an opinion on\n   /// the number of witnesses to have (they abstain and are non-voting accounts)\n\n   share_type stake_tally = 0; \n\n   size_t witness_count = 0;\n   if( stake_target > 0 )\n   {\n      while( (witness_count < _witness_count_histogram_buffer.size() - 1)\n             && (stake_tally <= stake_target) )\n      {\n         stake_tally += _witness_count_histogram_buffer[++witness_count];\n      }\n   }\n\n   const chain_property_object& cpo = get_chain_properties();\n\n   witness_count = std::max( witness_count*2+1, (size_t)cpo.immutable_parameters.min_witness_count );\n   auto wits = sort_votable_objects<witness_index>( witness_count );\n\n   const global_property_object& gpo = get_global_properties();\n\n   auto update_witness_total_votes = [this]( const witness_object& wit ) {\n      modify( wit, [this]( witness_object& obj )\n      {\n         obj.total_votes = _vote_tally_buffer[obj.vote_id];\n      });\n   };\n\n   if( _track_standby_votes )\n   {\n      const auto& all_witnesses = get_index_type<witness_index>().indices();\n      for( const witness_object& wit : all_witnesses )\n      {\n         update_witness_total_votes( wit );\n      }\n   }\n   else\n   {\n      for( const witness_object& wit : wits )\n      {\n         update_witness_total_votes( wit );\n      }\n   }\n\n   // Update witness authority\n   modify( get(GRAPHENE_WITNESS_ACCOUNT), [this,&wits]( account_object& a )\n   {\n      if( head_block_time() < HARDFORK_533_TIME )\n      {\n         uint64_t total_votes = 0;\n         map<account_id_type, uint64_t> weights;\n         a.active.weight_threshold = 0;\n         a.active.clear();\n\n         for( const witness_object& wit : wits )\n         {\n            weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]);\n            total_votes += _vote_tally_buffer[wit.vote_id];\n         }\n\n         // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,\n         // then I want to keep the most significant 16 bits of what's left.\n         int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);\n         for( const auto& weight : weights )\n         {\n            // Ensure that everyone has at least one vote. Zero weights aren't allowed.\n            uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );\n            a.active.account_auths[weight.first] += votes;\n            a.active.weight_threshold += votes;\n         }\n\n         a.active.weight_threshold /= 2;\n         a.active.weight_threshold += 1;\n      }\n      else\n      {\n         vote_counter vc;\n         for( const witness_object& wit : wits )\n            vc.add( wit.witness_account, _vote_tally_buffer[wit.vote_id] );\n         vc.finish( a.active );\n      }\n   } );\n\n   modify( gpo, [&wits]( global_property_object& gp )\n   {\n      gp.active_witnesses.clear();\n      gp.active_witnesses.reserve(wits.size());\n      std::transform(wits.begin(), wits.end(),\n                     std::inserter(gp.active_witnesses, gp.active_witnesses.end()),\n                     [](const witness_object& w) {\n         return w.id;\n      });\n   });\n\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid database::update_active_committee_members()\n{ try {\n   assert( _committee_count_histogram_buffer.size() > 0 );\n   share_type stake_target = (_total_voting_stake[0]-_committee_count_histogram_buffer[0]) / 2;\n\n   /// accounts that vote for 0 or 1 committee member do not get to express an opinion on\n   /// the number of committee members to have (they abstain and are non-voting accounts)\n   share_type stake_tally = 0;\n   size_t committee_member_count = 0;\n   if( stake_target > 0 )\n   {\n      while( (committee_member_count < _committee_count_histogram_buffer.size() - 1)\n             && (stake_tally <= stake_target.value) )\n      {\n         stake_tally += _committee_count_histogram_buffer[++committee_member_count];\n      }\n   }\n\n   const chain_property_object& cpo = get_chain_properties();\n\n   committee_member_count = std::max( committee_member_count*2+1, (size_t)cpo.immutable_parameters.min_committee_member_count );\n   auto committee_members = sort_votable_objects<committee_member_index>( committee_member_count );\n\n   auto update_committee_member_total_votes = [this]( const committee_member_object& cm ) {\n      modify( cm, [this]( committee_member_object& obj )\n      {\n         obj.total_votes = _vote_tally_buffer[obj.vote_id];\n      });\n   };\n\n   if( _track_standby_votes )\n   {\n      const auto& all_committee_members = get_index_type<committee_member_index>().indices();\n      for( const committee_member_object& cm : all_committee_members )\n      {\n         update_committee_member_total_votes( cm );\n      }\n   }\n   else\n   {\n      for( const committee_member_object& cm : committee_members )\n      {\n         update_committee_member_total_votes( cm );\n      }\n   }\n\n   // Update committee authorities\n   if( !committee_members.empty() )\n   {\n      const account_object& committee_account = get(GRAPHENE_COMMITTEE_ACCOUNT);\n      modify( committee_account, [this,&committee_members](account_object& a)\n      {\n         if( head_block_time() < HARDFORK_533_TIME )\n         {\n            uint64_t total_votes = 0;\n            map<account_id_type, uint64_t> weights;\n            a.active.weight_threshold = 0;\n            a.active.clear();\n\n            for( const committee_member_object& cm : committee_members )\n            {\n               weights.emplace( cm.committee_member_account, _vote_tally_buffer[cm.vote_id] );\n               total_votes += _vote_tally_buffer[cm.vote_id];\n            }\n\n            // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,\n            // then I want to keep the most significant 16 bits of what's left.\n            int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);\n            for( const auto& weight : weights )\n            {\n               // Ensure that everyone has at least one vote. Zero weights aren't allowed.\n               uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );\n               a.active.account_auths[weight.first] += votes;\n               a.active.weight_threshold += votes;\n            }\n\n            a.active.weight_threshold /= 2;\n            a.active.weight_threshold += 1;\n         }\n         else\n         {\n            vote_counter vc;\n            for( const committee_member_object& cm : committee_members )\n               vc.add( cm.committee_member_account, _vote_tally_buffer[cm.vote_id] );\n            vc.finish( a.active );\n         }\n      });\n      modify( get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&committee_account](account_object& a)\n      {\n         a.active = committee_account.active;\n      });\n   }\n   modify( get_global_properties(), [&committee_members](global_property_object& gp)\n   {\n      gp.active_committee_members.clear();\n      std::transform(committee_members.begin(), committee_members.end(),\n                     std::inserter(gp.active_committee_members, gp.active_committee_members.begin()),\n                     [](const committee_member_object& d) { return d.id; });\n   });\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const\n{\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n   const asset_object& core = get_core_asset();\n   const asset_dynamic_data_object& core_dd = get_core_dynamic_data();\n\n   rec.from_initial_reserve = core.reserved(*this);\n   rec.from_accumulated_fees = core_dd.accumulated_fees;\n   rec.from_unused_witness_budget = dpo.witness_budget;\n\n   if(    (dpo.last_budget_time == fc::time_point_sec())\n       || (now <= dpo.last_budget_time) )\n   {\n      rec.time_since_last_budget = 0;\n      return;\n   }\n\n   int64_t dt = (now - dpo.last_budget_time).to_seconds();\n   rec.time_since_last_budget = uint64_t( dt );\n\n   // We'll consider accumulated_fees to be reserved at the BEGINNING\n   // of the maintenance interval.  However, for speed we only\n   // call modify() on the asset_dynamic_data_object once at the\n   // end of the maintenance interval.  Thus the accumulated_fees\n   // are available for the budget at this point, but not included\n   // in core.reserved().\n   share_type reserve = rec.from_initial_reserve + core_dd.accumulated_fees;\n   // Similarly, we consider leftover witness_budget to be burned\n   // at the BEGINNING of the maintenance interval.\n   reserve += dpo.witness_budget;\n\n   fc::uint128_t budget_u128 = reserve.value;\n   budget_u128 *= uint64_t(dt);\n   budget_u128 *= GRAPHENE_CORE_ASSET_CYCLE_RATE;\n   //round up to the nearest satoshi -- this is necessary to ensure\n   //   there isn't an \"untouchable\" reserve, and we will eventually\n   //   be able to use the entire reserve\n   budget_u128 += ((uint64_t(1) << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS) - 1);\n   budget_u128 >>= GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;\n   if( budget_u128 < static_cast<fc::uint128_t>(reserve.value) )\n      rec.total_budget = share_type(static_cast<uint64_t>(budget_u128));\n   else\n      rec.total_budget = reserve;\n\n   return;\n}\n\n/**\n * Update the budget for witnesses and workers.\n */\nvoid database::process_budget()\n{\n   try\n   {\n      const global_property_object& gpo = get_global_properties();\n      const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n      const asset_dynamic_data_object& core = get_core_dynamic_data();\n      fc::time_point_sec now = head_block_time();\n\n      int64_t time_to_maint = (dpo.next_maintenance_time - now).to_seconds();\n      //\n      // The code that generates the next maintenance time should\n      //    only produce a result in the future.  If this assert\n      //    fails, then the next maintenance time algorithm is buggy.\n      //\n      assert( time_to_maint > 0 );\n      //\n      // Code for setting chain parameters should validate\n      //    block_interval > 0 (as well as the humans proposing /\n      //    voting on changes to block interval).\n      //\n      assert( gpo.parameters.block_interval > 0 );\n      uint64_t blocks_to_maint = (uint64_t(time_to_maint) + gpo.parameters.block_interval - 1) / gpo.parameters.block_interval;\n\n      // blocks_to_maint > 0 because time_to_maint > 0,\n      // which means numerator is at least equal to block_interval\n\n      budget_record rec;\n      initialize_budget_record( now, rec );\n      share_type available_funds = rec.total_budget;\n\n      share_type witness_budget = gpo.parameters.witness_pay_per_block.value * blocks_to_maint;\n      rec.requested_witness_budget = witness_budget;\n      witness_budget = std::min(witness_budget, available_funds);\n      rec.witness_budget = witness_budget;\n      available_funds -= witness_budget;\n\n      fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;\n      worker_budget_u128 *= uint64_t(time_to_maint);\n      worker_budget_u128 /= 60*60*24;\n\n      share_type worker_budget;\n      if( worker_budget_u128 >= static_cast<fc::uint128_t>(available_funds.value) )\n         worker_budget = available_funds;\n      else\n         worker_budget = static_cast<uint64_t>(worker_budget_u128);\n      rec.worker_budget = worker_budget;\n      available_funds -= worker_budget;\n\n      share_type leftover_worker_funds = worker_budget;\n      pay_workers(leftover_worker_funds);\n      rec.leftover_worker_funds = leftover_worker_funds;\n      available_funds += leftover_worker_funds;\n\n      rec.supply_delta = rec.witness_budget\n         + rec.worker_budget\n         - rec.leftover_worker_funds\n         - rec.from_accumulated_fees\n         - rec.from_unused_witness_budget;\n\n      modify(core, [&]( asset_dynamic_data_object& _core )\n      {\n         _core.current_supply = (_core.current_supply + rec.supply_delta );\n\n         assert( rec.supply_delta ==\n                                   witness_budget\n                                 + worker_budget\n                                 - leftover_worker_funds\n                                 - _core.accumulated_fees\n                                 - dpo.witness_budget\n                                );\n         _core.accumulated_fees = 0;\n      });\n\n      modify(dpo, [&]( dynamic_global_property_object& _dpo )\n      {\n         // Since initial witness_budget was rolled into\n         // available_funds, we replace it with witness_budget\n         // instead of adding it.\n         _dpo.witness_budget = witness_budget;\n         _dpo.last_budget_time = now;\n      });\n\n      create< budget_record_object >( [&]( budget_record_object& _rec )\n      {\n         _rec.time = head_block_time();\n         _rec.record = rec;\n      });\n\n      // available_funds is money we could spend, but don't want to.\n      // we simply let it evaporate back into the reserve.\n   }\n   FC_CAPTURE_AND_RETHROW()\n}\n\ntemplate< typename Visitor >\nvoid visit_special_authorities( const database& db, Visitor visit )\n{\n   const auto& sa_idx = db.get_index_type< special_authority_index >().indices().get<by_id>();\n\n   for( const special_authority_object& sao : sa_idx )\n   {\n      const account_object& acct = sao.account(db);\n      if( !acct.owner_special_authority.is_type< no_special_authority >() )\n      {\n         visit( acct, true, acct.owner_special_authority );\n      }\n      if( !acct.active_special_authority.is_type< no_special_authority >() )\n      {\n         visit( acct, false, acct.active_special_authority );\n      }\n   }\n}\n\nvoid update_top_n_authorities( database& db )\n{\n   visit_special_authorities( db,\n   [&]( const account_object& acct, bool is_owner, const special_authority& auth )\n   {\n      if( auth.is_type< top_holders_special_authority >() )\n      {\n         // use index to grab the top N holders of the asset and vote_counter to obtain the weights\n\n         const top_holders_special_authority& tha = auth.get< top_holders_special_authority >();\n         vote_counter vc;\n         const auto& bal_idx = db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();\n         uint8_t num_needed = tha.num_top_holders;\n         if( num_needed == 0 )\n            return;\n\n         // find accounts\n         const auto range = bal_idx.equal_range( boost::make_tuple( tha.asset ) );\n         for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )\n         {\n             assert( bal.asset_type == tha.asset );\n             if( bal.owner == acct.id )\n                continue;\n             vc.add( bal.owner, bal.balance.value );\n             --num_needed;\n             if( num_needed == 0 )\n                break;\n         }\n\n         db.modify( acct, [&]( account_object& a )\n         {\n            vc.finish( is_owner ? a.owner : a.active );\n            if( !vc.is_empty() )\n               a.top_n_control_flags |= (is_owner ? account_object::top_n_control_owner : account_object::top_n_control_active);\n         } );\n      }\n   } );\n}\n\nvoid split_fba_balance(\n   database& db,\n   uint64_t fba_id,\n   uint16_t network_pct,\n   uint16_t designated_asset_buyback_pct,\n   uint16_t designated_asset_issuer_pct\n)\n{\n   FC_ASSERT( uint32_t(network_pct) + uint32_t(designated_asset_buyback_pct) + uint32_t(designated_asset_issuer_pct) == GRAPHENE_100_PERCENT );\n   const fba_accumulator_object& fba = fba_accumulator_id_type( fba_id )(db);\n   if( fba.accumulated_fba_fees == 0 )\n      return;\n\n   const asset_dynamic_data_object& core_dd = db.get_core_dynamic_data();\n\n   if( !fba.is_configured(db) )\n   {\n      ilog( \"${n} core given to network at block ${b} due to non-configured FBA\", (\"n\", fba.accumulated_fba_fees)(\"b\", db.head_block_time()) );\n      db.modify( core_dd, [&]( asset_dynamic_data_object& _core_dd )\n      {\n         _core_dd.current_supply -= fba.accumulated_fba_fees;\n      } );\n      db.modify( fba, [&]( fba_accumulator_object& _fba )\n      {\n         _fba.accumulated_fba_fees = 0;\n      } );\n      return;\n   }\n\n   fc::uint128_t buyback_amount_128 = fba.accumulated_fba_fees.value;\n   buyback_amount_128 *= designated_asset_buyback_pct;\n   buyback_amount_128 /= GRAPHENE_100_PERCENT;\n   share_type buyback_amount = static_cast<uint64_t>(buyback_amount_128);\n\n   fc::uint128_t issuer_amount_128 = fba.accumulated_fba_fees.value;\n   issuer_amount_128 *= designated_asset_issuer_pct;\n   issuer_amount_128 /= GRAPHENE_100_PERCENT;\n   share_type issuer_amount = static_cast<uint64_t>(issuer_amount_128);\n\n   // this assert should never fail\n   FC_ASSERT( buyback_amount + issuer_amount <= fba.accumulated_fba_fees );\n\n   share_type network_amount = fba.accumulated_fba_fees - (buyback_amount + issuer_amount);\n\n   const asset_object& designated_asset = (*fba.designated_asset)(db);\n\n   if( network_amount != 0 )\n   {\n      db.modify( core_dd, [&]( asset_dynamic_data_object& _core_dd )\n      {\n         _core_dd.current_supply -= network_amount;\n      } );\n   }\n\n   fba_distribute_operation vop;\n   vop.account_id = *designated_asset.buyback_account;\n   vop.fba_id = fba.id;\n   vop.amount = buyback_amount;\n   if( vop.amount != 0 )\n   {\n      db.adjust_balance( *designated_asset.buyback_account, asset(buyback_amount) );\n      db.push_applied_operation(vop);\n   }\n\n   vop.account_id = designated_asset.issuer;\n   vop.fba_id = fba.id;\n   vop.amount = issuer_amount;\n   if( vop.amount != 0 )\n   {\n      db.adjust_balance( designated_asset.issuer, asset(issuer_amount) );\n      db.push_applied_operation(vop);\n   }\n\n   db.modify( fba, [&]( fba_accumulator_object& _fba )\n   {\n      _fba.accumulated_fba_fees = 0;\n   } );\n}\n\nvoid distribute_fba_balances( database& db )\n{\n   split_fba_balance( db, fba_accumulator_id_transfer_to_blind  , 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT );\n   split_fba_balance( db, fba_accumulator_id_blind_transfer     , 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT );\n   split_fba_balance( db, fba_accumulator_id_transfer_from_blind, 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT );\n}\n\nvoid create_buyback_orders( database& db )\n{\n   const auto& bbo_idx = db.get_index_type< buyback_index >().indices().get<by_id>();\n   const auto& bal_idx = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >();\n\n   for( const buyback_object& bbo : bbo_idx )\n   {\n      const asset_object& asset_to_buy = bbo.asset_to_buy(db);\n      assert( asset_to_buy.buyback_account.valid() );\n\n      const account_object& buyback_account = (*(asset_to_buy.buyback_account))(db);\n\n      if( !buyback_account.allowed_assets.valid() )\n      {\n         wlog( \"skipping buyback account ${b} at block ${n} because allowed_assets does not exist\", (\"b\", buyback_account)(\"n\", db.head_block_num()) );\n         continue;\n      }\n\n      for( const auto& entry : bal_idx.get_account_balances( buyback_account.id ) )\n      {\n         const auto* it = entry.second;\n         asset_id_type asset_to_sell = it->asset_type;\n         share_type amount_to_sell = it->balance;\n         if( asset_to_sell == asset_to_buy.id )\n            continue;\n         if( amount_to_sell == 0 )\n            continue;\n         if( buyback_account.allowed_assets->find( asset_to_sell ) == buyback_account.allowed_assets->end() )\n         {\n            wlog( \"buyback account ${b} not selling disallowed holdings of asset ${a} at block ${n}\", (\"b\", buyback_account)(\"a\", asset_to_sell)(\"n\", db.head_block_num()) );\n            continue;\n         }\n\n         try\n         {\n            transaction_evaluation_state buyback_context(&db);\n            buyback_context.skip_fee_schedule_check = true;\n\n            limit_order_create_operation create_vop;\n            create_vop.fee = asset( 0, asset_id_type() );\n            create_vop.seller = buyback_account.id;\n            create_vop.amount_to_sell = asset( amount_to_sell, asset_to_sell );\n            create_vop.min_to_receive = asset( 1, asset_to_buy.id );\n            create_vop.expiration = time_point_sec::maximum();\n            create_vop.fill_or_kill = false;\n\n            limit_order_id_type order_id = db.apply_operation( buyback_context, create_vop ).get< object_id_type >();\n\n            if( db.find( order_id ) != nullptr )\n            {\n               limit_order_cancel_operation cancel_vop;\n               cancel_vop.fee = asset( 0, asset_id_type() );\n               cancel_vop.order = order_id;\n               cancel_vop.fee_paying_account = buyback_account.id;\n\n               db.apply_operation( buyback_context, cancel_vop );\n            }\n         }\n         catch( const fc::exception& e )\n         {\n            // we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account\n            wlog( \"Skipping buyback processing selling ${as} for ${ab} for buyback account ${b} at block ${n}; exception was ${e}\",\n                  (\"as\", asset_to_sell)(\"ab\", asset_to_buy)(\"b\", buyback_account)(\"n\", db.head_block_num())(\"e\", e.to_detail_string()) );\n            continue;\n         }\n      }\n   }\n   return;\n}\n\nvoid deprecate_annual_members( database& db )\n{\n   const auto& account_idx = db.get_index_type<account_index>().indices().get<by_id>();\n   fc::time_point_sec now = db.head_block_time();\n   for( const account_object& acct : account_idx )\n   {\n      try\n      {\n         transaction_evaluation_state upgrade_context(&db);\n         upgrade_context.skip_fee_schedule_check = true;\n\n         if( acct.is_annual_member( now ) )\n         {\n            account_upgrade_operation upgrade_vop;\n            upgrade_vop.fee = asset( 0, asset_id_type() );\n            upgrade_vop.account_to_upgrade = acct.id;\n            upgrade_vop.upgrade_to_lifetime_member = true;\n            db.apply_operation( upgrade_context, upgrade_vop );\n         }\n      }\n      catch( const fc::exception& e )\n      {\n         // we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account\n         wlog( \"Skipping annual member deprecate processing for account ${a} (${an}) at block ${n}; exception was ${e}\",\n               (\"a\", acct.id)(\"an\", acct.name)(\"n\", db.head_block_num())(\"e\", e.to_detail_string()) );\n         continue;\n      }\n   }\n   return;\n}\n\nvoid database::process_bids( const asset_bitasset_data_object& bad )\n{\n   if( bad.is_prediction_market ) return;\n   if( bad.current_feed.settlement_price.is_null() ) return;\n\n   asset_id_type to_revive_id = (asset( 0, bad.options.short_backing_asset ) * bad.settlement_price).asset_id;\n   const asset_object& to_revive = to_revive_id( *this );\n   const asset_dynamic_data_object& bdd = to_revive.dynamic_data( *this );\n\n   const auto& bid_idx = get_index_type< collateral_bid_index >().indices().get<by_price>();\n   const auto start = bid_idx.lower_bound( boost::make_tuple( to_revive_id, price::max( bad.options.short_backing_asset, to_revive_id ), collateral_bid_id_type() ) );\n\n   share_type covered = 0;\n   auto itr = start;\n   while( covered < bdd.current_supply && itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == to_revive_id )\n   {\n      const collateral_bid_object& bid = *itr;\n      asset debt_in_bid = bid.inv_swan_price.quote;\n      if( debt_in_bid.amount > bdd.current_supply )\n         debt_in_bid.amount = bdd.current_supply;\n      asset total_collateral = debt_in_bid * bad.settlement_price;\n      total_collateral += bid.inv_swan_price.base;\n      price call_price = price::call_price( debt_in_bid, total_collateral, bad.current_feed.maintenance_collateral_ratio );\n      if( ~call_price >= bad.current_feed.settlement_price ) break;\n      covered += debt_in_bid.amount;\n      ++itr;\n   }\n   if( covered < bdd.current_supply ) return;\n\n   const auto end = itr;\n   share_type to_cover = bdd.current_supply;\n   share_type remaining_fund = bad.settlement_fund;\n   for( itr = start; itr != end; )\n   {\n      const collateral_bid_object& bid = *itr;\n      ++itr;\n      asset debt_in_bid = bid.inv_swan_price.quote;\n      if( debt_in_bid.amount > bdd.current_supply )\n         debt_in_bid.amount = bdd.current_supply;\n      share_type debt = debt_in_bid.amount;\n      share_type collateral = (debt_in_bid * bad.settlement_price).amount;\n      if( debt >= to_cover )\n      {\n         debt = to_cover;\n         collateral = remaining_fund;\n      }\n      to_cover -= debt;\n      remaining_fund -= collateral;\n      execute_bid( bid, debt, collateral, bad.current_feed );\n   }\n   FC_ASSERT( remaining_fund == 0 );\n   FC_ASSERT( to_cover == 0 );\n\n   _cancel_bids_and_revive_mpa( to_revive, bad );\n}\n\n/// Reset call_price of all call orders according to their remaining collateral and debt.\n/// Do not update orders of prediction markets because we're sure they're up to date.\nvoid update_call_orders_hf_343( database& db )\n{\n   // Update call_price\n   wlog( \"Updating all call orders for hardfork core-343 at block ${n}\", (\"n\",db.head_block_num()) );\n   asset_id_type current_asset;\n   const asset_bitasset_data_object* abd = nullptr;\n   // by_collateral index won't change after call_price updated, so it's safe to iterate\n   for( const auto& call_obj : db.get_index_type<call_order_index>().indices().get<by_collateral>() )\n   {\n      if( current_asset != call_obj.debt_type() ) // debt type won't be asset_id_type(), abd will always get initialized\n      {\n         current_asset = call_obj.debt_type();\n         abd = &current_asset(db).bitasset_data(db);\n      }\n      if( !abd || abd->is_prediction_market ) // nothing to do with PM's; check !abd just to be safe\n         continue;\n      db.modify( call_obj, [abd]( call_order_object& call ) {\n         call.call_price  =  price::call_price( call.get_debt(), call.get_collateral(),\n                                                abd->current_feed.maintenance_collateral_ratio );\n      });\n   }\n   wlog( \"Done updating all call orders for hardfork core-343 at block ${n}\", (\"n\",db.head_block_num()) );\n}\n\n/// Reset call_price of all call orders to (1,1) since it won't be used in the future.\n/// Update PMs as well.\nvoid update_call_orders_hf_1270( database& db )\n{\n   // Update call_price\n   for( const auto& call_obj : db.get_index_type<call_order_index>().indices().get<by_id>() )\n   {\n      db.modify( call_obj, []( call_order_object& call ) {\n         call.call_price.base.amount = 1;\n         call.call_price.quote.amount = 1;\n      });\n   }\n}\n\n/// Match call orders for all bitAssets, including PMs.\nvoid match_call_orders( database& db )\n{\n   // Match call orders\n   wlog( \"Matching call orders at block ${n}\", (\"n\",db.head_block_num()) );\n   const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();\n   auto itr = asset_idx.lower_bound( true /** market issued */ );\n   while( itr != asset_idx.end() )\n   {\n      const asset_object& a = *itr;\n      ++itr;\n      // be here, next_maintenance_time should have been updated already\n      db.check_call_orders( a, true, false ); // allow black swan, and call orders are taker\n   }\n   wlog( \"Done matching call orders at block ${n}\", (\"n\",db.head_block_num()) );\n}\n\nvoid database::process_bitassets()\n{\n   time_point_sec head_time = head_block_time();\n   uint32_t head_epoch_seconds = head_time.sec_since_epoch();\n   bool after_hf_core_518 = ( head_time >= HARDFORK_CORE_518_TIME ); // clear expired feeds\n\n   const auto update_bitasset = [this,head_time,head_epoch_seconds,after_hf_core_518]( asset_bitasset_data_object &o )\n   {\n      o.force_settled_volume = 0; // Reset all BitAsset force settlement volumes to zero\n\n      // clear expired feeds\n      if( after_hf_core_518 )\n      {\n         const auto &asset = get( o.asset_id );\n         auto flags = asset.options.flags;\n         if ( ( flags & ( witness_fed_asset | committee_fed_asset ) ) &&\n              o.options.feed_lifetime_sec < head_epoch_seconds ) // if smartcoin && check overflow\n         {\n            fc::time_point_sec calculated = head_time - o.options.feed_lifetime_sec;\n            for( auto itr = o.feeds.rbegin(); itr != o.feeds.rend(); ) // loop feeds\n            {\n               auto feed_time = itr->second.first;\n               std::advance( itr, 1 );\n               if( feed_time < calculated )\n                  o.feeds.erase( itr.base() ); // delete expired feed\n            }\n         }\n      }\n   };\n\n   for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )\n   {\n      modify( d, update_bitasset );\n      if( d.has_settlement() )\n         process_bids(d);\n   }\n}\n\n/****\n * @brief a one-time data process to correct max_supply\n * \n * NOTE: while exceeding max_supply happened in mainnet, it seemed to have corrected\n * itself before HF 1465. But this method must remain to correct some assets in testnet\n */\nvoid process_hf_1465( database& db )\n{\n   // for each market issued asset\n   const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();\n   for( auto asset_itr = asset_idx.lower_bound(true); asset_itr != asset_idx.end(); ++asset_itr )\n   {\n      const auto& current_asset = *asset_itr;\n      graphene::chain::share_type current_supply = current_asset.dynamic_data(db).current_supply;\n      graphene::chain::share_type max_supply = current_asset.options.max_supply;\n      if (current_supply > max_supply && max_supply != GRAPHENE_MAX_SHARE_SUPPLY)\n      {\n         wlog( \"Adjusting max_supply of ${asset} because current_supply (${current_supply}) is greater than ${old}.\", \n               (\"asset\", current_asset.symbol) \n               (\"current_supply\", current_supply.value)\n               (\"old\", max_supply));\n         db.modify<asset_object>( current_asset, [current_supply](asset_object& obj) {\n            obj.options.max_supply = graphene::chain::share_type(std::min(current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY));\n         });\n      }\n   }\n}\n\n/****\n * @brief a one-time data process to correct current_supply of BTS token in the BitShares mainnet\n */\nvoid process_hf_2103( database& db )\n{\n   const balance_object* bal = db.find( balance_id_type( HARDFORK_CORE_2103_BALANCE_ID ) );\n   if( bal != nullptr && bal->balance.amount < 0 )\n   {\n      const asset_dynamic_data_object& ddo = bal->balance.asset_id(db).dynamic_data(db);\n      db.modify<asset_dynamic_data_object>( ddo, [bal](asset_dynamic_data_object& obj) {\n         obj.current_supply -= bal->balance.amount;\n      });\n      db.remove( *bal );\n   }\n}\n\nvoid update_median_feeds(database& db)\n{\n   time_point_sec head_time = db.head_block_time();\n   time_point_sec next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;\n\n   const auto update_bitasset = [head_time, next_maint_time]( asset_bitasset_data_object &o )\n   {\n      o.update_median_feeds( head_time, next_maint_time );\n   };\n\n   for( const auto& d : db.get_index_type<asset_bitasset_data_index>().indices() )\n   {\n      db.modify( d, update_bitasset );\n   }\n}\n\n/******\n * @brief one-time data process for hard fork core-868-890\n *\n * Prior to hardfork 868, switching a bitasset's shorting asset would not reset its\n * feeds. This method will run at the hardfork time, and erase (or nullify) feeds\n * that have incorrect backing assets.\n * https://github.com/bitshares/bitshares-core/issues/868\n *\n * Prior to hardfork 890, changing a bitasset's feed expiration time would not\n * trigger a median feed update. This method will run at the hardfork time, and\n * correct all median feed data.\n * https://github.com/bitshares/bitshares-core/issues/890\n *\n * @param db the database\n * @param skip_check_call_orders true if check_call_orders() should not be called\n */\n// NOTE: Unable to remove this function for testnet nor mainnet. Unfortunately, bad\n//       feeds were found.\nvoid process_hf_868_890( database& db, bool skip_check_call_orders )\n{\n   const auto next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;\n   const auto head_time = db.head_block_time();\n   // for each market issued asset\n   const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();\n   for( auto asset_itr = asset_idx.lower_bound(true); asset_itr != asset_idx.end(); ++asset_itr )\n   {\n      const auto& current_asset = *asset_itr;\n      // Incorrect witness & committee feeds can simply be removed.\n      // For non-witness-fed and non-committee-fed assets, set incorrect\n      // feeds to price(), since we can't simply remove them. For more information:\n      // https://github.com/bitshares/bitshares-core/pull/832#issuecomment-384112633\n      bool is_witness_or_committee_fed = false;\n      if ( current_asset.options.flags & ( witness_fed_asset | committee_fed_asset ) )\n         is_witness_or_committee_fed = true;\n\n      // for each feed\n      const asset_bitasset_data_object& bitasset_data = current_asset.bitasset_data(db);\n      auto itr = bitasset_data.feeds.begin();\n      while( itr != bitasset_data.feeds.end() )\n      {\n         // If the feed is invalid\n         if ( itr->second.second.settlement_price.quote.asset_id != bitasset_data.options.short_backing_asset\n               && ( is_witness_or_committee_fed || itr->second.second.settlement_price != price() ) )\n         {\n            db.modify( bitasset_data, [&itr, is_witness_or_committee_fed]( asset_bitasset_data_object& obj )\n            {\n               if( is_witness_or_committee_fed )\n               {\n                  // erase the invalid feed\n                  itr = obj.feeds.erase(itr);\n               }\n               else\n               {\n                  // nullify the invalid feed\n                  obj.feeds[itr->first].second.settlement_price = price();\n                  ++itr;\n               }\n            });\n         }\n         else\n         {\n            // Feed is valid. Skip it.\n            ++itr;\n         }\n      } // end loop of each feed\n\n      // always update the median feed due to https://github.com/bitshares/bitshares-core/issues/890\n      db.modify( bitasset_data, [head_time,next_maint_time]( asset_bitasset_data_object &obj ) {\n         obj.update_median_feeds( head_time, next_maint_time );\n         // NOTE: Normally we should call check_call_orders() after called update_median_feeds(), but for\n         // mainnet actually check_call_orders() would do nothing, so we skipped it for better performance.\n      });\n\n   } // for each market issued asset\n}\n\n\n/**\n * @brief Remove any custom active authorities whose expiration dates are in the past\n * @param db A mutable database reference\n */\nvoid delete_expired_custom_authorities( database& db )\n{\n   const auto& index = db.get_index_type<custom_authority_index>().indices().get<by_expiration>();\n   while (!index.empty() && index.begin()->valid_to < db.head_block_time())\n      db.remove(*index.begin());\n}\n\nnamespace detail {\n\n   struct vote_recalc_times\n   {\n      time_point_sec full_power_time;\n      time_point_sec zero_power_time;\n   };\n\n   struct vote_recalc_options\n   {\n      vote_recalc_options( uint32_t f, uint32_t d, uint32_t s )\n      : full_power_seconds(f), recalc_steps(d), seconds_per_step(s)\n      {\n         total_recalc_seconds = ( recalc_steps - 1 ) * seconds_per_step; // should not overflow\n         power_percents_to_subtract.reserve( recalc_steps - 1 );\n         for( uint32_t i = 1; i < recalc_steps; ++i )\n            power_percents_to_subtract.push_back( GRAPHENE_100_PERCENT * i / recalc_steps ); // should not overflow\n      }\n\n      vote_recalc_times get_vote_recalc_times( const time_point_sec now ) const\n      {\n         return { now - full_power_seconds, now - full_power_seconds - total_recalc_seconds };\n      }\n\n      uint32_t full_power_seconds;\n      uint32_t recalc_steps; // >= 1\n      uint32_t seconds_per_step;\n      uint32_t total_recalc_seconds;\n      vector<uint16_t> power_percents_to_subtract;\n\n      static const vote_recalc_options witness();\n      static const vote_recalc_options committee();\n      static const vote_recalc_options worker();\n      static const vote_recalc_options delegator();\n\n      // return the stake that is \"recalced to X\"\n      uint64_t get_recalced_voting_stake( const uint64_t stake, const time_point_sec last_vote_time,\n                                         const vote_recalc_times& recalc_times ) const\n      {\n         time_point_sec now = time_point_sec( time_point::now() ) ;\n         if( HARDFORK_CORE_2103F_PASSED(now) )\n            return stake;\n\n         if( last_vote_time > recalc_times.full_power_time )\n            return stake;\n         if( last_vote_time <= recalc_times.zero_power_time )\n            return 0;\n         uint32_t diff = recalc_times.full_power_time.sec_since_epoch() - last_vote_time.sec_since_epoch();\n         uint32_t steps_to_subtract_minus_1 = diff / seconds_per_step;\n         fc::uint128_t stake_to_subtract( stake );\n         stake_to_subtract *= power_percents_to_subtract[steps_to_subtract_minus_1];\n         stake_to_subtract /= GRAPHENE_100_PERCENT;\n         return stake - static_cast<uint64_t>(stake_to_subtract);\n      }\n   };\n\n   const vote_recalc_options vote_recalc_options::witness()\n   {\n      time_point_sec now = time_point_sec( time_point::now() ) ;\n      static const vote_recalc_options o( 360*86400, 8, 45*86400 );\n      static const vote_recalc_options o10( 1*86400, 8, 1*21600 );\n      if( HARDFORK_CORE_2103F_PASSED(now) )\n         return o10;\n      \n      return o;\n   }\n   const vote_recalc_options vote_recalc_options::committee()\n   {\n      time_point_sec now = time_point_sec( time_point::now() ) ;\n      static const vote_recalc_options o( 360*86400, 8, 45*86400 );\n      static const vote_recalc_options o10( 1*86400, 8, 1*21600 );\n      if( HARDFORK_CORE_2103F_PASSED(now) )\n         return o10;\n\n      return o;\n   }\n   const vote_recalc_options vote_recalc_options::worker()\n   {\n      time_point_sec now = time_point_sec( time_point::now() ) ;\n      static const vote_recalc_options o( 360*86400, 8, 45*86400 );\n      static const vote_recalc_options o10( 1*86400, 8, 1*21600 );\n      if( HARDFORK_CORE_2103F_PASSED(now) )\n         return o10;\n         \n      return o;\n   }\n   const vote_recalc_options vote_recalc_options::delegator()\n   {\n      time_point_sec now = time_point_sec( time_point::now() ) ;\n      static const vote_recalc_options o( 360*86400, 8, 45*86400 );\n      static const vote_recalc_options o10( 1*86400, 8, 1*21600 );\n      if( HARDFORK_CORE_2103F_PASSED(now) )\n         return o10;\n         \n      return o;\n   }\n}\n\nvoid database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)\n{\n   const auto& gpo = get_global_properties();\n   const auto& dgpo = get_dynamic_global_properties();\n\n   distribute_fba_balances(*this);\n   create_buyback_orders(*this);\n\n   struct vote_tally_helper {\n      database& d;\n      const global_property_object& props;\n      const dynamic_global_property_object& dprops;\n      const time_point_sec now;\n      const bool hf2103_passed;\n      const bool pob_activated;\n      const bool hf2103f_passed;\n\n      optional<detail::vote_recalc_times> witness_recalc_times;\n      optional<detail::vote_recalc_times> committee_recalc_times;\n      optional<detail::vote_recalc_times> worker_recalc_times;\n      optional<detail::vote_recalc_times> delegator_recalc_times;\n\n      vote_tally_helper( database& db )\n         : d(db), props( d.get_global_properties() ), dprops( d.get_dynamic_global_properties() ),\n           now( d.head_block_time() ), hf2103_passed( HARDFORK_CORE_2103_PASSED( now ) ),\n           pob_activated( dprops.total_pob > 0 || dprops.total_inactive > 0 ),\n           hf2103f_passed( HARDFORK_CORE_2103F_PASSED( now ) )\n      {\n         d._vote_tally_buffer.resize( props.next_available_vote_id, 0 );\n         d._witness_count_histogram_buffer.resize( props.parameters.maximum_witness_count / 2 + 1, 0 );\n         d._committee_count_histogram_buffer.resize( props.parameters.maximum_committee_count / 2 + 1, 0 );\n         d._total_voting_stake[0] = 0;\n         d._total_voting_stake[1] = 0;\n         if( hf2103_passed)\n         {\n            witness_recalc_times   = detail::vote_recalc_options::witness().get_vote_recalc_times( now );\n            committee_recalc_times = detail::vote_recalc_options::committee().get_vote_recalc_times( now );\n            worker_recalc_times    = detail::vote_recalc_options::worker().get_vote_recalc_times( now );\n            delegator_recalc_times = detail::vote_recalc_options::delegator().get_vote_recalc_times( now );\n         }\n      }\n\n      void operator()( const account_object& stake_account, const account_statistics_object& stats )\n      {\n         // PoB activation\n         if( pob_activated && stats.total_core_pob == 0 && stats.total_core_inactive == 0 )\n            return;\n\n         if( props.parameters.count_non_member_votes || stake_account.is_member( now ) )\n         {\n            // There may be a difference between the account whose stake is voting and the one specifying opinions.\n            // Usually they're the same, but if the stake account has specified a voting_account, that account is the\n            // one specifying the opinions.\n            bool directly_voting = ( stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT );\n            const account_object& opinion_account = ( directly_voting ? stake_account\n                                                      : d.get(stake_account.options.voting_account) );\n\n            uint64_t voting_stake[3]; // 0=committee, 1=witness, 2=worker, as in vote_id_type::vote_type\n            uint64_t num_committee_voting_stake; // number of committee members\n            voting_stake[2] = ( pob_activated ? 0 : stats.total_core_in_orders.value )\n                  + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)\n                  + stats.core_in_balance.value;\n\n            //PoB\n            const uint64_t pol_amount = stats.total_core_pol.value;\n            const uint64_t pol_value = stats.total_pol_value.value;\n            const uint64_t pob_amount = stats.total_core_pob.value;\n            const uint64_t pob_value = stats.total_pob_value.value;\n            if( pob_amount == 0 )\n            {\n               voting_stake[2] += pol_value;\n            }\n            else if( pol_amount == 0 ) // and pob_amount > 0\n            {\n               if( pob_amount <= voting_stake[2] )\n               {\n                  voting_stake[2] += ( pob_value - pob_amount );\n               }\n               else\n               {\n                  auto base_value = static_cast<fc::uint128_t>( voting_stake[2] ) * pob_value / pob_amount;\n                  voting_stake[2] = static_cast<uint64_t>( base_value );\n               }\n            }\n            else if( pob_amount <= pol_amount ) // pob_amount > 0 && pol_amount > 0\n            {\n               auto base_value = static_cast<fc::uint128_t>( pob_value ) * pol_value / pol_amount;\n               auto diff_value = static_cast<fc::uint128_t>( pob_amount ) * pol_value / pol_amount;\n               base_value += ( pol_value - diff_value );\n               voting_stake[2] += static_cast<uint64_t>( base_value );\n            }\n            else // pob_amount > pol_amount > 0\n            {\n               auto base_value = static_cast<fc::uint128_t>( pol_value ) * pob_value / pob_amount;\n               fc::uint128_t diff_amount = pob_amount - pol_amount;\n               if( diff_amount <= voting_stake[2] )\n               {\n                  auto diff_value = static_cast<fc::uint128_t>( pol_amount ) * pob_value / pob_amount;\n                  base_value += ( pob_value - diff_value );\n                  voting_stake[2] += static_cast<uint64_t>( base_value - diff_amount );\n               }\n               else // diff_amount > voting_stake[2]\n               {\n                  base_value += static_cast<fc::uint128_t>( voting_stake[2] ) * pob_value / pob_amount;\n                  voting_stake[2] = static_cast<uint64_t>( base_value );\n               }\n            }\n\n            // Shortcut\n            if( voting_stake[2] == 0 )\n               return;\n\n            // Recalculate votes\n            if( !hf2103_passed )\n            {\n               voting_stake[0] = voting_stake[2];\n               voting_stake[1] = voting_stake[2];\n               num_committee_voting_stake = voting_stake[2];\n            }\n            else\n            {\n               if( !directly_voting )\n               {\n                  voting_stake[2] = detail::vote_recalc_options::delegator().get_recalced_voting_stake(\n                                          voting_stake[2], stats.last_vote_time, *delegator_recalc_times );\n               }\n               const account_statistics_object& opinion_account_stats = ( directly_voting ? stats\n                                          : opinion_account.statistics( d ) );\n               voting_stake[1] = detail::vote_recalc_options::witness().get_recalced_voting_stake(\n                                    voting_stake[2], opinion_account_stats.last_vote_time, *witness_recalc_times );\n               voting_stake[0] = detail::vote_recalc_options::committee().get_recalced_voting_stake(\n                                    voting_stake[2], opinion_account_stats.last_vote_time, *committee_recalc_times );\n               num_committee_voting_stake = voting_stake[0];\n               // 2103 not pass\n               if( opinion_account.num_committee_voted > 1 && !hf2103f_passed )\n                  voting_stake[0] /= opinion_account.num_committee_voted;\n               voting_stake[2] = detail::vote_recalc_options::worker().get_recalced_voting_stake(\n                                    voting_stake[2], opinion_account_stats.last_vote_time, *worker_recalc_times );\n            }\n\n            for( vote_id_type id : opinion_account.options.votes )\n            {\n               uint32_t offset = id.instance();\n               uint32_t type = std::min( id.type(), vote_id_type::vote_type::worker ); // cap the data\n               // if they somehow managed to specify an illegal offset, ignore it.\n               if( offset < d._vote_tally_buffer.size() )\n                  d._vote_tally_buffer[offset] += voting_stake[type];\n            }\n\n            // votes for a number greater than maximum_witness_count are skipped here\n            if( voting_stake[1] > 0\n                  && opinion_account.options.num_witness <= props.parameters.maximum_witness_count )\n            {\n               uint16_t offset = opinion_account.options.num_witness / 2;\n               d._witness_count_histogram_buffer[offset] += voting_stake[1];\n            }\n            // votes for a number greater than maximum_committee_count are skipped here\n            if( num_committee_voting_stake > 0\n                  && opinion_account.options.num_committee <= props.parameters.maximum_committee_count )\n            {\n               uint16_t offset = opinion_account.options.num_committee / 2;\n               d._committee_count_histogram_buffer[offset] += num_committee_voting_stake;\n            }\n\n            d._total_voting_stake[0] += num_committee_voting_stake;\n            d._total_voting_stake[1] += voting_stake[1];\n         }\n      }\n   } tally_helper(*this);\n\n   perform_account_maintenance( tally_helper );\n\n   struct clear_canary {\n      clear_canary(vector<uint64_t>& target): target(target){}\n      ~clear_canary() { target.clear(); }\n   private:\n      vector<uint64_t>& target;\n   };\n   clear_canary a(_witness_count_histogram_buffer),\n                b(_committee_count_histogram_buffer),\n                c(_vote_tally_buffer);\n\n   update_top_n_authorities(*this);\n   update_active_witnesses();\n   update_active_committee_members();\n   update_worker_votes();\n\n   modify(gpo, [&dgpo](global_property_object& p) {\n      // Remove scaling of account registration fee\n      p.parameters.get_mutable_fees().get<account_create_operation>().basic_fee >>= p.parameters.account_fee_scale_bitshifts *\n            (dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale);\n\n      if( p.pending_parameters )\n      {\n         p.parameters = std::move(*p.pending_parameters);\n         p.pending_parameters.reset();\n      }\n   });\n\n   auto next_maintenance_time = dgpo.next_maintenance_time;\n   auto maintenance_interval = gpo.parameters.maintenance_interval;\n\n   if( next_maintenance_time <= next_block.timestamp )\n   {\n      if( next_block.block_num() == 1 )\n         next_maintenance_time = time_point_sec() +\n               (((next_block.timestamp.sec_since_epoch() / maintenance_interval) + 1) * maintenance_interval);\n      else\n      {\n         // We want to find the smallest k such that next_maintenance_time + k * maintenance_interval > head_block_time()\n         //  This implies k > ( head_block_time() - next_maintenance_time ) / maintenance_interval\n         //\n         // Let y be the right-hand side of this inequality, i.e.\n         // y = ( head_block_time() - next_maintenance_time ) / maintenance_interval\n         //\n         // and let the fractional part f be y-floor(y).  Clearly 0 <= f < 1.\n         // We can rewrite f = y-floor(y) as floor(y) = y-f.\n         //\n         // Clearly k = floor(y)+1 has k > y as desired.  Now we must\n         // show that this is the least such k, i.e. k-1 <= y.\n         //\n         // But k-1 = floor(y)+1-1 = floor(y) = y-f <= y.\n         // So this k suffices.\n         //\n         auto y = (head_block_time() - next_maintenance_time).to_seconds() / maintenance_interval;\n         next_maintenance_time += (y+1) * maintenance_interval;\n      }\n   }\n\n   if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) )\n      deprecate_annual_members(*this);\n\n   // To reset call_price of all call orders, then match by new rule, for hard fork core-343\n   bool to_update_and_match_call_orders_for_hf_343 = false;\n   if( (dgpo.next_maintenance_time <= HARDFORK_CORE_343_TIME) && (next_maintenance_time > HARDFORK_CORE_343_TIME) )\n      to_update_and_match_call_orders_for_hf_343 = true;\n\n   // Process inconsistent price feeds\n   if( (dgpo.next_maintenance_time <= HARDFORK_CORE_868_890_TIME) && (next_maintenance_time > HARDFORK_CORE_868_890_TIME) )\n      process_hf_868_890( *this, to_update_and_match_call_orders_for_hf_343 );\n\n   // To reset call_price of all call orders, then match by new rule, for hard fork core-1270\n   bool to_update_and_match_call_orders_for_hf_1270 = false;\n   if( (dgpo.next_maintenance_time <= HARDFORK_CORE_1270_TIME) && (next_maintenance_time > HARDFORK_CORE_1270_TIME) )\n      to_update_and_match_call_orders_for_hf_1270 = true;\n\n   // make sure current_supply is less than or equal to max_supply\n   if ( dgpo.next_maintenance_time <= HARDFORK_CORE_1465_TIME && next_maintenance_time > HARDFORK_CORE_1465_TIME )\n      process_hf_1465(*this);\n\n   // Fix supply issue\n   if ( dgpo.next_maintenance_time <= HARDFORK_CORE_2103_TIME && next_maintenance_time > HARDFORK_CORE_2103_TIME )\n      process_hf_2103(*this);\n\n   modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {\n      d.next_maintenance_time = next_maintenance_time;\n      d.accounts_registered_this_interval = 0;\n   });\n\n   // We need to do it after updated next_maintenance_time, to apply new rules here, for hard fork core-343\n   if( to_update_and_match_call_orders_for_hf_343 )\n   {\n      update_call_orders_hf_343(*this);\n      match_call_orders(*this);\n   }\n\n   // We need to do it after updated next_maintenance_time, to apply new rules here, for hard fork core-1270.\n   if( to_update_and_match_call_orders_for_hf_1270 )\n   {\n      update_call_orders_hf_1270(*this);\n      update_median_feeds(*this);\n      match_call_orders(*this);\n   }\n\n   process_bitassets();\n   delete_expired_custom_authorities(*this);\n\n   // process_budget needs to run at the bottom because\n   //   it needs to know the next_maintenance_time\n   process_budget();\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_management.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/chain_property_object.hpp>\n#include <graphene/chain/witness_schedule_object.hpp>\n#include <graphene/chain/special_authority_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/fstream.hpp>\n\n#include <fstream>\n#include <functional>\n#include <iostream>\n#include <queue>\n#include <tuple>\n\nnamespace graphene { namespace chain {\n\ndatabase::database()\n{\n   initialize_indexes();\n   initialize_evaluators();\n}\n\ndatabase::~database()\n{\n   clear_pending();\n}\n\nvoid database::reindex( fc::path data_dir )\n{ try {\n   auto last_block = _block_id_to_block.last();\n   if( !last_block ) {\n      elog( \"!no last block\" );\n      edump((last_block));\n      return;\n   }\n   if( last_block->block_num() <= head_block_num()) return;\n\n   ilog( \"reindexing blockchain\" );\n   auto start = fc::time_point::now();\n   const auto last_block_num = last_block->block_num();\n   uint32_t undo_point = last_block_num < GRAPHENE_MAX_UNDO_HISTORY ? 0 : last_block_num - GRAPHENE_MAX_UNDO_HISTORY;\n\n   ilog( \"Replaying blocks, starting at ${next}...\", (\"next\",head_block_num() + 1) );\n   if( head_block_num() >= undo_point )\n   {\n      if( head_block_num() > 0 )\n         _fork_db.start_block( *fetch_block_by_number( head_block_num() ) );\n   }\n   else\n      _undo_db.disable();\n\n   uint32_t skip = node_properties().skip_flags;\n\n   size_t total_block_size = _block_id_to_block.total_block_size();\n   const auto& gpo = get_global_properties();\n   std::queue< std::tuple< size_t, signed_block, fc::future< void > > > blocks;\n   uint32_t next_block_num = head_block_num() + 1;\n   uint32_t i = next_block_num;\n   while( next_block_num <= last_block_num || !blocks.empty() )\n   {\n      if( next_block_num <= last_block_num && blocks.size() < 20 )\n      {\n         const size_t processed_block_size = _block_id_to_block.blocks_current_position();\n         fc::optional< signed_block > block = _block_id_to_block.fetch_by_number( next_block_num++ );\n         if( block.valid() )\n         {\n            if( block->timestamp >= last_block->timestamp - gpo.parameters.maximum_time_until_expiration )\n               skip &= ~skip_transaction_dupe_check;\n            blocks.emplace( processed_block_size, std::move(*block), fc::future<void>() );\n            std::get<2>(blocks.back()) = precompute_parallel( std::get<1>(blocks.back()), skip );\n         }\n         else\n         {\n            wlog( \"Reindexing terminated due to gap:  Block ${i} does not exist!\", (\"i\", i) );\n            uint32_t dropped_count = 0;\n            while( true )\n            {\n               fc::optional< block_id_type > last_id = _block_id_to_block.last_id();\n               // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1\n               if( !last_id.valid() )\n                  break;\n               // we've caught up to the gap\n               if( block_header::num_from_id( *last_id ) <= i )\n                  break;\n               _block_id_to_block.remove( *last_id );\n               dropped_count++;\n            }\n            wlog( \"Dropped ${n} blocks from after the gap\", (\"n\", dropped_count) );\n            next_block_num = last_block_num + 1; // don't load more blocks\n         }\n      }\n      else\n      {\n         std::get<2>(blocks.front()).wait();\n         const signed_block& block = std::get<1>(blocks.front());\n\n         if( i % 10000 == 0 )\n         {\n            std::stringstream bysize;\n            std::stringstream bynum;\n            bysize << std::fixed << std::setprecision(5) << double(std::get<0>(blocks.front())) / total_block_size * 100;\n            bynum << std::fixed << std::setprecision(5) << double(i*100)/last_block_num;\n            ilog(\n               \"   [by size: ${size}%   ${processed} of ${total}]   [by num: ${num}%   ${i} of ${last}]\",\n               (\"size\", bysize.str())\n               (\"processed\", std::get<0>(blocks.front()))\n               (\"total\", total_block_size)\n               (\"num\", bynum.str())\n               (\"i\", i)\n               (\"last\", last_block_num)\n            );\n         }\n         if( i == undo_point )\n         {\n            ilog( \"Writing database to disk at block ${i}\", (\"i\",i) );\n            flush();\n            ilog( \"Done\" );\n         }\n         if( i < undo_point )\n            apply_block( block, skip );\n         else\n         {\n            _undo_db.enable();\n            push_block( block, skip );\n         }\n         blocks.pop();\n         i++;\n      }\n   }\n   _undo_db.enable();\n   auto end = fc::time_point::now();\n   ilog( \"Done reindexing, elapsed time: ${t} sec\", (\"t\",double((end-start).count())/1000000.0 ) );\n} FC_CAPTURE_AND_RETHROW( (data_dir) ) }\n\nvoid database::wipe(const fc::path& data_dir, bool include_blocks)\n{\n   ilog(\"Wiping database\", (\"include_blocks\", include_blocks));\n   if (_opened) {\n     close();\n   }\n   object_database::wipe(data_dir);\n   if( include_blocks )\n      fc::remove_all( data_dir / \"database\" );\n}\n\nvoid database::open(\n   const fc::path& data_dir,\n   std::function<genesis_state_type()> genesis_loader,\n   const std::string& db_version)\n{\n   try\n   {\n      bool wipe_object_db = false;\n      if( !fc::exists( data_dir / \"db_version\" ) )\n         wipe_object_db = true;\n      else\n      {\n         std::string version_string;\n         fc::read_file_contents( data_dir / \"db_version\", version_string );\n         wipe_object_db = ( version_string != db_version );\n      }\n      if( wipe_object_db ) {\n          ilog(\"Wiping object_database due to missing or wrong version\");\n          object_database::wipe( data_dir );\n          std::ofstream version_file( (data_dir / \"db_version\").generic_string().c_str(),\n                                      std::ios::out | std::ios::binary | std::ios::trunc );\n          version_file.write( db_version.c_str(), db_version.size() );\n          version_file.close();\n      }\n\n      object_database::open(data_dir);\n\n      _block_id_to_block.open(data_dir / \"database\" / \"block_num_to_block\");\n\n      if( !find(global_property_id_type()) )\n         init_genesis(genesis_loader());\n      else\n      {\n         _p_core_asset_obj = &get( asset_id_type() );\n         _p_core_dynamic_data_obj = &get( asset_dynamic_data_id_type() );\n         _p_global_prop_obj = &get( global_property_id_type() );\n         _p_chain_property_obj = &get( chain_property_id_type() );\n         _p_dyn_global_prop_obj = &get( dynamic_global_property_id_type() );\n         _p_witness_schedule_obj = &get( witness_schedule_id_type() );\n      }\n\n      fc::optional<block_id_type> last_block = _block_id_to_block.last_id();\n      if( last_block.valid() )\n      {\n         FC_ASSERT( *last_block >= head_block_id(),\n                    \"last block ID does not match current chain state\",\n                    (\"last_block->id\", last_block)(\"head_block_id\",head_block_num()) );\n         reindex( data_dir );\n      }\n      _opened = true;\n   }\n   FC_CAPTURE_LOG_AND_RETHROW( (data_dir) )\n}\n\nvoid database::close(bool rewind)\n{\n   if (!_opened)\n      return;\n      \n   // TODO:  Save pending tx's on close()\n   clear_pending();\n\n   // pop all of the blocks that we can given our undo history, this should\n   // throw when there is no more undo history to pop\n   if( rewind )\n   {\n      try\n      {\n         uint32_t cutoff = get_dynamic_global_properties().last_irreversible_block_num;\n\n         ilog( \"Rewinding from ${head} to ${cutoff}\", (\"head\",head_block_num())(\"cutoff\",cutoff) );\n         while( head_block_num() > cutoff )\n         {\n            block_id_type popped_block_id = head_block_id();\n            pop_block();\n            _fork_db.remove(popped_block_id); // doesn't throw on missing\n         }\n      }\n      catch ( const fc::exception& e )\n      {\n         wlog( \"Database close unexpected exception: ${e}\", (\"e\", e) );\n      }\n   }\n\n   // Since pop_block() will move tx's in the popped blocks into pending,\n   // we have to clear_pending() after we're done popping to get a clean\n   // DB state (issue #336).\n   clear_pending();\n\n   object_database::flush();\n   object_database::close();\n\n   if( _block_id_to_block.is_open() )\n      _block_id_to_block.close();\n\n   _fork_db.reset();\n\n   _opened = false;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_market.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n#include <fc/uint128.hpp>\n\nnamespace graphene { namespace chain {\n\nnamespace detail {\n\n   share_type calculate_percent(const share_type& value, uint16_t percent)\n   {\n      fc::uint128_t a(value.value);\n      a *= percent;\n      a /= GRAPHENE_100_PERCENT;\n      FC_ASSERT( a <= GRAPHENE_MAX_SHARE_SUPPLY, \"overflow when calculating percent\" );\n      return static_cast<int64_t>(a);\n   }\n\n} //detail\n\n/**\n * All margin positions are force closed at the swan price\n * Collateral received goes into a force-settlement fund\n * No new margin positions can be created for this asset\n * Force settlement happens without delay at the swan price, deducting from force-settlement fund\n * No more asset updates may be issued.\n*/\nvoid database::globally_settle_asset( const asset_object& mia, const price& settlement_price )\n{\n   auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n   bool before_core_hardfork_1669 = ( maint_time <= HARDFORK_CORE_1669_TIME ); // whether to use call_price\n\n   if( before_core_hardfork_1669 )\n   {\n      globally_settle_asset_impl( mia, settlement_price,\n                                  get_index_type<call_order_index>().indices().get<by_price>() );\n   }\n   else\n   {\n      globally_settle_asset_impl( mia, settlement_price,\n                                  get_index_type<call_order_index>().indices().get<by_collateral>() );\n   }\n}\n\ntemplate<typename IndexType>\nvoid database::globally_settle_asset_impl( const asset_object& mia,\n                                           const price& settlement_price,\n                                           const IndexType& call_index )\n{ try {\n   const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);\n   FC_ASSERT( !bitasset.has_settlement(), \"black swan already occurred, it should not happen again\" );\n\n   const asset_object& backing_asset = bitasset.options.short_backing_asset(*this);\n   asset collateral_gathered = backing_asset.amount(0);\n\n   const asset_dynamic_data_object& mia_dyn = mia.dynamic_asset_data_id(*this);\n   auto original_mia_supply = mia_dyn.current_supply;\n\n   auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n   bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding\n\n   // cancel all call orders and accumulate it into collateral_gathered\n   auto call_itr = call_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );\n   auto call_end = call_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );\n\n   asset pays;\n   while( call_itr != call_end )\n   {\n      const call_order_object& order = *call_itr;\n      ++call_itr;\n\n      if( before_core_hardfork_342 )\n         pays = order.get_debt() * settlement_price; // round down, in favor of call order\n      else\n         pays = order.get_debt().multiply_and_round_up( settlement_price ); // round up in favor of global-settle fund\n\n      if( pays > order.get_collateral() )\n         pays = order.get_collateral();\n\n      collateral_gathered += pays;\n\n      FC_ASSERT( fill_call_order( order, pays, order.get_debt(), settlement_price, true ) ); // call order is maker\n   }\n\n   modify( bitasset, [&mia,original_mia_supply,&collateral_gathered]( asset_bitasset_data_object& obj ){\n           obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered;\n           obj.settlement_fund  = collateral_gathered.amount;\n           });\n\n   /// After all margin positions are closed, the current supply will be reported as 0, but\n   /// that is a lie, the supply didn't change.   We need to capture the current supply before\n   /// filling all call orders and then restore it afterward.   Then in the force settlement\n   /// evaluator reduce the supply\n   modify( mia_dyn, [original_mia_supply]( asset_dynamic_data_object& obj ){\n           obj.current_supply = original_mia_supply;\n         });\n\n} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }\n\nvoid database::revive_bitasset( const asset_object& bitasset )\n{ try {\n   FC_ASSERT( bitasset.is_market_issued() );\n   const asset_bitasset_data_object& bad = bitasset.bitasset_data(*this);\n   FC_ASSERT( bad.has_settlement() );\n   const asset_dynamic_data_object& bdd = bitasset.dynamic_asset_data_id(*this);\n   FC_ASSERT( !bad.is_prediction_market );\n   FC_ASSERT( !bad.current_feed.settlement_price.is_null() );\n\n   if( bdd.current_supply > 0 )\n   {\n      // Create + execute a \"bid\" with 0 additional collateral\n      const collateral_bid_object& pseudo_bid = create<collateral_bid_object>([&](collateral_bid_object& bid) {\n         bid.bidder = bitasset.issuer;\n         bid.inv_swan_price = asset(0, bad.options.short_backing_asset)\n                              / asset(bdd.current_supply, bitasset.id);\n      });\n      execute_bid( pseudo_bid, bdd.current_supply, bad.settlement_fund, bad.current_feed );\n   } else\n      FC_ASSERT( bad.settlement_fund == 0 );\n\n   _cancel_bids_and_revive_mpa( bitasset, bad );\n} FC_CAPTURE_AND_RETHROW( (bitasset) ) }\n\nvoid database::_cancel_bids_and_revive_mpa( const asset_object& bitasset, const asset_bitasset_data_object& bad )\n{ try {\n   FC_ASSERT( bitasset.is_market_issued() );\n   FC_ASSERT( bad.has_settlement() );\n   FC_ASSERT( !bad.is_prediction_market );\n\n   // cancel remaining bids\n   const auto& bid_idx = get_index_type< collateral_bid_index >().indices().get<by_price>();\n   auto itr = bid_idx.lower_bound( boost::make_tuple( bitasset.id,\n                                                      price::max( bad.options.short_backing_asset, bitasset.id ),\n                                                      collateral_bid_id_type() ) );\n   while( itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == bitasset.id )\n   {\n      const collateral_bid_object& bid = *itr;\n      ++itr;\n      cancel_bid( bid );\n   }\n\n   // revive\n   modify( bad, [&]( asset_bitasset_data_object& obj ){\n              obj.settlement_price = price();\n              obj.settlement_fund = 0;\n           });\n} FC_CAPTURE_AND_RETHROW( (bitasset) ) }\n\nvoid database::cancel_bid(const collateral_bid_object& bid, bool create_virtual_op)\n{\n   adjust_balance(bid.bidder, bid.inv_swan_price.base);\n\n   if( create_virtual_op )\n   {\n      bid_collateral_operation vop;\n      vop.bidder = bid.bidder;\n      vop.additional_collateral = bid.inv_swan_price.base;\n      vop.debt_covered = asset( 0, bid.inv_swan_price.quote.asset_id );\n      push_applied_operation( vop );\n   }\n   remove(bid);\n}\n\nvoid database::execute_bid( const collateral_bid_object& bid, share_type debt_covered, share_type collateral_from_fund,\n                            const price_feed& current_feed )\n{\n   const call_order_object& call_obj = create<call_order_object>( [&](call_order_object& call ){\n         call.borrower = bid.bidder;\n         call.collateral = bid.inv_swan_price.base.amount + collateral_from_fund;\n         call.debt = debt_covered;\n         // don't calculate call_price after core-1270 hard fork\n         if( get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_1270_TIME )\n            // bid.inv_swan_price is in collateral / debt\n            call.call_price = price( asset( 1, bid.inv_swan_price.base.asset_id ),\n                                     asset( 1, bid.inv_swan_price.quote.asset_id ) );\n         else\n            call.call_price = price::call_price( asset(debt_covered, bid.inv_swan_price.quote.asset_id),\n                                                 asset(call.collateral, bid.inv_swan_price.base.asset_id),\n                                                 current_feed.maintenance_collateral_ratio );\n      });\n\n   // Note: CORE asset in collateral_bid_object is not counted in account_stats.total_core_in_orders\n   if( bid.inv_swan_price.base.asset_id == asset_id_type() )\n      modify( get_account_stats_by_owner(bid.bidder), [&](account_statistics_object& stats) {\n         stats.total_core_in_orders += call_obj.collateral;\n      });\n\n   push_applied_operation( execute_bid_operation( bid.bidder, asset( call_obj.collateral, bid.inv_swan_price.base.asset_id ),\n                                                  asset( debt_covered, bid.inv_swan_price.quote.asset_id ) ) );\n\n   remove(bid);\n}\n\nvoid database::cancel_settle_order(const force_settlement_object& order, bool create_virtual_op)\n{\n   adjust_balance(order.owner, order.balance);\n\n   if( create_virtual_op )\n   {\n      asset_settle_cancel_operation vop;\n      vop.settlement = order.id;\n      vop.account = order.owner;\n      vop.amount = order.balance;\n      push_applied_operation( vop );\n   }\n   remove(order);\n}\n\nvoid database::cancel_limit_order( const limit_order_object& order, bool create_virtual_op, bool skip_cancel_fee )\n{\n   // if need to create a virtual op, try deduct a cancellation fee here.\n   // there are two scenarios when order is cancelled and need to create a virtual op:\n   // 1. due to expiration: always deduct a fee if there is any fee deferred\n   // 2. due to cull_small: deduct a fee after hard fork 604, but not before (will set skip_cancel_fee)\n   const account_statistics_object* seller_acc_stats = nullptr;\n   const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;\n   limit_order_cancel_operation vop;\n   share_type deferred_fee = order.deferred_fee;\n   asset deferred_paid_fee = order.deferred_paid_fee;\n   if( create_virtual_op )\n   {\n      vop.order = order.id;\n      vop.fee_paying_account = order.seller;\n      // only deduct fee if not skipping fee, and there is any fee deferred\n      if( !skip_cancel_fee && deferred_fee > 0 )\n      {\n         asset core_cancel_fee = current_fee_schedule().calculate_fee( vop );\n         // cap the fee\n         if( core_cancel_fee.amount > deferred_fee )\n            core_cancel_fee.amount = deferred_fee;\n         // if there is any CORE fee to deduct, redirect it to referral program\n         if( core_cancel_fee.amount > 0 )\n         {\n            seller_acc_stats = &order.seller( *this ).statistics( *this );\n            modify( *seller_acc_stats, [&]( account_statistics_object& obj ) {\n               obj.pay_fee( core_cancel_fee.amount, get_global_properties().parameters.cashback_vesting_threshold );\n            } );\n            deferred_fee -= core_cancel_fee.amount;\n            // handle originally paid fee if any:\n            //    to_deduct = round_up( paid_fee * core_cancel_fee / deferred_core_fee_before_deduct )\n            if( deferred_paid_fee.amount == 0 )\n            {\n               vop.fee = core_cancel_fee;\n            }\n            else\n            {\n               fc::uint128_t fee128( deferred_paid_fee.amount.value );\n               fee128 *= core_cancel_fee.amount.value;\n               // to round up\n               fee128 += order.deferred_fee.value;\n               fee128 -= 1;\n               fee128 /= order.deferred_fee.value;\n               share_type cancel_fee_amount = static_cast<int64_t>(fee128);\n               // cancel_fee should be positive, pay it to asset's accumulated_fees\n               fee_asset_dyn_data = &deferred_paid_fee.asset_id(*this).dynamic_asset_data_id(*this);\n               modify( *fee_asset_dyn_data, [&](asset_dynamic_data_object& addo) {\n                  addo.accumulated_fees += cancel_fee_amount;\n               });\n               // cancel_fee should be no more than deferred_paid_fee\n               deferred_paid_fee.amount -= cancel_fee_amount;\n               vop.fee = asset( cancel_fee_amount, deferred_paid_fee.asset_id );\n            }\n         }\n      }\n   }\n\n   // refund funds in order\n   auto refunded = order.amount_for_sale();\n   if( refunded.asset_id == asset_id_type() )\n   {\n      if( seller_acc_stats == nullptr )\n         seller_acc_stats = &order.seller( *this ).statistics( *this );\n      modify( *seller_acc_stats, [&]( account_statistics_object& obj ) {\n         obj.total_core_in_orders -= refunded.amount;\n      });\n   }\n   adjust_balance(order.seller, refunded);\n\n   // refund fee\n   // could be virtual op or real op here\n   if( order.deferred_paid_fee.amount == 0 )\n   {\n      // be here, order.create_time <= HARDFORK_CORE_604_TIME, or fee paid in CORE, or no fee to refund.\n      // if order was created before hard fork 604 then cancelled no matter before or after hard fork 604,\n      //    see it as fee paid in CORE, deferred_fee should be refunded to order owner but not fee pool\n      adjust_balance( order.seller, deferred_fee );\n   }\n   else // need to refund fee in originally paid asset\n   {\n      adjust_balance(order.seller, deferred_paid_fee);\n      // be here, must have: fee_asset != CORE\n      if( fee_asset_dyn_data == nullptr )\n         fee_asset_dyn_data = &deferred_paid_fee.asset_id(*this).dynamic_asset_data_id(*this);\n      modify( *fee_asset_dyn_data, [&](asset_dynamic_data_object& addo) {\n         addo.fee_pool += deferred_fee;\n      });\n   }\n\n   if( create_virtual_op )\n      push_applied_operation( vop );\n\n   remove(order);\n}\n\nbool maybe_cull_small_order( database& db, const limit_order_object& order )\n{\n   /**\n    *  There are times when the AMOUNT_FOR_SALE * SALE_PRICE == 0 which means that we\n    *  have hit the limit where the seller is asking for nothing in return.  When this\n    *  happens we must refund any balance back to the seller, it is too small to be\n    *  sold at the sale price.\n    *\n    *  If the order is a taker order (as opposed to a maker order), so the price is\n    *  set by the counterparty, this check is deferred until the order becomes unmatched\n    *  (see #555) -- however, detecting this condition is the responsibility of the caller.\n    */\n   if( order.amount_to_receive().amount == 0 )\n   {\n      if( order.deferred_fee > 0 && db.head_block_time() <= HARDFORK_CORE_604_TIME )\n      {\n         db.cancel_limit_order( order, true, true );\n      }\n      else\n         db.cancel_limit_order( order );\n      return true;\n   }\n   return false;\n}\n\nbool database::apply_order_before_hardfork_625(const limit_order_object& new_order_object, bool allow_black_swan)\n{\n   auto order_id = new_order_object.id;\n   const asset_object& sell_asset = get(new_order_object.amount_for_sale().asset_id);\n   const asset_object& receive_asset = get(new_order_object.amount_to_receive().asset_id);\n\n   // Possible optimization: We only need to check calls if both are true:\n   //  - The new order is at the front of the book\n   //  - The new order is below the call limit price\n   bool called_some = check_call_orders(sell_asset, allow_black_swan, true); // the first time when checking, call order is maker\n   called_some |= check_call_orders(receive_asset, allow_black_swan, true); // the other side, same as above\n   if( called_some && !find_object(order_id) ) // then we were filled by call order\n      return true;\n\n   const auto& limit_price_idx = get_index_type<limit_order_index>().indices().get<by_price>();\n\n   // TODO: it should be possible to simply check the NEXT/PREV iterator after new_order_object to\n   // determine whether or not this order has \"changed the book\" in a way that requires us to\n   // check orders. For now I just lookup the lower bound and check for equality... this is log(n) vs\n   // constant time check. Potential optimization.\n\n   auto max_price = ~new_order_object.sell_price;\n   auto limit_itr = limit_price_idx.lower_bound(max_price.max());\n   auto limit_end = limit_price_idx.upper_bound(max_price);\n\n   bool finished = false;\n   while( !finished && limit_itr != limit_end )\n   {\n      auto old_limit_itr = limit_itr;\n      ++limit_itr;\n      // match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop.\n      finished = (match(new_order_object, *old_limit_itr, old_limit_itr->sell_price) != 2);\n   }\n\n   //Possible optimization: only check calls if the new order completely filled some old order\n   //Do I need to check both assets?\n   check_call_orders(sell_asset, allow_black_swan); // after the new limit order filled some orders on the book,\n                                                    // if a call order matches another order, the call order is taker\n   check_call_orders(receive_asset, allow_black_swan); // the other side, same as above\n\n   const limit_order_object* updated_order_object = find< limit_order_object >( order_id );\n   if( updated_order_object == nullptr )\n      return true;\n   if( head_block_time() <= HARDFORK_555_TIME )\n      return false;\n   // before #555 we would have done maybe_cull_small_order() logic as a result of fill_order() being called by match() above\n   // however after #555 we need to get rid of small orders -- #555 hardfork defers logic that was done too eagerly before, and\n   // this is the point it's deferred to.\n   return maybe_cull_small_order( *this, *updated_order_object );\n}\n\n/***\n * @brief apply a new limit_order_object to the market, matching with existing limit orders or\n *    margin call orders where possible, leaving remainder on the book if not fully matched.\n * @detail Called from limit_order_create_evaluator::do_apply() in market_evaluator.cpp in\n *    response to a limit_order_create operation.  If we're not at the front of the book, we\n *    return false early and do nothing else, since there's nothing we can match.  If we are at\n *    the front of the book, then we first look for matching limit orders that are more\n *    favorable than the margin call price, then we search through active margin calls, then\n *    finaly the remaining limit orders, until we either fully consume the order or can no\n *    longer match and must leave the remainder on the book.\n * @return Returns true if limit order is completely consumed by matching, else false if it\n *    remains on the book.\n * @param new_order_object the new limit order (read only ref, though the corresponding db\n *    object is modified as we match and deleted if filled completely)\n * @param allow_black_swan ignored, defaulted to true (is used in the _before_hardfork_625\n *   variant of this function, but not this variant)\n */\nbool database::apply_order(const limit_order_object& new_order_object, bool allow_black_swan)\n{\n   auto order_id = new_order_object.id;\n   asset_id_type sell_asset_id = new_order_object.sell_asset_id();\n   asset_id_type recv_asset_id = new_order_object.receive_asset_id();\n\n   // We only need to check if the new order will match with others if it is at the front of the book\n   const auto& limit_price_idx = get_index_type<limit_order_index>().indices().get<by_price>();\n   auto limit_itr = limit_price_idx.lower_bound( boost::make_tuple( new_order_object.sell_price, order_id ) );\n   if( limit_itr != limit_price_idx.begin() )\n   {\n      --limit_itr;\n      if( limit_itr->sell_asset_id() == sell_asset_id && limit_itr->receive_asset_id() == recv_asset_id )\n         return false;\n   }\n\n   // this is the opposite side (on the book)\n   auto max_price = ~new_order_object.sell_price;\n   limit_itr = limit_price_idx.lower_bound( max_price.max() );\n   auto limit_end = limit_price_idx.upper_bound( max_price );\n\n   // Order matching should be in favor of the taker.\n   // When a new limit order is created, e.g. an ask, need to check if it will match the highest bid.\n   // We were checking call orders first. However, due to MSSR (maximum_short_squeeze_ratio),\n   // effective price of call orders may be worse than limit orders, so we should also check limit orders here.\n\n   // Question: will a new limit order trigger a black swan event?\n   //\n   // 1. as of writing, it's possible due to the call-order-and-limit-order overlapping issue:\n   //       https://github.com/bitshares/bitshares-core/issues/606 .\n   //    when it happens, a call order can be very big but don't match with the opposite,\n   //    even when price feed is too far away, further than swan price,\n   //    if the new limit order is in the same direction with the call orders, it can eat up all the opposite,\n   //    then the call order will lose support and trigger a black swan event.\n   // 2. after issue 606 is fixed, there will be no limit order on the opposite side \"supporting\" the call order,\n   //    so a new order in the same direction with the call order won't trigger a black swan event.\n   // 3. calling is one direction. if the new limit order is on the opposite direction,\n   //    no matter if matches with the call, it won't trigger a black swan event.\n   //    (if a match at MSSP caused a black swan event, it means the call order is already undercollateralized,\n   //      which should trigger a black swan event earlier.)\n   //\n   // Since it won't trigger a black swan, no need to check here.\n\n   // currently we don't do cross-market (triangle) matching.\n   // the limit order will only match with a call order if meet all of these:\n   // 1. it's buying collateral, which means sell_asset is the MIA, receive_asset is the backing asset.\n   // 2. sell_asset is not a prediction market\n   // 3. sell_asset is not globally settled\n   // 4. sell_asset has a valid price feed\n   // 5. the call order's collateral ratio is below or equals to MCR\n   // 6. the limit order provided a good price\n\n   auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n   bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue\n\n   bool to_check_call_orders = false;\n   const asset_object& sell_asset = sell_asset_id( *this );\n   const asset_bitasset_data_object* sell_abd = nullptr;\n   price call_match_price;  // Price at which margin calls sit on the books. Prior to BSIP-74 this price is\n                            // same as the MSSP. After, it is the MCOP, which may deviate from MSSP due to MCFR.\n   price call_pays_price;   // Price margin call actually relinquishes collateral at. Equals the MSSP and it may\n                            // differ from call_match_price if there is a Margin Call Fee.\n   if( sell_asset.is_market_issued() )\n   {\n      sell_abd = &sell_asset.bitasset_data( *this );\n      if( sell_abd->options.short_backing_asset == recv_asset_id\n          && !sell_abd->is_prediction_market\n          && !sell_abd->has_settlement()\n          && !sell_abd->current_feed.settlement_price.is_null() )\n      {\n         if( before_core_hardfork_1270 ) {\n            call_match_price = ~sell_abd->current_feed.max_short_squeeze_price_before_hf_1270();\n            call_pays_price = call_match_price;\n         } else {\n            call_match_price = ~sell_abd->current_feed.\n               margin_call_order_price(sell_abd->options.extensions.value.margin_call_fee_ratio);\n            call_pays_price = ~sell_abd->current_feed.max_short_squeeze_price();\n         }\n         if( ~new_order_object.sell_price <= call_match_price ) // If new limit order price is good enough to\n            to_check_call_orders = true;                        // match a call, then check if there are calls.\n      }\n   }\n\n   bool finished = false; // whether the new order is gone\n   if( to_check_call_orders )\n   {\n      // check limit orders first, match the ones with better price in comparison to call orders\n      while( !finished && limit_itr != limit_end && limit_itr->sell_price > call_match_price )\n      {\n         auto old_limit_itr = limit_itr;\n         ++limit_itr;\n         // match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop.\n         finished = ( match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 );\n      }\n\n      if( !finished && !before_core_hardfork_1270 ) // TODO refactor or cleanup duplicate code after core-1270 hard fork\n      {\n         // check if there are margin calls\n         const auto& call_collateral_idx = get_index_type<call_order_index>().indices().get<by_collateral>();\n         auto call_min = price::min( recv_asset_id, sell_asset_id );\n         while( !finished )\n         {\n            // hard fork core-343 and core-625 took place at same time,\n            // always check call order with least collateral ratio\n            auto call_itr = call_collateral_idx.lower_bound( call_min );\n            if( call_itr == call_collateral_idx.end()\n                  || call_itr->debt_type() != sell_asset_id\n                  // feed protected https://github.com/cryptonomex/graphene/issues/436\n                  || call_itr->collateralization() > sell_abd->current_maintenance_collateralization )\n               break;\n            // hard fork core-338 and core-625 took place at same time, not checking HARDFORK_CORE_338_TIME here.\n            int match_result = match( new_order_object, *call_itr, call_match_price,\n                                      sell_abd->current_feed.settlement_price,\n                                      sell_abd->current_feed.maintenance_collateral_ratio,\n                                      sell_abd->current_maintenance_collateralization,\n                                      call_pays_price);\n            // match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching.\n            // since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2.\n            if( match_result == 1 || match_result == 3 )\n               finished = true;\n         }\n      }\n      else if( !finished ) // and before core-1270 hard fork\n      {\n         // check if there are margin calls\n         const auto& call_price_idx = get_index_type<call_order_index>().indices().get<by_price>();\n         auto call_min = price::min( recv_asset_id, sell_asset_id );\n         while( !finished )\n         {\n            // assume hard fork core-343 and core-625 will take place at same time, always check call order with least call_price\n            auto call_itr = call_price_idx.lower_bound( call_min );\n            if( call_itr == call_price_idx.end()\n                  || call_itr->debt_type() != sell_asset_id\n                  // feed protected https://github.com/cryptonomex/graphene/issues/436\n                  || call_itr->call_price > ~sell_abd->current_feed.settlement_price )\n               break;\n            // assume hard fork core-338 and core-625 will take place at same time, not checking HARDFORK_CORE_338_TIME here.\n            int match_result = match( new_order_object, *call_itr, call_match_price,\n                                      sell_abd->current_feed.settlement_price,\n                                      sell_abd->current_feed.maintenance_collateral_ratio,\n                                      optional<price>() );\n            // match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching.\n            // since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2.\n            if( match_result == 1 || match_result == 3 )\n               finished = true;\n         }\n      }\n   }\n\n   // still need to check limit orders\n   while( !finished && limit_itr != limit_end )\n   {\n      auto old_limit_itr = limit_itr;\n      ++limit_itr;\n      // match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop.\n      finished = ( match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 );\n   }\n\n   const limit_order_object* updated_order_object = find< limit_order_object >( order_id );\n   if( updated_order_object == nullptr )\n      return true;\n\n   // before #555 we would have done maybe_cull_small_order() logic as a result of fill_order() being called by match() above\n   // however after #555 we need to get rid of small orders -- #555 hardfork defers logic that was done too eagerly before, and\n   // this is the point it's deferred to.\n   return maybe_cull_small_order( *this, *updated_order_object );\n}\n\n/**\n *  Matches the two orders, the first parameter is taker, the second is maker.\n *\n *  @return a bit field indicating which orders were filled (and thus removed)\n *\n *  0 - no orders were matched\n *  1 - taker was filled\n *  2 - maker was filled\n *  3 - both were filled\n */\nint database::match( const limit_order_object& usd, const limit_order_object& core, const price& match_price )\n{\n   FC_ASSERT( usd.sell_price.quote.asset_id == core.sell_price.base.asset_id );\n   FC_ASSERT( usd.sell_price.base.asset_id  == core.sell_price.quote.asset_id );\n   FC_ASSERT( usd.for_sale > 0 && core.for_sale > 0 );\n\n   auto usd_for_sale = usd.amount_for_sale();\n   auto core_for_sale = core.amount_for_sale();\n\n   asset usd_pays, usd_receives, core_pays, core_receives;\n\n   auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n   bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding\n\n   bool cull_taker = false;\n   if( usd_for_sale <= core_for_sale * match_price ) // rounding down here should be fine\n   {\n      usd_receives  = usd_for_sale * match_price; // round down, in favor of bigger order\n\n      // Be here, it's possible that taker is paying something for nothing due to partially filled in last loop.\n      // In this case, we see it as filled and cancel it later\n      if( usd_receives.amount == 0 && maint_time > HARDFORK_CORE_184_TIME )\n         return 1;\n\n      if( before_core_hardfork_342 )\n         core_receives = usd_for_sale;\n      else\n      {\n         // The remaining amount in order `usd` would be too small,\n         //   so we should cull the order in fill_limit_order() below.\n         // The order would receive 0 even at `match_price`, so it would receive 0 at its own price,\n         //   so calling maybe_cull_small() will always cull it.\n         core_receives = usd_receives.multiply_and_round_up( match_price );\n         cull_taker = true;\n      }\n   }\n   else\n   {\n      //This line once read: assert( core_for_sale < usd_for_sale * match_price );\n      //This assert is not always true -- see trade_amount_equals_zero in operation_tests.cpp\n      //Although usd_for_sale is greater than core_for_sale * match_price, core_for_sale == usd_for_sale * match_price\n      //Removing the assert seems to be safe -- apparently no asset is created or destroyed.\n\n      // The maker won't be paying something for nothing, since if it would, it would have been cancelled already.\n      core_receives = core_for_sale * match_price; // round down, in favor of bigger order\n      if( before_core_hardfork_342 )\n         usd_receives = core_for_sale;\n      else\n         // The remaining amount in order `core` would be too small,\n         //   so the order will be culled in fill_limit_order() below\n         usd_receives = core_receives.multiply_and_round_up( match_price );\n   }\n\n   core_pays = usd_receives;\n   usd_pays  = core_receives;\n\n   if( before_core_hardfork_342 )\n      FC_ASSERT( usd_pays == usd.amount_for_sale() ||\n                 core_pays == core.amount_for_sale() );\n\n   int result = 0;\n   result |= fill_limit_order( usd, usd_pays, usd_receives, cull_taker, match_price, false ); // the first param is taker\n   result |= fill_limit_order( core, core_pays, core_receives, true, match_price, true ) << 1; // the second param is maker\n   FC_ASSERT( result != 0 );\n   return result;\n}\n\nint database::match( const limit_order_object& bid, const call_order_object& ask, const price& match_price,\n                     const price& feed_price, const uint16_t maintenance_collateral_ratio,\n                     const optional<price>& maintenance_collateralization,\n                     const price& call_pays_price )\n{\n   FC_ASSERT( bid.sell_asset_id() == ask.debt_type() );\n   FC_ASSERT( bid.receive_asset_id() == ask.collateral_type() );\n   FC_ASSERT( bid.for_sale > 0 && ask.debt > 0 && ask.collateral > 0 );\n\n   bool cull_taker = false;\n\n   asset usd_for_sale = bid.amount_for_sale();\n   asset usd_to_buy   = asset( ask.get_max_debt_to_cover( call_pays_price, feed_price,\n         maintenance_collateral_ratio,  maintenance_collateralization ), ask.debt_type() );\n\n   asset call_pays, call_receives, order_pays, order_receives;\n   if( usd_to_buy > usd_for_sale )\n   {  // fill limit order\n      order_receives  = usd_for_sale * match_price; // round down here, in favor of call order\n      call_pays       = usd_for_sale * call_pays_price; // (same as match_price until BSIP-74)\n\n      // Be here, it's possible that taker is paying something for nothing due to partially filled in last loop.\n      // In this case, we see it as filled and cancel it later\n      if( order_receives.amount == 0 )\n         return 1;\n\n      // The remaining amount in the limit order would be too small,\n      //   so we should cull the order in fill_limit_order() below.\n      // The order would receive 0 even at `match_price`, so it would receive 0 at its own price,\n      //   so calling maybe_cull_small() will always cull it.\n      call_receives = order_receives.multiply_and_round_up( match_price );\n      cull_taker = true;\n   }\n   else\n   {  // fill call order\n      call_receives  = usd_to_buy;\n      order_receives = usd_to_buy.multiply_and_round_up( match_price ); // round up here, in favor of limit order\n      call_pays      = usd_to_buy.multiply_and_round_up( call_pays_price );\n   }\n   order_pays = call_receives;\n\n   // Compute margin call fee (BSIP74). Difference between what the call order pays and the limit order\n   // receives is the margin call fee that is paid by the call order owner to the asset issuer.\n   // Margin call fee should equal = X*MCFR/settle_price, to within rounding error.\n   FC_ASSERT(call_pays >= order_receives);\n   const asset margin_call_fee = call_pays - order_receives;\n\n   int result = 0;\n   result |= fill_limit_order( bid, order_pays, order_receives, cull_taker, match_price, false ); // taker\n   result |= fill_call_order( ask, call_pays, call_receives, match_price, true, margin_call_fee ) << 1; // maker\n   // result can be 0 when call order has target_collateral_ratio option set.\n\n   return result;\n}\n\n\nasset database::match( const call_order_object& call, \n                       const force_settlement_object& settle, \n                       const price& match_price,\n                       asset max_settlement,\n                       const price& fill_price )\n{ try {\n   FC_ASSERT(call.get_debt().asset_id == settle.balance.asset_id );\n   FC_ASSERT(call.debt > 0 && call.collateral > 0 && settle.balance.amount > 0);\n\n   auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n   bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding\n\n   auto settle_for_sale = std::min(settle.balance, max_settlement);\n   auto call_debt = call.get_debt();\n   auto call_collateral = call.get_collateral();\n\n   asset call_receives   = std::min(settle_for_sale, call_debt);\n   asset call_pays       = call_receives * match_price; // round down here, in favor of call order, for first check\n                                                        // TODO possible optimization: check need to round up or down first\n\n   // Be here, the call order may be paying nothing.\n   bool cull_settle_order = false; // whether need to cancel dust settle order\n   if( call_pays.amount == 0 )\n   {\n      if( maint_time > HARDFORK_CORE_184_TIME )\n      {\n         if( call_receives == call_debt ) // the call order is smaller than or equal to the settle order\n         {\n            call_pays.amount = 1;\n         }\n         else\n         {\n            if( call_receives == settle.balance ) // the settle order is smaller\n            {\n               cancel_settle_order( settle );\n            }\n            // else do nothing: neither order will be completely filled, perhaps due to max_settlement too small\n\n            return asset( 0, settle.balance.asset_id );\n         }\n      }\n\n   }\n   else // the call order is not paying nothing, but still possible it's paying more than minimum required due to rounding\n   {\n      if( !before_core_hardfork_342 )\n      {\n         if( call_receives == call_debt ) // the call order is smaller than or equal to the settle order\n         {\n            call_pays = call_receives.multiply_and_round_up( match_price ); // round up here, in favor of settle order\n            // be here, we should have: call_pays <= \n            \n            if ( call_pays.amount > call.collateral )\n            {\n               call_pays.amount = call.collateral;\n            }\n            \n         }\n         else\n         {\n            // be here, call_pays has been rounded down\n\n            // be here, we should have: call_pays <= call_collateral\n\n            if( call_receives == settle.balance ) // the settle order will be completely filled, assuming we need to cull it\n               cull_settle_order = true;\n            // else do nothing, since we can't cull the settle order\n\n            call_receives = call_pays.multiply_and_round_up( match_price ); // round up here to mitigate rounding issue (core-342).\n                                                                            // It is important to understand here that the newly\n                                                                            // rounded up call_receives won't be greater than the\n                                                                            // old call_receives.\n\n            if( call_receives == settle.balance ) // the settle order will be completely filled, no need to cull\n               cull_settle_order = false;\n            // else do nothing, since we still need to cull the settle order or still can't cull the settle order\n         }\n      }\n   }\n\n   asset settle_pays     = call_receives;\n   asset settle_receives = call_pays;\n\n   /**\n    *  If the least collateralized call position lacks sufficient\n    *  collateral to cover at the match price then this indicates a black \n    *  swan event according to the price feed, but only the market \n    *  can trigger a black swan.  So now we must cancel the forced settlement\n    *  object.\n    */\n   if( before_core_hardfork_342 )\n   {\n      auto call_collateral = call.get_collateral();\n      GRAPHENE_ASSERT( call_pays < call_collateral, black_swan_exception, \"\" );\n\n      assert( settle_pays == settle_for_sale || call_receives == call.get_debt() );\n   }\n   // else do nothing, since black swan event won't happen, and the assertion is no longer true\n\n   fill_call_order( call, call_pays, call_receives, fill_price, true ); // call order is maker\n   fill_settle_order( settle, settle_pays, settle_receives, fill_price, false ); // force settlement order is taker\n\n   if( cull_settle_order )\n      cancel_settle_order( settle );\n\n   return call_receives;\n} FC_CAPTURE_AND_RETHROW( (call)(settle)(match_price)(max_settlement) ) }\n\nbool database::fill_limit_order( const limit_order_object& order, const asset& pays, const asset& receives, bool cull_if_small,\n                           const price& fill_price, const bool is_maker)\n{ try {\n   cull_if_small |= (head_block_time() < HARDFORK_555_TIME);\n\n   FC_ASSERT( order.amount_for_sale().asset_id == pays.asset_id );\n   FC_ASSERT( pays.asset_id != receives.asset_id );\n\n   const account_object& seller = order.seller(*this);\n\n   const auto issuer_fees = pay_market_fees(&seller, receives.asset_id(*this), receives, is_maker);\n\n   pay_order( seller, receives - issuer_fees, pays );\n\n   assert( pays.asset_id != receives.asset_id );\n   push_applied_operation( fill_order_operation( order.id, order.seller, pays, receives, issuer_fees, fill_price, is_maker ) );\n\n   // BSIP85: Maker order creation fee discount, https://github.com/bitshares/bsips/blob/master/bsip-0085.md\n   //   if the order creation fee was paid in BTS,\n   //     return round_down(deferred_fee * maker_fee_discount_percent) to the owner,\n   //     then process the remaining deferred fee as before;\n   //   if the order creation fee was paid in another asset,\n   //     return round_down(deferred_paid_fee * maker_fee_discount_percent) to the owner,\n   //     return round_down(deferred_fee * maker_fee_discount_percent) to the fee pool of the asset,\n   //     then process the remaining deferred fee and deferred paid fee as before.\n   const uint16_t maker_discount_percent = get_global_properties().parameters.get_maker_fee_discount_percent();\n\n   // Save local copies for calculation\n   share_type deferred_fee = order.deferred_fee;\n   share_type deferred_paid_fee = order.deferred_paid_fee.amount;\n\n   // conditional because cheap integer comparison may allow us to avoid two expensive modify() and object lookups\n   if( order.deferred_paid_fee.amount > 0 ) // implies head_block_time() > HARDFORK_CORE_604_TIME\n   {\n      share_type fee_pool_refund = 0;\n      if( is_maker && maker_discount_percent > 0 )\n      {\n         share_type refund = detail::calculate_percent( deferred_paid_fee, maker_discount_percent );\n         // Note: it's possible that the deferred_paid_fee is very small,\n         //       which can result in a zero refund due to rounding issue,\n         //       in this case, no refund to the fee pool\n         if( refund > 0 )\n         {\n            FC_ASSERT( refund <= deferred_paid_fee, \"Internal error\" );\n            adjust_balance( order.seller, asset(refund, order.deferred_paid_fee.asset_id) );\n            deferred_paid_fee -= refund;\n\n            // deferred_fee might be positive too\n            FC_ASSERT( deferred_fee > 0, \"Internal error\" );\n            fee_pool_refund = detail::calculate_percent( deferred_fee, maker_discount_percent );\n            FC_ASSERT( fee_pool_refund <= deferred_fee, \"Internal error\" );\n            deferred_fee -= fee_pool_refund;\n         }\n      }\n\n      const auto& fee_asset_dyn_data = order.deferred_paid_fee.asset_id(*this).dynamic_asset_data_id(*this);\n      modify( fee_asset_dyn_data, [deferred_paid_fee,fee_pool_refund](asset_dynamic_data_object& addo) {\n         addo.accumulated_fees += deferred_paid_fee;\n         addo.fee_pool += fee_pool_refund;\n      });\n   }\n\n   if( order.deferred_fee > 0 )\n   {\n      if( order.deferred_paid_fee.amount <= 0 // paid in CORE, or before HF 604\n            && is_maker && maker_discount_percent > 0 )\n      {\n         share_type refund = detail::calculate_percent( deferred_fee, maker_discount_percent );\n         if( refund > 0 )\n         {\n            FC_ASSERT( refund <= deferred_fee, \"Internal error\" );\n            adjust_balance( order.seller, asset(refund, asset_id_type()) );\n            deferred_fee -= refund;\n         }\n      }\n      // else do nothing here, because we have already processed it above, or no need to process\n\n      if( deferred_fee > 0 )\n      {\n         modify( seller.statistics(*this), [deferred_fee,this]( account_statistics_object& statistics )\n         {\n            statistics.pay_fee( deferred_fee, get_global_properties().parameters.cashback_vesting_threshold );\n         } );\n      }\n   }\n\n   if( pays == order.amount_for_sale() )\n   {\n      remove( order );\n      return true;\n   }\n   else\n   {\n      modify( order, [&pays]( limit_order_object& b ) {\n                             b.for_sale -= pays.amount;\n                             b.deferred_fee = 0;\n                             b.deferred_paid_fee.amount = 0;\n                          });\n      if( cull_if_small )\n         return maybe_cull_small_order( *this, order );\n      return false;\n   }\n} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }\n\n/***\n * @brief fill a call order in the specified amounts\n * @param order the call order\n * @param pays What the call order will give to the other party (collateral)\n * @param receives what the call order will receive from the other party (debt)\n * @param fill_price the price at which the call order will execute\n * @param is_maker TRUE if the call order is the maker, FALSE if it is the taker\n * @param margin_call_fee Margin call fees paid in collateral asset\n * @returns TRUE if the call order was completely filled\n */\nbool database::fill_call_order( const call_order_object& order, const asset& pays, const asset& receives,\n      const price& fill_price, const bool is_maker, const asset& margin_call_fee )\n{ try {\n   FC_ASSERT( order.debt_type() == receives.asset_id );\n   FC_ASSERT( order.collateral_type() == pays.asset_id );\n   FC_ASSERT( order.collateral >= pays.amount );\n\n   // TODO pass in mia and bitasset_data for better performance\n   const asset_object& mia = receives.asset_id(*this);\n   FC_ASSERT( mia.is_market_issued() );\n   const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);\n\n   optional<asset> collateral_freed;\n   // adjust the order\n   modify( order, [&]( call_order_object& o ) {\n         o.debt       -= receives.amount;\n         o.collateral -= pays.amount;\n         if( o.debt == 0 ) // is the whole debt paid?\n         {\n            collateral_freed = o.get_collateral();\n            o.collateral = 0;\n         }\n         else // the debt was not completely paid\n         {\n            auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n            // update call_price after core-343 hard fork,\n            // but don't update call_price after core-1270 hard fork\n            if( maint_time <= HARDFORK_CORE_1270_TIME && maint_time > HARDFORK_CORE_343_TIME )\n            {\n               o.call_price = price::call_price( o.get_debt(), o.get_collateral(),\n                     bitasset.current_feed.maintenance_collateral_ratio );\n            }\n         }\n      });\n\n   // update current supply\n   const asset_dynamic_data_object& mia_ddo = mia.dynamic_asset_data_id(*this);\n   modify( mia_ddo, [&receives]( asset_dynamic_data_object& ao ){\n         ao.current_supply -= receives.amount;\n      });\n\n   // If the whole debt is paid, adjust borrower's collateral balance\n   if( collateral_freed.valid() )\n      adjust_balance( order.borrower, *collateral_freed );\n\n   // Update account statistics. We know that order.collateral_type() == pays.asset_id\n   if( pays.asset_id == asset_id_type() )\n   {\n      modify( get_account_stats_by_owner(order.borrower), [&collateral_freed,&pays]( account_statistics_object& b ){\n         b.total_core_in_orders -= pays.amount;\n         if( collateral_freed.valid() )\n            b.total_core_in_orders -= collateral_freed->amount;\n      });\n   }\n\n   // BSIP74: Accumulate the collateral-denominated fee\n   if (margin_call_fee.amount.value != 0)\n      mia.accumulate_fee(*this, margin_call_fee);\n\n   // virtual operation for account history\n   push_applied_operation( fill_order_operation( order.id, order.borrower, pays, receives,\n         margin_call_fee, fill_price, is_maker ) );\n\n   // Call order completely filled, remove it\n   if( collateral_freed.valid() )\n      remove( order );\n\n   return collateral_freed.valid();\n} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }\n\n/***\n * @brief fullfill a settle order in the specified amounts\n *\n * @details Called from database::match(), this coordinates exchange of debt asset X held in the\n *    settle order for collateral asset Y held in a call order, and routes fees.  Note that we\n *    don't touch the call order directly, as match() handles this via a separate call to\n *    fill_call_order().  We are told exactly how much X and Y to exchange, based on details of\n *    order matching determined higher up the call chain. Thus it is possible that the settle\n *    order is not completely satisfied at the conclusion of this function.\n *\n * @param settle the force_settlement object\n * @param pays the quantity of market-issued debt asset X which the settler will yield in this\n *    round (may be less than the full amount indicated in settle object)\n * @param receives the quantity of collateral asset Y which the settler will receive in\n *    exchange for X\n * @param fill_price the price at which the settle order will execute (not used - passed through\n *    to virtual operation)\n * @param is_maker TRUE if the settle order is the maker, FALSE if it is the taker (passed\n *    through to virtual operation)\n * @returns TRUE if the settle order was completely filled, FALSE if only partially filled\n */\nbool database::fill_settle_order( const force_settlement_object& settle, const asset& pays, const asset& receives,\n                                  const price& fill_price, const bool is_maker )\n{ try {\n   bool filled = false;\n\n   const account_object* settle_owner_ptr = nullptr;\n   // The owner of the settle order pays market fees to the issuer of the collateral asset.\n   // After HF core-1780, these fees are shared to the referral program, which is flagged to\n   // pay_market_fees by setting settle_owner_ptr non-null.\n   //\n   // TODO Check whether the HF check can be removed after the HF.\n   //      Note: even if logically it can be removed, perhaps the removal will lead to a small performance\n   //            loss. Needs testing.\n   if( head_block_time() >= HARDFORK_CORE_1780_TIME )\n      settle_owner_ptr = &settle.owner(*this);\n   // Compute and pay the market fees:\n   asset market_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives, is_maker );\n\n   // Issuer of the settled smartcoin asset lays claim to a force-settlement fee (BSIP87), but\n   // note that fee is denominated in collateral asset, not the debt asset.  Asset object of\n   // debt asset is passed to the pay function so it knows where to put the fee. Note that\n   // amount of collateral asset upon which fee is assessed is reduced by market_fees already\n   // paid to prevent the total fee exceeding total collateral.\n   asset force_settle_fees = pay_force_settle_fees( get(pays.asset_id), receives - market_fees );\n\n   auto total_collateral_denominated_fees = market_fees + force_settle_fees;\n\n   // If we don't consume entire settle order:\n   if( pays < settle.balance )\n   {\n      modify(settle, [&pays](force_settlement_object& s) {\n         s.balance -= pays;\n      });\n   } else {\n      filled = true;\n   }\n   // Give released collateral not already taken as fees to settle order owner:\n   adjust_balance(settle.owner, receives - total_collateral_denominated_fees);\n\n   assert( pays.asset_id != receives.asset_id );\n   push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives,\n                                                 total_collateral_denominated_fees, fill_price, is_maker ) );\n\n   if (filled)\n      remove(settle);\n\n   return filled;\n\n} FC_CAPTURE_AND_RETHROW( (settle)(pays)(receives) ) }\n\n/**\n *  Starting with the least collateralized orders, fill them if their\n *  call price is above the max(lowest bid,call_limit).\n *\n *  This method will return true if it filled a short or limit\n *\n *  @param mia - the market issued asset that should be called.\n *  @param enable_black_swan - when adjusting collateral, triggering a black swan is invalid and will throw\n *                             if enable_black_swan is not set to true.\n *  @param for_new_limit_order - true if this function is called when matching call orders with a new\n *     limit order. (Only relevent before hardfork 625. apply_order_before_hardfork_625() is only\n *     function that calls this with for_new_limit_order true.)\n *  @param bitasset_ptr - an optional pointer to the bitasset_data object of the asset\n *\n *  @return true if a margin call was executed.\n */\nbool database::check_call_orders( const asset_object& mia, bool enable_black_swan, bool for_new_limit_order,\n                                  const asset_bitasset_data_object* bitasset_ptr )\n{ try {\n    const auto& dyn_prop = get_dynamic_global_properties();\n    auto maint_time = dyn_prop.next_maintenance_time;\n    if( for_new_limit_order )\n       FC_ASSERT( maint_time <= HARDFORK_CORE_625_TIME ); // `for_new_limit_order` is only true before HF 338 / 625\n\n    if( !mia.is_market_issued() ) return false;\n\n    const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) );\n    \n    // price feeds can cause black swans in prediction markets\n    // The hardfork check may be able to be removed after the hardfork date\n    // if check_for_blackswan never triggered a black swan on a prediction market.\n    // NOTE: check_for_blackswan returning true does not always mean a black\n    // swan was triggered.\n    if ( maint_time >= HARDFORK_CORE_460_TIME && bitasset.is_prediction_market )\n       return false;\n\n    if( check_for_blackswan( mia, enable_black_swan, &bitasset ) )\n       return false;\n\n    if( bitasset.is_prediction_market ) return false;\n    if( bitasset.current_feed.settlement_price.is_null() ) return false;\n\n    const limit_order_index& limit_index = get_index_type<limit_order_index>();\n    const auto& limit_price_index = limit_index.indices().get<by_price>();\n\n    bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue\n\n    // Looking for limit orders selling the most USD for the least CORE.\n    auto max_price = price::max( mia.id, bitasset.options.short_backing_asset );\n    // Stop when limit orders are selling too little USD for too much CORE.\n    // Note that since BSIP74, margin calls offer somewhat less CORE per USD\n    // if the issuer claims a Margin Call Fee.\n    auto min_price = ( before_core_hardfork_1270 ?\n                         bitasset.current_feed.max_short_squeeze_price_before_hf_1270()\n                       : bitasset.current_feed.margin_call_order_price(\n                                      bitasset.options.extensions.value.margin_call_fee_ratio )\n                     );\n\n    // NOTE limit_price_index is sorted from greatest to least\n    auto limit_itr = limit_price_index.lower_bound( max_price );\n    auto limit_end = limit_price_index.upper_bound( min_price );\n\n    if( limit_itr == limit_end )\n       return false;\n\n    const call_order_index& call_index = get_index_type<call_order_index>();\n    const auto& call_price_index = call_index.indices().get<by_price>();\n    const auto& call_collateral_index = call_index.indices().get<by_collateral>();\n\n    auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );\n    auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );\n\n    auto call_price_itr = call_price_index.begin();\n    auto call_price_end = call_price_itr;\n    auto call_collateral_itr = call_collateral_index.begin();\n    auto call_collateral_end = call_collateral_itr;\n\n    if( before_core_hardfork_1270 )\n    {\n       call_price_itr = call_price_index.lower_bound( call_min );\n       call_price_end = call_price_index.upper_bound( call_max );\n    }\n    else\n    {\n       call_collateral_itr = call_collateral_index.lower_bound( call_min );\n       call_collateral_end = call_collateral_index.upper_bound( call_max );\n    }\n\n    bool filled_limit = false;\n    bool margin_called = false;         // toggles true once/if we actually execute a margin call\n\n    auto head_time = head_block_time();\n    auto head_num = head_block_num();\n\n    bool before_hardfork_615 = ( head_time < HARDFORK_615_TIME );\n    bool after_hardfork_436 = ( head_time > HARDFORK_436_TIME );\n\n    bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding\n    bool before_core_hardfork_343 = ( maint_time <= HARDFORK_CORE_343_TIME ); // update call_price after partially filled\n    bool before_core_hardfork_453 = ( maint_time <= HARDFORK_CORE_453_TIME ); // multiple matching issue\n    bool before_core_hardfork_606 = ( maint_time <= HARDFORK_CORE_606_TIME ); // feed always trigger call\n    bool before_core_hardfork_834 = ( maint_time <= HARDFORK_CORE_834_TIME ); // target collateral ratio option\n\n    while( !check_for_blackswan( mia, enable_black_swan, &bitasset ) // TODO perhaps improve performance by passing in iterators\n           && limit_itr != limit_end\n           && ( ( !before_core_hardfork_1270 && call_collateral_itr != call_collateral_end )\n              || ( before_core_hardfork_1270 && call_price_itr != call_price_end ) ) )\n    {\n       bool  filled_call      = false;\n\n       const call_order_object& call_order = ( before_core_hardfork_1270 ? *call_price_itr : *call_collateral_itr );\n\n       // Feed protected (don't call if CR>MCR) https://github.com/cryptonomex/graphene/issues/436\n       if( ( !before_core_hardfork_1270 && bitasset.current_maintenance_collateralization < call_order.collateralization() )\n             || ( before_core_hardfork_1270\n                   && after_hardfork_436 && bitasset.current_feed.settlement_price > ~call_order.call_price ) )\n          return margin_called;\n\n       const limit_order_object& limit_order = *limit_itr;\n\n       price match_price  = limit_order.sell_price;\n       // There was a check `match_price.validate();` here, which is removed now because it always passes\n       price call_pays_price = match_price * bitasset.current_feed.margin_call_pays_ratio(\n                                                bitasset.options.extensions.value.margin_call_fee_ratio);\n       // Since BSIP74, the call \"pays\" a bit more collateral per debt than the match price, with the\n       // excess being kept by the asset issuer as a margin call fee. In what follows, we use\n       // call_pays_price for the black swan check, and for the TCR, but we still use the match_price,\n       // of course, to determine what the limit order receives.  Note margin_call_pays_ratio() returns\n       // 1/1 if margin_call_fee_ratio is unset (i.e. before BSIP74), so hardfork check is implicit.\n\n       // Old rule: margin calls can only buy high https://github.com/bitshares/bitshares-core/issues/606\n       if( before_core_hardfork_606 && match_price > ~call_order.call_price )\n          return margin_called;\n\n       margin_called = true;\n\n       // Although we checked for black swan above, we do one more check to ensure the call order can\n       // pay the amount of collateral which we intend to take from it (including margin call fee).  I\n       // guess this is just a sanity check, as, I'm not sure how we'd get here without it being\n       // detected in the prior swan check, aside perhaps for rounding errors. Or maybe there was some\n       // way prior to hf_1270.\n       auto usd_to_buy = call_order.get_debt();\n       if( usd_to_buy * call_pays_price > call_order.get_collateral() )\n       {\n          elog( \"black swan detected on asset ${symbol} (${id}) at block ${b}\",\n                (\"id\",mia.id)(\"symbol\",mia.symbol)(\"b\",head_num) );\n          edump((enable_black_swan));\n          FC_ASSERT( enable_black_swan );\n          globally_settle_asset(mia, bitasset.current_feed.settlement_price );\n          return true;\n       }\n\n       if( !before_core_hardfork_1270 )\n       {\n          usd_to_buy.amount = call_order.get_max_debt_to_cover( call_pays_price,\n                                                                bitasset.current_feed.settlement_price,\n                                                                bitasset.current_feed.maintenance_collateral_ratio,\n                                                                bitasset.current_maintenance_collateralization );\n       }\n       else if( !before_core_hardfork_834 )\n       {\n          usd_to_buy.amount = call_order.get_max_debt_to_cover( call_pays_price,\n                                                                bitasset.current_feed.settlement_price,\n                                                                bitasset.current_feed.maintenance_collateral_ratio );\n       }\n\n       asset usd_for_sale = limit_order.amount_for_sale();\n       asset call_pays, call_receives, limit_pays, limit_receives;\n       if( usd_to_buy > usd_for_sale )\n       {  // fill order\n          limit_receives  = usd_for_sale * match_price; // round down, in favor of call order\n          call_pays       = usd_for_sale * call_pays_price; // (same as match_price until BSIP-74)\n\n          // Be here, the limit order won't be paying something for nothing, since if it would, it would have\n          //   been cancelled elsewhere already (a maker limit order won't be paying something for nothing):\n          // * after hard fork core-625, the limit order will be always a maker if entered this function;\n          // * before hard fork core-625,\n          //   * when the limit order is a taker, it could be paying something for nothing only when\n          //     the call order is smaller and is too small\n          //   * when the limit order is a maker, it won't be paying something for nothing\n\n          if( before_core_hardfork_342 )\n             call_receives = usd_for_sale;\n          else\n             // The remaining amount in the limit order would be too small,\n             //   so we should cull the order in fill_limit_order() below.\n             // The order would receive 0 even at `match_price`, so it would receive 0 at its own price,\n             //   so calling maybe_cull_small() will always cull it.\n             call_receives = limit_receives.multiply_and_round_up( match_price );\n\n          filled_limit = true;\n\n       } else { // fill call\n          call_receives  = usd_to_buy;\n\n               if( before_core_hardfork_342 )\n               {\n                  limit_receives = usd_to_buy * match_price; // round down, in favor of call order\n                  call_pays = limit_receives;\n               } else {\n                  call_pays      = usd_to_buy.multiply_and_round_up( call_pays_price ); // BSIP74; excess is fee.\n                  // Note: Due to different rounding, this could potentialy be\n                  //       one satoshi more than the blackswan check above\n                  if( call_pays.amount > call_order.collateral )\n                  {\n                        call_pays.amount = call_order.collateral;\n                  }\n                  // Note: if it is a partial fill due to TCR, the math guarantees that the new CR will be higher\n                  //       than the old CR, so no additional check for potential blackswan here\n\n                  limit_receives = usd_to_buy.multiply_and_round_up( match_price ); // round up, favors limit order\n                  if( limit_receives.amount > call_order.collateral )\n                     limit_receives.amount = call_order.collateral;\n               }\n\n          filled_call    = true; // this is safe, since BSIP38 (hard fork core-834) depends on BSIP31 (hard fork core-343)\n\n          if( usd_to_buy == usd_for_sale )\n             filled_limit = true;\n          else if( filled_limit && maint_time <= HARDFORK_CORE_453_TIME )\n          {\n             //NOTE: Multiple limit match problem (see issue 453, yes this happened)\n             if( before_hardfork_615 )\n                _issue_453_affected_assets.insert( bitasset.asset_id );\n          }\n       }\n       limit_pays = call_receives;\n\n       // BSIP74: Margin call fee\n       FC_ASSERT(call_pays >= limit_receives);\n       const asset margin_call_fee = call_pays - limit_receives;\n\n       if( filled_call && before_core_hardfork_343 )\n          ++call_price_itr;\n\n       // when for_new_limit_order is true, the call order is maker, otherwise the call order is taker\n       fill_call_order( call_order, call_pays, call_receives, match_price, for_new_limit_order, margin_call_fee);\n\n       if( !before_core_hardfork_1270 )\n          call_collateral_itr = call_collateral_index.lower_bound( call_min );\n       else if( !before_core_hardfork_343 )\n          call_price_itr = call_price_index.lower_bound( call_min );\n\n       auto next_limit_itr = std::next( limit_itr );\n       // when for_new_limit_order is true, the limit order is taker, otherwise the limit order is maker\n       bool really_filled = fill_limit_order( limit_order, limit_pays, limit_receives, true,\n                                              match_price, !for_new_limit_order );\n       if( really_filled || ( filled_limit && before_core_hardfork_453 ) )\n          limit_itr = next_limit_itr;\n\n    } // while call_itr != call_end\n\n    return margin_called;\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid database::pay_order( const account_object& receiver, const asset& receives, const asset& pays )\n{\n   const auto& balances = receiver.statistics(*this);\n   modify( balances, [&]( account_statistics_object& b ){\n         if( pays.asset_id == asset_id_type() )\n         {\n            b.total_core_in_orders -= pays.amount;\n         }\n   });\n   adjust_balance(receiver.get_id(), receives);\n}\n\nasset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount,\n                                      const bool& is_maker )const\n{\n   assert( trade_asset.id == trade_amount.asset_id );\n\n   if( !trade_asset.charges_market_fees() )\n      return trade_asset.amount(0);\n   // Optimization: The fee is zero if the order is a maker, and the maker fee percent is 0%\n   if( is_maker && trade_asset.options.market_fee_percent == 0 )\n      return trade_asset.amount(0);\n\n   // Optimization: The fee is zero if the order is a taker, and the taker fee percent is 0%\n   const optional<uint16_t>& taker_fee_percent = trade_asset.options.extensions.value.taker_fee_percent;\n   if(!is_maker && taker_fee_percent.valid() && *taker_fee_percent == 0)\n      return trade_asset.amount(0);\n\n   uint16_t fee_percent;\n   if (is_maker) {\n      // Maker orders are charged the maker fee percent\n      fee_percent = trade_asset.options.market_fee_percent;\n   } else {\n      // Taker orders are charged the taker fee percent if they are valid.  Otherwise, the maker fee percent.\n      fee_percent = taker_fee_percent.valid() ? *taker_fee_percent : trade_asset.options.market_fee_percent;\n   }\n\n   auto value = detail::calculate_percent(trade_amount.amount, fee_percent);\n   asset percent_fee = trade_asset.amount(value);\n\n   if( percent_fee.amount > trade_asset.options.max_market_fee )\n      percent_fee.amount = trade_asset.options.max_market_fee;\n\n   return percent_fee;\n}\n\n\nasset database::pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives,\n                                const bool& is_maker, const optional<asset>& calculated_market_fees )\n{\n   const auto market_fees = ( calculated_market_fees.valid() ? *calculated_market_fees\n                                    : calculate_market_fee( recv_asset, receives, is_maker ) );\n   auto issuer_fees = market_fees;\n   FC_ASSERT( issuer_fees <= receives, \"Market fee shouldn't be greater than receives\");\n   //Don't dirty undo state if not actually collecting any fees\n   if ( issuer_fees.amount > 0 )\n   {\n      // Share market fees to the network\n      const uint16_t network_percent = get_global_properties().parameters.get_market_fee_network_percent();\n      if( network_percent > 0 )\n      {\n         const auto network_fees_amt = detail::calculate_percent( issuer_fees.amount, network_percent );\n         FC_ASSERT( network_fees_amt <= issuer_fees.amount,\n                    \"Fee shared to the network shouldn't be greater than total market fee\" );\n         if( network_fees_amt > 0 )\n         {\n            const asset network_fees = recv_asset.amount( network_fees_amt );\n            deposit_market_fee_vesting_balance( GRAPHENE_COMMITTEE_ACCOUNT, network_fees );\n            issuer_fees -= network_fees;\n         }\n      }\n   }\n\n   // Process the remaining fees\n   if ( issuer_fees.amount > 0 )\n   {\n      // calculate and pay rewards\n      asset reward = recv_asset.amount(0);\n\n      auto is_rewards_allowed = [&recv_asset, seller]() {\n         if (seller == nullptr)\n            return false;\n         const auto &white_list = recv_asset.options.extensions.value.whitelist_market_fee_sharing;\n         return ( !white_list || (*white_list).empty() \n               || ( (*white_list).find(seller->registrar) != (*white_list).end() ) );\n      };\n\n      if ( is_rewards_allowed() )\n      {\n         const auto reward_percent = recv_asset.options.extensions.value.reward_percent;\n         if ( reward_percent && *reward_percent )\n         {\n            const auto reward_value = detail::calculate_percent(issuer_fees.amount, *reward_percent);\n            if ( reward_value > 0 && is_authorized_asset(*this, seller->registrar(*this), recv_asset) )\n            {\n               reward = recv_asset.amount(reward_value);\n               // TODO after hf_1774, remove the `if` check, keep the code in `else`\n               if( head_block_time() < HARDFORK_1774_TIME ){\n                  FC_ASSERT( reward < issuer_fees, \"Market reward should be less than issuer fees\");\n               }\n               else{\n                  FC_ASSERT( reward <= issuer_fees, \"Market reward should not be greater than issuer fees\");\n               }\n               // cut referrer percent from reward\n               auto registrar_reward = reward;\n\n               auto registrar = seller->registrar;\n               auto referrer = seller->referrer;\n\n               // After HF core-1800, for funds going to temp-account, redirect to committee-account\n               if( head_block_time() >= HARDFORK_CORE_1800_TIME )\n               {\n                  if( registrar == GRAPHENE_TEMP_ACCOUNT )\n                     registrar = GRAPHENE_COMMITTEE_ACCOUNT;\n                  if( referrer == GRAPHENE_TEMP_ACCOUNT )\n                     referrer = GRAPHENE_COMMITTEE_ACCOUNT;\n               }\n\n               if( referrer != registrar )\n               {\n                  const auto referrer_rewards_value = detail::calculate_percent( reward.amount,\n                                                                                 seller->referrer_rewards_percentage );\n\n                  if ( referrer_rewards_value > 0 && is_authorized_asset(*this, referrer(*this), recv_asset) )\n                  {\n                     FC_ASSERT ( referrer_rewards_value <= reward.amount.value,\n                                 \"Referrer reward shouldn't be greater than total reward\" );\n                     const asset referrer_reward = recv_asset.amount(referrer_rewards_value);\n                     registrar_reward -= referrer_reward;\n                     deposit_market_fee_vesting_balance(referrer, referrer_reward);\n                  }\n               }\n               if( registrar_reward.amount > 0 )\n                  deposit_market_fee_vesting_balance(registrar, registrar_reward);\n            }\n         }\n      }\n\n      if( issuer_fees.amount > reward.amount )\n      {\n         const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this);\n         modify( recv_dyn_data, [&issuer_fees, &reward]( asset_dynamic_data_object& obj ){\n            obj.accumulated_fees += issuer_fees.amount - reward.amount;\n         });\n      }\n   }\n\n   return market_fees;\n}\n\n/***\n * @brief Calculate force-settlement fee and give it to issuer of the settled asset\n * @param collecting_asset the smart asset object which should receive the fee\n * @param collat_receives the amount of collateral the settler would expect to receive absent this fee\n *     (fee is computed as a percentage of this amount)\n * @return asset denoting the amount of fee collected\n */\nasset database::pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives)\n{\n   FC_ASSERT( collecting_asset.get_id() != collat_receives.asset_id );\n\n   const bitasset_options& collecting_bitasset_opts = collecting_asset.bitasset_data(*this).options;\n\n   if( !collecting_bitasset_opts.extensions.value.force_settle_fee_percent.valid()\n         || *collecting_bitasset_opts.extensions.value.force_settle_fee_percent == 0 )\n      return asset{ 0, collat_receives.asset_id };\n\n   auto value = detail::calculate_percent(collat_receives.amount,\n                                          *collecting_bitasset_opts.extensions.value.force_settle_fee_percent);\n   asset settle_fee = asset{ value, collat_receives.asset_id };\n\n   // Deposit fee in asset's dynamic data object:\n   if( value > 0) {\n      collecting_asset.accumulate_fee(*this, settle_fee);\n   }\n   return settle_fee;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_notify.cpp",
    "content": "#include <fc/container/flat.hpp>\n\n#include <graphene/protocol/authority.hpp>\n#include <graphene/protocol/operations.hpp>\n#include <graphene/protocol/transaction.hpp>\n\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/transaction_history_object.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n#include <graphene/chain/ticket_object.hpp>\n#include <graphene/chain/liquidity_pool_object.hpp>\n#include <graphene/chain/impacted.hpp>\n#include <graphene/chain/hardfork.hpp>\n\nusing namespace fc;\nnamespace graphene { namespace chain { namespace detail {\n\n// TODO:  Review all of these, especially no-ops\nstruct get_impacted_account_visitor\n{\n   flat_set<account_id_type>& _impacted;\n   bool _ignore_custom_op_reqd_auths;\n\n   get_impacted_account_visitor( flat_set<account_id_type>& impact, bool ignore_custom_operation_required_auths )\n      : _impacted( impact ), _ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )\n   {}\n\n   using result_type = void;\n\n   void operator()( const transfer_operation& op )\n   {\n      _impacted.insert( op.to );\n      _impacted.insert( op.fee_payer() ); // from\n   }\n   void operator()( const asset_claim_fees_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const asset_claim_pool_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const limit_order_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // seller\n   }\n   void operator()( const limit_order_cancel_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // fee_paying_account\n   }\n   void operator()( const call_order_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // funding_account\n   }\n   void operator()( const bid_collateral_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // bidder\n   }\n   void operator()( const fill_order_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account_id\n   }\n   void operator()( const execute_bid_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // bidder\n   }\n   void operator()( const account_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // registrar\n      _impacted.insert( op.referrer );\n      add_authority_accounts( _impacted, op.owner );\n      add_authority_accounts( _impacted, op.active );\n   }\n   void operator()( const account_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n      if( op.owner )\n         add_authority_accounts( _impacted, *(op.owner) );\n      if( op.active )\n         add_authority_accounts( _impacted, *(op.active) );\n   }\n   void operator()( const account_whitelist_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // authorizing_account\n      _impacted.insert( op.account_to_list );\n   }\n   void operator()( const account_upgrade_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account_to_upgrade\n   }\n   void operator()( const account_transfer_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account_id\n   }\n   void operator()( const asset_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const asset_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n      if( op.new_issuer )\n         _impacted.insert( *(op.new_issuer) );\n   }\n   void operator()( const asset_update_issuer_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n      _impacted.insert( op.new_issuer );\n   }\n   void operator()( const asset_update_bitasset_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const asset_update_feed_producers_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const asset_issue_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n      _impacted.insert( op.issue_to_account );\n   }\n   void operator()( const asset_reserve_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // payer\n   }\n   void operator()( const asset_fund_fee_pool_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // from_account\n   }\n   void operator()( const asset_settle_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const asset_global_settle_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const asset_publish_feed_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // publisher\n   }\n   void operator()( const witness_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // witness_account\n   }\n   void operator()( const witness_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // witness_account\n   }\n   void operator()( const proposal_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // fee_paying_account\n      vector<authority> other;\n      for( const auto& proposed_op : op.proposed_ops )\n         operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other, _ignore_custom_op_reqd_auths );\n      for( auto& o : other )\n         add_authority_accounts( _impacted, o );\n   }\n   void operator()( const proposal_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // fee_paying_account\n   }\n   void operator()( const proposal_delete_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // fee_paying_account\n   }\n   void operator()( const withdraw_permission_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // withdraw_from_account\n      _impacted.insert( op.authorized_account );\n   }\n   void operator()( const withdraw_permission_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // withdraw_from_account\n      _impacted.insert( op.authorized_account );\n   }\n   void operator()( const withdraw_permission_claim_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // withdraw_to_account\n      _impacted.insert( op.withdraw_from_account );\n   }\n   void operator()( const withdraw_permission_delete_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // withdraw_from_account\n      _impacted.insert( op.authorized_account );\n   }\n   void operator()( const committee_member_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // committee_member_account\n   }\n   void operator()( const committee_member_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // committee_member_account\n   }\n   void operator()( const committee_member_update_global_parameters_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account_id_type()\n   }\n   void operator()( const vesting_balance_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // creator\n      _impacted.insert( op.owner );\n   }\n   void operator()( const vesting_balance_withdraw_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // owner\n   }\n   void operator()( const worker_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // owner\n   }\n   void operator()( const custom_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // payer\n      if( !_ignore_custom_op_reqd_auths )\n         _impacted.insert( op.required_auths.begin(), op.required_auths.end() );\n   }\n   void operator()( const assert_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // fee_paying_account\n   }\n   void operator()( const balance_claim_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // deposit_to_account\n   }\n   void operator()( const override_transfer_operation& op )\n   {\n      _impacted.insert( op.to );\n      _impacted.insert( op.from );\n      _impacted.insert( op.fee_payer() ); // issuer\n   }\n   void operator()( const transfer_to_blind_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // from\n      for( const auto& out : op.outputs )\n         add_authority_accounts( _impacted, out.owner );\n   }\n   void operator()( const blind_transfer_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // GRAPHENE_TEMP_ACCOUNT\n      for( const auto& in : op.inputs )\n         add_authority_accounts( _impacted, in.owner );\n      for( const auto& out : op.outputs )\n         add_authority_accounts( _impacted, out.owner );\n   }\n   void operator()( const transfer_from_blind_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // GRAPHENE_TEMP_ACCOUNT\n      _impacted.insert( op.to );\n      for( const auto& in : op.inputs )\n         add_authority_accounts( _impacted, in.owner );\n   }\n   void operator()( const asset_settle_cancel_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const fba_distribute_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account_id\n   }\n   void operator()( const htlc_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() );\n      _impacted.insert( op.to );\n   }\n   void operator()( const htlc_redeem_operation& op )\n   {\n      _impacted.insert( op.fee_payer() );\n   }\n   void operator()( const htlc_redeemed_operation& op )\n   {\n      _impacted.insert( op.from );\n      if ( op.to != op.redeemer )\n         _impacted.insert( op.to );\n   }\n   void operator()( const htlc_extend_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); \n   }\n   void operator()( const htlc_refund_operation& op ) \n   { \n      _impacted.insert( op.fee_payer() );\n   }\n   void operator()( const custom_authority_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n      add_authority_accounts( _impacted, op.auth );\n   }\n   void operator()( const custom_authority_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n      if ( op.new_auth )\n         add_authority_accounts(_impacted, *op.new_auth);\n   }\n   void operator()( const custom_authority_delete_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const ticket_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const ticket_update_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const liquidity_pool_create_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const liquidity_pool_delete_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const liquidity_pool_deposit_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const liquidity_pool_withdraw_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n   void operator()( const liquidity_pool_exchange_operation& op )\n   {\n      _impacted.insert( op.fee_payer() ); // account\n   }\n};\n\n} // namespace detail\n\nvoid operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, \n      bool ignore_custom_operation_required_auths ) \n{\n  detail::get_impacted_account_visitor vtor = detail::get_impacted_account_visitor( result, \n      ignore_custom_operation_required_auths );\n  op.visit( vtor );\n}\n\nvoid transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result, \n      bool ignore_custom_operation_required_auths ) \n{\n  for( const auto& op : tx.operations )\n    operation_get_impacted_accounts( op, result, ignore_custom_operation_required_auths );\n}\n\nvoid get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts, bool ignore_custom_operation_required_auths ) {\n   if( obj->id.space() == protocol_ids )\n   {\n      switch( (object_type)obj->id.type() )\n      {\n        case null_object_type:\n        case base_object_type:\n           return;\n        case account_object_type:{\n           accounts.insert( obj->id );\n           break;\n        } case asset_object_type:{\n           const auto& aobj = dynamic_cast<const asset_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->issuer );\n           break;\n        } case force_settlement_object_type:{\n           const auto& aobj = dynamic_cast<const force_settlement_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->owner );\n           break;\n        } case committee_member_object_type:{\n           const auto& aobj = dynamic_cast<const committee_member_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->committee_member_account );\n           break;\n        } case witness_object_type:{\n           const auto& aobj = dynamic_cast<const witness_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->witness_account );\n           break;\n        } case limit_order_object_type:{\n           const auto& aobj = dynamic_cast<const limit_order_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->seller );\n           break;\n        } case call_order_object_type:{\n           const auto& aobj = dynamic_cast<const call_order_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->borrower );\n           break;\n        } case custom_object_type:{\n          break;\n        } case proposal_object_type:{\n           const auto& aobj = dynamic_cast<const proposal_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           transaction_get_impacted_accounts( aobj->proposed_transaction, accounts,\n                                              ignore_custom_operation_required_auths );\n           break;\n        } case operation_history_object_type:{\n           const auto& aobj = dynamic_cast<const operation_history_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           operation_get_impacted_accounts( aobj->op, accounts,\n                                            ignore_custom_operation_required_auths );\n           break;\n        } case withdraw_permission_object_type:{\n           const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->withdraw_from_account );\n           accounts.insert( aobj->authorized_account );\n           break;\n        } case vesting_balance_object_type:{\n           const auto& aobj = dynamic_cast<const vesting_balance_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->owner );\n           break;\n        } case worker_object_type:{\n           const auto& aobj = dynamic_cast<const worker_object*>(obj);\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->worker_account );\n           break;\n        } case balance_object_type:{\n           /** these are free from any accounts */\n           break;\n        } case htlc_object_type:{\n              const auto& htlc_obj = dynamic_cast<const htlc_object*>(obj);\n              FC_ASSERT( htlc_obj != nullptr );\n              accounts.insert( htlc_obj->transfer.from );\n              accounts.insert( htlc_obj->transfer.to );\n              break;\n        } case custom_authority_object_type:{\n           const auto* cust_auth_obj = dynamic_cast<const custom_authority_object*>( obj );\n           FC_ASSERT( cust_auth_obj != nullptr );\n           accounts.insert( cust_auth_obj->account );\n           add_authority_accounts( accounts, cust_auth_obj->auth );\n           break;\n        } case ticket_object_type:{\n           const auto* aobj = dynamic_cast<const ticket_object*>( obj );\n           FC_ASSERT( aobj != nullptr );\n           accounts.insert( aobj->account );\n           break;\n        } case liquidity_pool_object_type:{\n           // no account info in the object although it does have an owner\n           break;\n        }\n      }\n   }\n   else if( obj->id.space() == implementation_ids )\n   {\n      switch( (impl_object_type)obj->id.type() )\n      {\n             case impl_global_property_object_type:\n              break;\n             case impl_dynamic_global_property_object_type:\n              break;\n             case impl_reserved0_object_type:\n              break;\n             case impl_asset_dynamic_data_object_type:\n              break;\n             case impl_asset_bitasset_data_object_type:\n              break;\n             case impl_account_balance_object_type:{\n              const auto& aobj = dynamic_cast<const account_balance_object*>(obj);\n              FC_ASSERT( aobj != nullptr );\n              accounts.insert( aobj->owner );\n              break;\n           } case impl_account_statistics_object_type:{\n              const auto& aobj = dynamic_cast<const account_statistics_object*>(obj);\n              FC_ASSERT( aobj != nullptr );\n              accounts.insert( aobj->owner );\n              break;\n           } case impl_transaction_history_object_type:{\n              const auto& aobj = dynamic_cast<const transaction_history_object*>(obj);\n              FC_ASSERT( aobj != nullptr );\n              transaction_get_impacted_accounts( aobj->trx, accounts,\n                                                 ignore_custom_operation_required_auths );\n              break;\n           } case impl_blinded_balance_object_type:{\n              const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);\n              FC_ASSERT( aobj != nullptr );\n              for( const auto& a : aobj->owner.account_auths )\n                accounts.insert( a.first );\n              break;\n           } case impl_block_summary_object_type:\n              break;\n             case impl_account_transaction_history_object_type: {\n              const auto& aobj = dynamic_cast<const account_transaction_history_object*>(obj);\n              FC_ASSERT( aobj != nullptr );\n              accounts.insert( aobj->account );\n              break;\n           } case impl_chain_property_object_type:\n              break;\n             case impl_witness_schedule_object_type:\n              break;\n             case impl_budget_record_object_type:\n              break;\n             case impl_special_authority_object_type:\n              break;\n             case impl_buyback_object_type:\n              break;\n             case impl_fba_accumulator_object_type:\n              break;\n             case impl_collateral_bid_object_type:{\n              const auto& aobj = dynamic_cast<const collateral_bid_object*>(obj);\n              FC_ASSERT( aobj != nullptr );\n              accounts.insert( aobj->bidder );\n              break;\n           }\n      }\n   }\n} // end get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )\n\nvoid database::notify_applied_block( const signed_block& block )\n{\n   GRAPHENE_TRY_NOTIFY( applied_block, block )\n}\n\nvoid database::notify_on_pending_transaction( const signed_transaction& tx )\n{\n   GRAPHENE_TRY_NOTIFY( on_pending_transaction, tx )\n}\n\nvoid database::notify_changed_objects()\n{ try {\n   if( _undo_db.enabled() ) \n   {\n      const auto& head_undo = _undo_db.head();\n      auto chain_time = head_block_time();\n\n      // New\n      if( !new_objects.empty() )\n      {\n        vector<object_id_type> new_ids;  new_ids.reserve(head_undo.new_ids.size());\n        flat_set<account_id_type> new_accounts_impacted;\n        for( const auto& item : head_undo.new_ids )\n        {\n          new_ids.push_back(item);\n          auto obj = find_object(item);\n          if(obj != nullptr)\n            get_relevant_accounts(obj, new_accounts_impacted,\n                                  MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));\n        }\n\n        if( new_ids.size() )\n           GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted)\n      }\n\n      // Changed\n      if( !changed_objects.empty() )\n      {\n        vector<object_id_type> changed_ids;  changed_ids.reserve(head_undo.old_values.size());\n        flat_set<account_id_type> changed_accounts_impacted;\n        for( const auto& item : head_undo.old_values )\n        {\n          changed_ids.push_back(item.first);\n          get_relevant_accounts(item.second.get(), changed_accounts_impacted,\n                                MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));\n        }\n\n        if( changed_ids.size() )\n           GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted)\n      }\n\n      // Removed\n      if( !removed_objects.empty() )\n      {\n        vector<object_id_type> removed_ids; removed_ids.reserve( head_undo.removed.size() );\n        vector<const object*> removed; removed.reserve( head_undo.removed.size() );\n        flat_set<account_id_type> removed_accounts_impacted;\n        for( const auto& item : head_undo.removed )\n        {\n          removed_ids.emplace_back( item.first );\n          auto obj = item.second.get();\n          removed.emplace_back( obj );\n          get_relevant_accounts(obj, removed_accounts_impacted,\n                                MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));\n        }\n\n        if( removed_ids.size() )\n           GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted )\n      }\n   }\n} FC_CAPTURE_AND_LOG( (0) ) }\n\n} } // namespace graphene::chain\n"
  },
  {
    "path": "libraries/chain/db_update.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/db_with.hpp>\n\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/ticket_object.hpp>\n#include <graphene/chain/transaction_history_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <graphene/protocol/fee_schedule.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks )\n{\n   const dynamic_global_property_object& _dgp = get_dynamic_global_properties();\n\n   // dynamic global properties updating\n   modify( _dgp, [&b,this,missed_blocks]( dynamic_global_property_object& dgp ){\n      const uint32_t block_num = b.block_num();\n      if( BOOST_UNLIKELY( block_num == 1 ) )\n         dgp.recently_missed_count = 0;\n      else if( _checkpoints.size() && _checkpoints.rbegin()->first >= block_num )\n         dgp.recently_missed_count = 0;\n      else if( missed_blocks )\n         dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;\n      else if( dgp.recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )\n         dgp.recently_missed_count -= GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT;\n      else if( dgp.recently_missed_count > 0 )\n         dgp.recently_missed_count--;\n\n      dgp.head_block_number = block_num;\n      dgp.head_block_id = b.id();\n      dgp.time = b.timestamp;\n      dgp.current_witness = b.witness;\n      dgp.recent_slots_filled = (\n           (dgp.recent_slots_filled << 1)\n           + 1) << missed_blocks;\n      dgp.current_aslot += missed_blocks+1;\n   });\n\n   if( !(get_node_properties().skip_flags & skip_undo_history_check) )\n   {\n      GRAPHENE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num  < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,\n                 \"The database does not have enough undo history to support a blockchain with so many missed blocks. \"\n                 \"Please add a checkpoint if you would like to continue applying blocks beyond this point.\",\n                 (\"last_irreversible_block_num\",_dgp.last_irreversible_block_num)(\"head\", _dgp.head_block_number)\n                 (\"recently_missed\",_dgp.recently_missed_count)(\"max_undo\",GRAPHENE_MAX_UNDO_HISTORY) );\n   }\n\n   _undo_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );\n   _fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );\n}\n\nvoid database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block)\n{\n   const global_property_object& gpo = get_global_properties();\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n   uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time( new_block.timestamp );\n\n   share_type witness_pay = std::min( gpo.parameters.witness_pay_per_block, dpo.witness_budget );\n\n   modify( dpo, [&]( dynamic_global_property_object& _dpo )\n   {\n      _dpo.witness_budget -= witness_pay;\n   } );\n\n   deposit_witness_pay( signing_witness, witness_pay );\n\n   modify( signing_witness, [&]( witness_object& _wit )\n   {\n      _wit.last_aslot = new_block_aslot;\n      _wit.last_confirmed_block_num = new_block.block_num();\n   } );\n}\n\nvoid database::update_last_irreversible_block()\n{\n   const global_property_object& gpo = get_global_properties();\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n\n   // TODO for better performance, move this to db_maint, because only need to do it once per maintenance interval\n   vector< const witness_object* > wit_objs;\n   wit_objs.reserve( gpo.active_witnesses.size() );\n   for( const witness_id_type& wid : gpo.active_witnesses )\n      wit_objs.push_back( &(wid(*this)) );\n\n   static_assert( GRAPHENE_IRREVERSIBLE_THRESHOLD > 0, \"irreversible threshold must be nonzero\" );\n\n   // 1 1 1 2 2 2 2 2 2 2 -> 2     .3*10 = 3\n   // 1 1 1 1 1 1 1 2 2 2 -> 1\n   // 3 3 3 3 3 3 3 3 3 3 -> 3\n   // 3 3 3 4 4 4 4 4 4 4 -> 4\n\n   size_t offset = ((GRAPHENE_100_PERCENT - GRAPHENE_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / GRAPHENE_100_PERCENT);\n\n   std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(),\n      []( const witness_object* a, const witness_object* b )\n      {\n         return a->last_confirmed_block_num < b->last_confirmed_block_num;\n      } );\n\n   uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num;\n\n   if( new_last_irreversible_block_num > dpo.last_irreversible_block_num )\n   {\n      modify( dpo, [&]( dynamic_global_property_object& _dpo )\n      {\n         _dpo.last_irreversible_block_num = new_last_irreversible_block_num;\n      } );\n   }\n}\n\nvoid database::clear_expired_transactions()\n{ try {\n   //Look for expired transactions in the deduplication list, and remove them.\n   //Transactions must have expired by at least two forking windows in order to be removed.\n   auto& transaction_idx = static_cast<transaction_index&>(get_mutable_index(implementation_ids,\n                                                                             impl_transaction_history_object_type));\n   const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();\n   while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.begin()->trx.expiration) )\n      transaction_idx.remove(*dedupe_index.begin());\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid database::clear_expired_proposals()\n{\n   const auto& proposal_expiration_index = get_index_type<proposal_index>().indices().get<by_expiration>();\n   while( !proposal_expiration_index.empty() && proposal_expiration_index.begin()->expiration_time <= head_block_time() )\n   {\n      const proposal_object& proposal = *proposal_expiration_index.begin();\n      processed_transaction result;\n      try {\n         if( proposal.is_authorized_to_execute(*this) )\n         {\n            result = push_proposal(proposal);\n            //TODO: Do something with result so plugins can process it.\n            continue;\n         }\n      } catch( const fc::exception& e ) {\n         elog(\"Failed to apply proposed transaction on its expiration. Deleting it.\\n${proposal}\\n${error}\",\n              (\"proposal\", proposal)(\"error\", e.to_detail_string()));\n      }\n      remove(proposal);\n   }\n}\n\n/**\n *  let HB = the highest bid for the collateral  (aka who will pay the most DEBT for the least collateral)\n *  let SP = current median feed's Settlement Price \n *  let LC = the least collateralized call order's swan price (debt/collateral)\n *\n *  If there is no valid price feed or no bids then there is no black swan.\n *\n *  A black swan occurs if MAX(HB,SP) <= LC\n */\nbool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan,\n                                    const asset_bitasset_data_object* bitasset_ptr )\n{\n    if( !mia.is_market_issued() ) return false;\n\n    const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) );\n    if( bitasset.has_settlement() ) return true; // already force settled\n    auto settle_price = bitasset.current_feed.settlement_price;\n    if( settle_price.is_null() ) return false; // no feed\n\n    const call_order_object* call_ptr = nullptr; // place holder for the call order with least collateral ratio\n\n    asset_id_type debt_asset_id = mia.id;\n    auto call_min = price::min( bitasset.options.short_backing_asset, debt_asset_id );\n\n    auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n    bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue\n\n    if( before_core_hardfork_1270 ) // before core-1270 hard fork, check with call_price\n    {\n       const auto& call_price_index = get_index_type<call_order_index>().indices().get<by_price>();\n       auto call_itr = call_price_index.lower_bound( call_min );\n       if( call_itr == call_price_index.end() ) // no call order\n          return false;\n       call_ptr = &(*call_itr);\n    }\n    else // after core-1270 hard fork, check with collateralization\n    {\n       const auto& call_collateral_index = get_index_type<call_order_index>().indices().get<by_collateral>();\n       auto call_itr = call_collateral_index.lower_bound( call_min );\n       if( call_itr == call_collateral_index.end() ) // no call order\n          return false;\n       call_ptr = &(*call_itr);\n    }\n    if( call_ptr->debt_type() != debt_asset_id ) // no call order\n       return false;\n\n    price highest = settle_price;\n    if( maint_time > HARDFORK_CORE_1270_TIME )\n       // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here\n       highest = bitasset.current_feed.max_short_squeeze_price();\n    else if( maint_time > HARDFORK_CORE_338_TIME )\n       // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here\n       highest = bitasset.current_feed.max_short_squeeze_price_before_hf_1270();\n\n    const limit_order_index& limit_index = get_index_type<limit_order_index>();\n    const auto& limit_price_index = limit_index.indices().get<by_price>();\n\n    // looking for limit orders selling the most USD for the least CORE\n    auto highest_possible_bid = price::max( mia.id, bitasset.options.short_backing_asset );\n    // stop when limit orders are selling too little USD for too much CORE\n    auto lowest_possible_bid  = price::min( mia.id, bitasset.options.short_backing_asset );\n\n    FC_ASSERT( highest_possible_bid.base.asset_id == lowest_possible_bid.base.asset_id );\n    // NOTE limit_price_index is sorted from greatest to least\n    auto limit_itr = limit_price_index.lower_bound( highest_possible_bid );\n    auto limit_end = limit_price_index.upper_bound( lowest_possible_bid );\n\n    if( limit_itr != limit_end ) {\n       FC_ASSERT( highest.base.asset_id == limit_itr->sell_price.base.asset_id );\n       highest = std::max( limit_itr->sell_price, highest );\n    }\n\n    auto least_collateral = call_ptr->collateralization();\n    if( ~least_collateral >= highest  ) \n    {\n       wdump( (*call_ptr) );\n       elog( \"Black Swan detected on asset ${symbol} (${id}) at block ${b}: \\n\"\n             \"   Least collateralized call: ${lc}  ${~lc}\\n\"\n           //  \"   Highest Bid:               ${hb}  ${~hb}\\n\"\n             \"   Settle Price:              ${~sp}  ${sp}\\n\"\n             \"   Max:                       ${~h}  ${h}\\n\",\n            (\"id\",mia.id)(\"symbol\",mia.symbol)(\"b\",head_block_num())\n            (\"lc\",least_collateral.to_real())(\"~lc\",(~least_collateral).to_real())\n          //  (\"hb\",limit_itr->sell_price.to_real())(\"~hb\",(~limit_itr->sell_price).to_real())\n            (\"sp\",settle_price.to_real())(\"~sp\",(~settle_price).to_real())\n            (\"h\",highest.to_real())(\"~h\",(~highest).to_real()) );\n       edump((enable_black_swan));\n       FC_ASSERT( enable_black_swan, \"Black swan was detected during a margin update which is not allowed to trigger a blackswan\" );\n       if( maint_time > HARDFORK_CORE_338_TIME && ~least_collateral <= settle_price )\n          // global settle at feed price if possible\n          globally_settle_asset(mia, settle_price );\n       else\n          globally_settle_asset(mia, ~least_collateral );\n       return true;\n    } \n    return false;\n}\n\nvoid database::clear_expired_orders()\n{ try {\n         //Cancel expired limit orders\n         auto head_time = head_block_time();\n         auto maint_time = get_dynamic_global_properties().next_maintenance_time;\n\n         bool before_core_hardfork_184 = ( maint_time <= HARDFORK_CORE_184_TIME ); // something-for-nothing\n         bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding\n         bool before_core_hardfork_606 = ( maint_time <= HARDFORK_CORE_606_TIME ); // feed always trigger call\n\n         auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>();\n         while( !limit_index.empty() && limit_index.begin()->expiration <= head_time )\n         {\n            const limit_order_object& order = *limit_index.begin();\n            auto base_asset = order.sell_price.base.asset_id;\n            auto quote_asset = order.sell_price.quote.asset_id;\n            cancel_limit_order( order );\n            if( before_core_hardfork_606 )\n            {\n               // check call orders\n               // Comments below are copied from limit_order_cancel_evaluator::do_apply(...)\n               // Possible optimization: order can be called by cancelling a limit order\n               //   if the canceled order was at the top of the book.\n               // Do I need to check calls in both assets?\n               check_call_orders( base_asset( *this ) );\n               check_call_orders( quote_asset( *this ) );\n            }\n         }\n\n   //Process expired force settlement orders\n   auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>();\n   if( !settlement_index.empty() )\n   {\n      asset_id_type current_asset = settlement_index.begin()->settlement_asset_id();\n      asset max_settlement_volume;\n      price settlement_fill_price;\n      price settlement_price;\n      bool current_asset_finished = false;\n      bool extra_dump = false;\n\n      auto next_asset = [&current_asset, &current_asset_finished, &settlement_index, &extra_dump] {\n         auto bound = settlement_index.upper_bound(current_asset);\n         if( bound == settlement_index.end() )\n         {\n            if( extra_dump )\n            {\n               ilog( \"next_asset() returning false\" );\n            }\n            return false;\n         }\n         if( extra_dump )\n         {\n            ilog( \"next_asset returning true, bound is ${b}\", (\"b\", *bound) );\n         }\n         current_asset = bound->settlement_asset_id();\n         current_asset_finished = false;\n         return true;\n      };\n\n      uint32_t count = 0;\n\n      // At each iteration, we either consume the current order and remove it, or we move to the next asset\n      for( auto itr = settlement_index.lower_bound(current_asset);\n           itr != settlement_index.end();\n           itr = settlement_index.lower_bound(current_asset) )\n      {\n         ++count;\n         const force_settlement_object& order = *itr;\n         auto order_id = order.id;\n         current_asset = order.settlement_asset_id();\n         const asset_object& mia_object = get(current_asset);\n         const asset_bitasset_data_object& mia = mia_object.bitasset_data(*this);\n\n         extra_dump = ((count >= 1000) && (count <= 1020));\n\n         if( extra_dump )\n         {\n            wlog( \"clear_expired_orders() dumping extra data for iteration ${c}\", (\"c\", count) );\n            ilog( \"head_block_num is ${hb} current_asset is ${a}\", (\"hb\", head_block_num())(\"a\", current_asset) );\n         }\n\n         if( mia.has_settlement() )\n         {\n            ilog( \"Canceling a force settlement because of black swan\" );\n            cancel_settle_order( order );\n            continue;\n         }\n\n         // Has this order not reached its settlement date?\n         if( order.settlement_date > head_time )\n         {\n            if( next_asset() )\n            {\n               if( extra_dump )\n               {\n                  ilog( \"next_asset() returned true when order.settlement_date > head_block_time()\" );\n               }\n               continue;\n            }\n            break;\n         }\n         // Can we still settle in this asset?\n         if( mia.current_feed.settlement_price.is_null() )\n         {\n            ilog(\"Canceling a force settlement in ${asset} because settlement price is null\",\n                 (\"asset\", mia_object.symbol));\n            cancel_settle_order(order);\n            continue;\n         }\n         if( GRAPHENE_100_PERCENT == mia.options.force_settlement_offset_percent ) // settle something for nothing\n         {\n            ilog( \"Canceling a force settlement in ${asset} because settlement offset is 100%\",\n                  (\"asset\", mia_object.symbol));\n            cancel_settle_order(order);\n            continue;\n         }\n         if( max_settlement_volume.asset_id != current_asset )\n            max_settlement_volume = mia_object.amount(mia.max_force_settlement_volume(mia_object.dynamic_data(*this).current_supply));\n         // When current_asset_finished is true, this would be the 2nd time processing the same order.\n         // In this case, we move to the next asset.\n         if( mia.force_settled_volume >= max_settlement_volume.amount || current_asset_finished )\n         {\n            /*\n            ilog(\"Skipping force settlement in ${asset}; settled ${settled_volume} / ${max_volume}\",\n                 (\"asset\", mia_object.symbol)(\"settlement_price_null\",mia.current_feed.settlement_price.is_null())\n                 (\"settled_volume\", mia.force_settled_volume)(\"max_volume\", max_settlement_volume));\n                 */\n            if( next_asset() )\n            {\n               if( extra_dump )\n               {\n                  ilog( \"next_asset() returned true when mia.force_settled_volume >= max_settlement_volume.amount\" );\n               }\n               continue;\n            }\n            break;\n         }\n\n         if( settlement_fill_price.base.asset_id != current_asset ) // only calculate once per asset\n            settlement_fill_price = mia.current_feed.settlement_price\n                                    / ratio_type( GRAPHENE_100_PERCENT - mia.options.force_settlement_offset_percent,\n                                                  GRAPHENE_100_PERCENT );\n\n         if( before_core_hardfork_342 )\n         {\n            auto& pays = order.balance;\n            auto receives = (order.balance * mia.current_feed.settlement_price);\n            receives.amount = static_cast<uint64_t>( fc::uint128_t(receives.amount.value) *\n                                (GRAPHENE_100_PERCENT - mia.options.force_settlement_offset_percent) /\n                                GRAPHENE_100_PERCENT );\n            assert(receives <= order.balance * mia.current_feed.settlement_price);\n            settlement_price = pays / receives;\n         }\n         else if( settlement_price.base.asset_id != current_asset ) // only calculate once per asset\n            settlement_price = settlement_fill_price;\n\n         auto& call_index = get_index_type<call_order_index>().indices().get<by_collateral>();\n         asset settled = mia_object.amount(mia.force_settled_volume);\n         // Match against the least collateralized short until the settlement is finished or we reach max settlements\n         while( settled < max_settlement_volume && find_object(order_id) )\n         {\n            auto itr = call_index.lower_bound(boost::make_tuple(price::min(mia_object.bitasset_data(*this).options.short_backing_asset,\n                                                                           mia_object.get_id())));\n            // There should always be a call order, since asset exists!\n            assert(itr != call_index.end() && itr->debt_type() == mia_object.get_id());\n\n            if (itr == call_index.end() || itr->debt_type() != mia_object.get_id())\n            {\n               wlog( \"No debt position found when processing force settlement ${o}\",(\"o\",order) );\n               cancel_settle_order( order );\n               break;\n            }\n            \n            asset max_settlement = max_settlement_volume - settled;\n\n            if( order.balance.amount == 0 )\n            {\n               wlog( \"0 settlement detected\" );\n               cancel_settle_order( order );\n               break;\n            }\n            try {\n               asset new_settled = match(*itr, order, settlement_price, max_settlement, settlement_fill_price);\n               if( !before_core_hardfork_184 && new_settled.amount == 0 ) // unable to fill this settle order\n               {\n                  if( find_object( order_id ) ) // the settle order hasn't been cancelled\n                     current_asset_finished = true;\n                  break;\n               }\n               settled += new_settled;\n               // before hard fork core-342, `new_settled > 0` is always true, we'll have:\n               // * call order is completely filled (thus itr will change in next loop), or\n               // * settle order is completely filled (thus find_object(order_id) will be false so will break out), or\n               // * reached max_settlement_volume limit (thus new_settled == max_settlement so will break out).\n               //\n               // after hard fork core-342, if new_settled > 0, we'll have:\n               // * call order is completely filled (thus itr will change in next loop), or\n               // * settle order is completely filled (thus find_object(order_id) will be false so will break out), or\n               // * reached max_settlement_volume limit, but it's possible that new_settled < max_settlement,\n               //   in this case, new_settled will be zero in next iteration of the loop, so no need to check here.\n            } \n            catch ( const black_swan_exception& e ) { \n               wlog( \"Cancelling a settle_order since it may trigger a black swan: ${o}, ${e}\",\n                     (\"o\", order)(\"e\", e.to_detail_string()) );\n               cancel_settle_order( order );\n               break;\n            }\n         }\n         if( mia.force_settled_volume != settled.amount )\n         {\n            modify(mia, [settled](asset_bitasset_data_object& b) {\n               b.force_settled_volume = settled.amount;\n            });\n         }\n      }\n   }\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid database::update_expired_feeds()\n{\n   const auto head_time = head_block_time();\n   const auto next_maint_time = get_dynamic_global_properties().next_maintenance_time;\n   bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME );\n\n   const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_feed_expiration>();\n   auto itr = idx.begin();\n   while( itr != idx.end() && itr->feed_is_expired( head_time ) )\n   {\n      const asset_bitasset_data_object& b = *itr;\n      ++itr; // not always process begin() because old code skipped updating some assets before hf 615\n      bool update_cer = false; // for better performance, to only update bitasset once, also check CER in this function\n      const asset_object* asset_ptr = nullptr;\n      // update feeds, check margin calls\n      if( after_hardfork_615 || b.feed_is_expired_before_hardfork_615( head_time ) )\n      {\n         auto old_median_feed = b.current_feed;\n         modify( b, [head_time,next_maint_time,&update_cer]( asset_bitasset_data_object& abdo )\n         {\n            abdo.update_median_feeds( head_time, next_maint_time );\n            if( abdo.need_to_update_cer() )\n            {\n               update_cer = true;\n               abdo.asset_cer_updated = false;\n               abdo.feed_cer_updated = false;\n            }\n         });\n         if( !b.current_feed.settlement_price.is_null()\n               && !b.current_feed.margin_call_params_equal( old_median_feed ) )\n         {\n            asset_ptr = &b.asset_id( *this );\n            check_call_orders( *asset_ptr, true, false, &b );\n         }\n      }\n      // update CER\n      if( update_cer )\n      {\n         if( !asset_ptr )\n            asset_ptr = &b.asset_id( *this );\n         if( asset_ptr->options.core_exchange_rate != b.current_feed.core_exchange_rate )\n         {\n            modify( *asset_ptr, [&b]( asset_object& ao )\n            {\n               ao.options.core_exchange_rate = b.current_feed.core_exchange_rate;\n            });\n         }\n      }\n   } // for each asset whose feed is expired\n\n   // process assets affected by bitshares-core issue 453 before hard fork 615\n   if( !after_hardfork_615 )\n   {\n      for( asset_id_type a : _issue_453_affected_assets )\n      {\n         check_call_orders( a(*this) );\n      }\n   }\n}\n\nvoid database::update_core_exchange_rates()\n{\n   const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_cer_update>();\n   if( idx.begin() != idx.end() )\n   {\n      for( auto itr = idx.rbegin(); itr->need_to_update_cer(); itr = idx.rbegin() )\n      {\n         const asset_bitasset_data_object& b = *itr;\n         const asset_object& a = b.asset_id( *this );\n         if( a.options.core_exchange_rate != b.current_feed.core_exchange_rate )\n         {\n            modify( a, [&b]( asset_object& ao )\n            {\n               ao.options.core_exchange_rate = b.current_feed.core_exchange_rate;\n            });\n         }\n         modify( b, []( asset_bitasset_data_object& abdo )\n         {\n            abdo.asset_cer_updated = false;\n            abdo.feed_cer_updated = false;\n         });\n      }\n   }\n}\n\nvoid database::update_maintenance_flag( bool new_maintenance_flag )\n{\n   modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& dpo )\n   {\n      auto maintenance_flag = dynamic_global_property_object::maintenance_flag;\n      dpo.dynamic_flags =\n           (dpo.dynamic_flags & ~maintenance_flag)\n         | (new_maintenance_flag ? maintenance_flag : 0);\n   } );\n   return;\n}\n\nvoid database::update_withdraw_permissions()\n{\n   auto& permit_index = get_index_type<withdraw_permission_index>().indices().get<by_expiration>();\n   while( !permit_index.empty() && permit_index.begin()->expiration <= head_block_time() )\n      remove(*permit_index.begin());\n}\n\nvoid database::clear_expired_htlcs()\n{\n   const auto& htlc_idx = get_index_type<htlc_index>().indices().get<by_expiration>();\n   while ( htlc_idx.begin() != htlc_idx.end()\n         && htlc_idx.begin()->conditions.time_lock.expiration <= head_block_time() )\n   {\n      const htlc_object& obj = *htlc_idx.begin();\n      const auto amount = asset(obj.transfer.amount, obj.transfer.asset_id);\n      adjust_balance( obj.transfer.from, amount );\n      // notify related parties\n      htlc_refund_operation vop( obj.id, obj.transfer.from, obj.transfer.to, amount,\n         obj.conditions.hash_lock.preimage_hash, obj.conditions.hash_lock.preimage_size );\n      push_applied_operation( vop );\n      remove( obj );\n   }\n}\n\ngeneric_operation_result database::process_tickets()\n{\n   generic_operation_result result;\n   share_type total_delta_pob;\n   share_type total_delta_inactive;\n   auto& idx = get_index_type<ticket_index>().indices().get<by_next_update>();\n   while( !idx.empty() && idx.begin()->next_auto_update_time <= head_block_time() )\n   {\n      const ticket_object& ticket = *idx.begin();\n      const auto& stat = get_account_stats_by_owner( ticket.account );\n      if( ticket.status == withdrawing && ticket.current_type == liquid )\n      {\n         adjust_balance( ticket.account, ticket.amount );\n         // Note: amount.asset_id is checked when creating the ticket, so no check here\n         modify( stat, [&ticket](account_statistics_object& aso) {\n            aso.total_core_pol -= ticket.amount.amount;\n            aso.total_pol_value -= ticket.value;\n         });\n         result.removed_objects.insert( ticket.id );\n         remove( ticket );\n      }\n      else\n      {\n         ticket_type old_type = ticket.current_type;\n         share_type old_value = ticket.value;\n         modify( ticket, []( ticket_object& o ) {\n            o.auto_update();\n         });\n         result.updated_objects.insert( ticket.id );\n\n         share_type delta_inactive_amount;\n         share_type delta_forever_amount;\n         share_type delta_forever_value;\n         share_type delta_other_amount;\n         share_type delta_other_value;\n\n         if( old_type == lock_forever ) // It implies that the new type is lock_forever too\n         {\n            if( ticket.value == 0 )\n            {\n               total_delta_pob -= ticket.amount.amount;\n               total_delta_inactive += ticket.amount.amount;\n               delta_inactive_amount = ticket.amount.amount;\n               delta_forever_amount = -ticket.amount.amount;\n            }\n            delta_forever_value = ticket.value - old_value;\n         }\n         else // old_type != lock_forever\n         {\n            if( ticket.current_type == lock_forever )\n            {\n               total_delta_pob += ticket.amount.amount;\n               delta_forever_amount = ticket.amount.amount;\n               delta_forever_value = ticket.value;\n               delta_other_amount = -ticket.amount.amount;\n               delta_other_value = -old_value;\n            }\n            else // ticket.current_type != lock_forever\n            {\n               delta_other_value = ticket.value - old_value;\n            }\n         }\n\n         // Note: amount.asset_id is checked when creating the ticket, so no check here\n         modify( stat, [delta_inactive_amount,delta_forever_amount,delta_forever_value,\n                        delta_other_amount,delta_other_value](account_statistics_object& aso) {\n            aso.total_core_inactive += delta_inactive_amount;\n            aso.total_core_pob += delta_forever_amount;\n            aso.total_core_pol += delta_other_amount;\n            aso.total_pob_value += delta_forever_value;\n            aso.total_pol_value += delta_other_value;\n         });\n\n      }\n      // TODO if a lock_forever ticket lost all the value, remove it\n   }\n\n   // TODO merge stable tickets with the same account and the same type\n\n   // Update global data\n   if( total_delta_pob != 0 || total_delta_inactive != 0 )\n   {\n      modify( get_dynamic_global_properties(),\n              [total_delta_pob,total_delta_inactive]( dynamic_global_property_object& dgp ) {\n         dgp.total_pob += total_delta_pob;\n         dgp.total_inactive += total_delta_inactive;\n      });\n   }\n\n   return result;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/db_witness_schedule.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/witness_schedule_object.hpp>\n\n#include <fc/popcount.hpp>\n\nnamespace graphene { namespace chain {\n\nusing boost::container::flat_set;\n\nwitness_id_type database::get_scheduled_witness( uint32_t slot_num )const\n{\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n   const witness_schedule_object& wso = get_witness_schedule_object();\n   uint64_t current_aslot = dpo.current_aslot + slot_num;\n   return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ];\n}\n\nfc::time_point_sec database::get_slot_time(uint32_t slot_num)const\n{\n   if( slot_num == 0 )\n      return fc::time_point_sec();\n\n   auto interval = block_interval();\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n\n   if( head_block_num() == 0 )\n   {\n      // n.b. first block is at genesis_time plus one block interval\n      fc::time_point_sec genesis_time = dpo.time;\n      return genesis_time + slot_num * interval;\n   }\n\n   int64_t head_block_abs_slot = head_block_time().sec_since_epoch() / interval;\n   fc::time_point_sec head_slot_time(head_block_abs_slot * interval);\n\n   const global_property_object& gpo = get_global_properties();\n\n   if( dpo.dynamic_flags & dynamic_global_property_object::maintenance_flag )\n      slot_num += gpo.parameters.maintenance_skip_slots;\n\n   // \"slot 0\" is head_slot_time\n   // \"slot 1\" is head_slot_time,\n   //   plus maint interval if head block is a maint block\n   //   plus block interval if head block is not a maint block\n   return head_slot_time + (slot_num * interval);\n}\n\nuint32_t database::get_slot_at_time(fc::time_point_sec when)const\n{\n   fc::time_point_sec first_slot_time = get_slot_time( 1 );\n   if( when < first_slot_time )\n      return 0;\n   return (when - first_slot_time).to_seconds() / block_interval() + 1;\n}\n\nuint32_t database::update_witness_missed_blocks( const signed_block& b )\n{\n   uint32_t missed_blocks = get_slot_at_time( b.timestamp );\n   FC_ASSERT( missed_blocks != 0, \"Trying to push double-produced block onto current block?!\" );\n   missed_blocks--;\n   const auto& witnesses = witness_schedule_id_type()(*this).current_shuffled_witnesses;\n   if( missed_blocks < witnesses.size() )\n      for( uint32_t i = 0; i < missed_blocks; ++i ) {\n         const auto& witness_missed = get_scheduled_witness( i+1 )(*this);\n         modify( witness_missed, []( witness_object& w ) {\n            w.total_missed++;\n         });\n      }\n   return missed_blocks;\n}\n\nuint32_t database::witness_participation_rate()const\n{\n   const dynamic_global_property_object& dpo = get_dynamic_global_properties();\n   return uint64_t(GRAPHENE_100_PERCENT) * fc::popcount(dpo.recent_slots_filled) / 128;\n}\n\nvoid database::update_witness_schedule()\n{\n   const witness_schedule_object& wso = get_witness_schedule_object();\n   const global_property_object& gpo = get_global_properties();\n\n   if( head_block_num() % gpo.active_witnesses.size() == 0 )\n   {\n      modify( wso, [&]( witness_schedule_object& _wso )\n      {\n         _wso.current_shuffled_witnesses.clear();\n         _wso.current_shuffled_witnesses.reserve( gpo.active_witnesses.size() );\n\n         for( const witness_id_type& w : gpo.active_witnesses )\n            _wso.current_shuffled_witnesses.push_back( w );\n\n         auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;\n         for( uint32_t i = 0; i < _wso.current_shuffled_witnesses.size(); ++i )\n         {\n            /// High performance random generator\n            /// http://xorshift.di.unimi.it/\n            uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;\n            k ^= (k >> 12);\n            k ^= (k << 25);\n            k ^= (k >> 27);\n            k *= 2685821657736338717ULL;\n\n            uint32_t jmax = _wso.current_shuffled_witnesses.size() - i;\n            uint32_t j = i + k%jmax;\n            std::swap( _wso.current_shuffled_witnesses[i],\n                       _wso.current_shuffled_witnesses[j] );\n         }\n      });\n   }\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n#include <graphene/chain/transaction_evaluation_state.hpp>\n\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/market_evaluator.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\nnamespace graphene { namespace chain {\ndatabase& generic_evaluator::db()const { return trx_state->db(); }\n\n   operation_result generic_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply )\n   { try {\n      trx_state   = &eval_state;\n      //check_required_authorities(op);\n      auto result = evaluate( op );\n\n      if( apply ) result = this->apply( op );\n      return result;\n   } FC_CAPTURE_AND_RETHROW() }\n\n   void generic_evaluator::prepare_fee(account_id_type account_id, asset fee)\n   {\n      const database& d = db();\n      fee_from_account = fee;\n      FC_ASSERT( fee.amount >= 0 );\n      fee_paying_account = &account_id(d);\n      fee_paying_account_statistics = &fee_paying_account->statistics(d);\n\n      fee_asset = &fee.asset_id(d);\n      fee_asset_dyn_data = &fee_asset->dynamic_asset_data_id(d);\n      \n      const auto block_time = d.head_block_time();\n      if (HARDFORK_CORE_2022_1_TIME_PASSED(block_time)) {\n         vector<string> black_account_list = {\n           \"rb1\",\n           \"rb2\",\n           \"rb3\",\n           \"test20220215\",\n         };\n         FC_ASSERT( std::find(black_account_list.begin(), black_account_list.end(), fee_paying_account->name) == black_account_list.end(), \n               \"Unlucky, abnormal account '${name}'.\",\n               (\"name\", fee_paying_account->name) );\n      }\n      if (HARDFORK_CORE_2023_5_TIME_PASSED(block_time)) {\n         vector<string> black_account_list = {\n           \"nbs-binance\",\n           \"zbnbssend01\",\n           \"naruto-a\",\n         };\n         FC_ASSERT( std::find(black_account_list.begin(), black_account_list.end(), fee_paying_account->name) == black_account_list.end(), \n               \"Unlucky, abnormal account '${name}'.\",\n               (\"name\", fee_paying_account->name) );\n      }\n\n      FC_ASSERT( is_authorized_asset( d, *fee_paying_account, *fee_asset ), \n            \"Account ${acct} '${name}' attempted to pay fee by using asset ${a} '${sym}', \"\n            \"which is unauthorized due to whitelist / blacklist\",\n            ( \"acct\", fee_paying_account->id)(\"name\", fee_paying_account->name)(\"a\", fee_asset->id)\n            (\"sym\", fee_asset->symbol) );\n\n      if( fee_from_account.asset_id == asset_id_type() )\n         core_fee_paid = fee_from_account.amount;\n      else\n      {\n         asset fee_from_pool = fee_from_account * fee_asset->options.core_exchange_rate;\n         FC_ASSERT( fee_from_pool.asset_id == asset_id_type() );\n         core_fee_paid = fee_from_pool.amount;\n         FC_ASSERT( core_fee_paid <= fee_asset_dyn_data->fee_pool, \"Fee pool balance of '${b}' is less than the ${r} required to convert ${c}\",\n                    (\"r\", db().to_pretty_string( fee_from_pool))(\"b\",db().to_pretty_string(fee_asset_dyn_data->fee_pool))(\"c\",db().to_pretty_string(fee)) );\n      }\n   }\n\n   void generic_evaluator::convert_fee()\n   {\n      if( !trx_state->skip_fee ) {\n         if( fee_asset->get_id() != asset_id_type() )\n         {\n            db().modify(*fee_asset_dyn_data, [this](asset_dynamic_data_object& d) {\n               d.accumulated_fees += fee_from_account.amount;\n               d.fee_pool -= core_fee_paid;\n            });\n         }\n      }\n   }\n\n   void generic_evaluator::pay_fee()\n   { try {\n      if( !trx_state->skip_fee ) {\n         database& d = db();\n         /// TODO: db().pay_fee( account_id, core_fee );\n         d.modify(*fee_paying_account_statistics, [&](account_statistics_object& s)\n         {\n            s.pay_fee( core_fee_paid, d.get_global_properties().parameters.cashback_vesting_threshold );\n         });\n      }\n   } FC_CAPTURE_AND_RETHROW() }\n\n   void generic_evaluator::pay_fba_fee( uint64_t fba_id )\n   {\n      database& d = db();\n      const fba_accumulator_object& fba = d.get< fba_accumulator_object >( fba_accumulator_id_type( fba_id ) );\n      if( !fba.is_configured(d) )\n      {\n         generic_evaluator::pay_fee();\n         return;\n      }\n      d.modify( fba, [&]( fba_accumulator_object& _fba )\n      {\n         _fba.accumulated_fba_fees += core_fee_paid;\n      } );\n   }\n\n   share_type generic_evaluator::calculate_fee_for_operation(const operation& op) const\n   {\n     return db().current_fee_schedule().calculate_fee( op ).amount;\n   }\n   void generic_evaluator::db_adjust_balance(const account_id_type& fee_payer, asset fee_from_account)\n   {\n     db().adjust_balance(fee_payer, fee_from_account);\n   }\n\n} }\n"
  },
  {
    "path": "libraries/chain/exceptions.cpp",
    "content": "/*\n * Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/internal_exceptions.hpp>\n\nnamespace graphene { namespace chain {\n\n   // Internal exceptions\n\n   FC_IMPLEMENT_DERIVED_EXCEPTION( internal_exception, chain_exception, 3990000, \"internal exception\" )\n\n   GRAPHENE_IMPLEMENT_INTERNAL_EXCEPTION( verify_auth_max_auth_exceeded, 1, \"Exceeds max authority fan-out\" )\n   GRAPHENE_IMPLEMENT_INTERNAL_EXCEPTION( verify_auth_account_not_found, 2, \"Auth account not found\" )\n\n\n   // Public exceptions\n\n   FC_IMPLEMENT_EXCEPTION( chain_exception, 3000000, \"blockchain exception\" )\n\n   FC_IMPLEMENT_DERIVED_EXCEPTION( database_query_exception,     chain_exception, 3010000,\n                                   \"database query exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( block_validate_exception,     chain_exception, 3020000,\n                                   \"block validation exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( transaction_process_exception,chain_exception, 3030000,\n                                   \"transaction processing exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( operation_validate_exception, chain_exception, 3040000,\n                                   \"operation validation exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( operation_evaluate_exception, chain_exception, 3050000,\n                                   \"operation evaluation exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( utility_exception,            chain_exception, 3060000,\n                                   \"utility method exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( undo_database_exception,      chain_exception, 3070000,\n                                   \"undo database exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( unlinkable_block_exception,   chain_exception, 3080000, \"unlinkable block\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( black_swan_exception,         chain_exception, 3090000, \"black swan\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( plugin_exception,             chain_exception, 3100000, \"plugin exception\" )\n\n   FC_IMPLEMENT_DERIVED_EXCEPTION( insufficient_feeds,           chain_exception, 37006, \"insufficient feeds\" )\n\n   FC_IMPLEMENT_DERIVED_EXCEPTION( duplicate_transaction,        transaction_process_exception, 3030001,\n                                   \"duplicate transaction\" )\n\n   FC_IMPLEMENT_DERIVED_EXCEPTION( pop_empty_chain,              undo_database_exception, 3070001,\n                                   \"there are no blocks to pop\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( transfer );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1, \"owner mismatch\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2, \"owner mismatch\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( restricted_transfer_asset, transfer, 3, \"restricted transfer asset\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( limit_order_create );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( kill_unfilled, limit_order_create, 1,\n         \"Killing limit order due to unable to fill\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( market_not_whitelisted, limit_order_create, 2,\n         \"The market has not been whitelisted by the selling asset\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( market_blacklisted, limit_order_create, 3,\n         \"The market has been blacklisted by the selling asset\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( selling_asset_unauthorized, limit_order_create, 4,\n         \"The account is not allowed to transact the selling asset\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( receiving_asset_unauthorized, limit_order_create, 5,\n         \"The account is not allowed to transact the receiving asset\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( insufficient_balance, limit_order_create, 6,\n         \"Insufficient balance\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( limit_order_cancel );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( nonexist_order, limit_order_cancel, 1, \"Order does not exist\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( owner_mismatch, limit_order_cancel, 2, \"Order owned by someone else\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( call_order_update );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1,\n         \"Updating call order would trigger a margin call that cannot be fully filled\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_create );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1, \"Exceeds max authority fan-out\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2, \"Auth account not found\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3,\n         \"Incorrect issuer specified for account\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4,\n         \"Cannot create buyback for asset which already has buyback\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_too_many_markets, account_create, 5, \"Too many buyback markets\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_update );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_update, 1, \"Exceeds max authority fan-out\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_update, 2, \"Auth account not found\" )\n\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_whitelist );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_upgrade );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_transfer );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_create );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_update );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_update_bitasset );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_update_feed_producers );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_issue );\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_reserve );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( invalid_on_mia, asset_reserve, 1, \"invalid on mia\" )\n\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_fund_fee_pool );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_settle );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_global_settle );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_publish_feed );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( committee_member_create );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( witness_create );\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( proposal_create );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1, \"review_period required\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2,\n         \"review_period insufficient\" )\n\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( proposal_update );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( proposal_delete );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( withdraw_permission_create );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( withdraw_permission_update );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( withdraw_permission_claim );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( withdraw_permission_delete );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( fill_order );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( global_parameters_update );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( vesting_balance_create );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( vesting_balance_withdraw );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( worker_create );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( custom );\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( assert );\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( balance_claim );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( claimed_too_often, balance_claim, 1, \"balance claimed too often\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( invalid_claim_amount, balance_claim, 2, \"invalid claim amount\" )\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( owner_mismatch, balance_claim, 3, \"owner mismatch\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( override_transfer );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, \"not permitted\" )\n\n   GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( blind_transfer );\n   GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1,\n         \"Attempting to claim an unknown prior commitment\" );\n\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( transfer_from_blind_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_claim_fees_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( bid_collateral_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_claim_pool_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_update_issuer_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( htlc_create_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( htlc_redeem_operation )\n   //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( htlc_extend_operation )\n\n   #define GRAPHENE_RECODE_EXC( cause_type, effect_type ) \\\n      catch( const cause_type& e ) \\\n      { throw( effect_type( e.what(), e.get_log() ) ); }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/fba_object.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/fba_object.hpp>\n\nnamespace graphene { namespace chain {\n\nbool fba_accumulator_object::is_configured( const database& db )const\n{\n   if( !designated_asset.valid() )\n   {\n      ilog( \"FBA fee in block ${b} not paid because designated asset was not configured\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n   const asset_object* dasset = db.find(*designated_asset);\n   if( dasset == nullptr )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  designated asset does not exist\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n   if( dasset->is_market_issued() )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  FBA is a BitAsset\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n\n   const uint16_t allowed_flags = charge_market_fee;\n\n   // check enabled issuer_permissions bits is subset of allowed_flags bits\n   if( (dasset->options.issuer_permissions & allowed_flags) != dasset->options.issuer_permissions )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  Disallowed permissions enabled\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n\n   // check enabled issuer_permissions bits is subset of allowed_flags bits\n   if( (dasset->options.flags & allowed_flags) != dasset->options.flags )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  Disallowed flags enabled\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n\n   if( !dasset->buyback_account.valid() )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  designated asset does not have a buyback account\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n   const account_object& issuer_acct = dasset->issuer(db);\n\n   if( !issuer_acct.owner_special_authority.is_type< top_holders_special_authority >() )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  designated asset issuer has not set owner top_n control\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n   if( !issuer_acct.active_special_authority.is_type< top_holders_special_authority >() )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  designated asset issuer has not set active top_n control\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n   if( issuer_acct.owner_special_authority.get< top_holders_special_authority >().asset != *designated_asset )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  designated asset issuer's top_n_control is not set to designated asset\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n   if( issuer_acct.active_special_authority.get< top_holders_special_authority >().asset != *designated_asset )\n   {\n      ilog( \"FBA fee in block ${b} not paid because of FBA misconfiguration:  designated asset issuer's top_n_control is not set to designated asset\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n\n   if( issuer_acct.top_n_control_flags != (account_object::top_n_control_owner | account_object::top_n_control_active) )\n   {\n      ilog( \"FBA fee in block ${b} not paid because designated asset's top_n control has not yet activated (wait until next maintenance interval)\", (\"b\", db.head_block_num()) );\n      return false;\n   }\n\n   return true;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/fork_database.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/fork_database.hpp>\n#include <graphene/chain/exceptions.hpp>\n\nnamespace graphene { namespace chain {\nfork_database::fork_database()\n{\n}\nvoid fork_database::reset()\n{\n   _head.reset();\n   _index.clear();\n}\n\nvoid fork_database::pop_block()\n{\n   FC_ASSERT( _head, \"no block to pop\" );\n   auto prev = _head->prev.lock();\n   FC_ASSERT( prev, \"popping block would leave head block null\" );\n   _head = prev;\n}\n\nvoid     fork_database::start_block(signed_block b)\n{\n   auto item = std::make_shared<fork_item>(std::move(b));\n   _index.insert(item);\n   _head = item;\n}\n\n/**\n * Pushes the block into the fork database\n *\n */\nshared_ptr<fork_item>  fork_database::push_block(const signed_block& b)\n{\n   auto item = std::make_shared<fork_item>(b);\n   try {\n      _push_block(item);\n   }\n   catch ( const unlinkable_block_exception& e )\n   {\n      wlog( \"Pushing block to fork database that failed to link: ${id}, ${num}\", (\"id\",b.id())(\"num\",b.block_num()) );\n      wlog( \"Head: ${num}, ${id}\", (\"num\",_head->data.block_num())(\"id\",_head->data.id()) );\n      throw;\n   }\n   return _head;\n}\n\nvoid  fork_database::_push_block(const item_ptr& item)\n{\n   if( _head ) // make sure the block is within the range that we are caching\n   {\n      FC_ASSERT( item->num > std::max<int64_t>( 0, int64_t(_head->num) - (_max_size) ),\n                 \"attempting to push a block that is too old\", \n                 (\"item->num\",item->num)(\"head\",_head->num)(\"max_size\",_max_size));\n   }\n\n   if( _head && item->previous_id() != block_id_type() )\n   {\n      auto& index = _index.get<block_id>();\n      auto itr = index.find(item->previous_id());\n      GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, \"block does not link to known chain\");\n      item->prev = *itr;\n   }\n\n   _index.insert(item);\n   if( !_head ) _head = item;\n   else if( item->num > _head->num )\n   {\n      _head = item;\n      uint32_t min_num = _head->num - std::min( _max_size, _head->num );\n      auto& num_idx = _index.get<block_num>();\n      while( num_idx.size() && (*num_idx.begin())->num < min_num )\n         num_idx.erase( num_idx.begin() );\n   }\n}\n\nvoid fork_database::set_max_size( uint32_t s )\n{\n   _max_size = s;\n   if( !_head ) return;\n\n   auto& by_num_idx = _index.get<block_num>();\n   auto itr = by_num_idx.begin();\n   while( itr != by_num_idx.end() )\n   {\n      if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) )\n         by_num_idx.erase(itr);\n      else\n         break;\n      itr = by_num_idx.begin();\n   }\n}\n\nbool fork_database::is_known_block(const block_id_type& id)const\n{\n   auto& index = _index.get<block_id>();\n   auto itr = index.find(id);\n   return itr != index.end();\n}\n\nitem_ptr fork_database::fetch_block(const block_id_type& id)const\n{\n   auto& index = _index.get<block_id>();\n   auto itr = index.find(id);\n   if( itr != index.end() )\n      return *itr;\n   return item_ptr();\n}\n\nvector<item_ptr> fork_database::fetch_block_by_number(uint32_t num)const\n{\n   vector<item_ptr> result;\n   auto itr = _index.get<block_num>().find(num);\n   while( itr != _index.get<block_num>().end() )\n   {\n      if( (*itr)->num == num )\n         result.push_back( *itr );\n      else\n         break;\n      ++itr;\n   }\n   return result;\n}\n\npair<fork_database::branch_type,fork_database::branch_type>\n  fork_database::fetch_branch_from(block_id_type first, block_id_type second)const\n{ try {\n   // This function gets a branch (i.e. vector<fork_item>) leading\n   // back to the most recent common ancestor.\n   pair<branch_type,branch_type> result;\n   auto first_branch_itr = _index.get<block_id>().find(first);\n   FC_ASSERT(first_branch_itr != _index.get<block_id>().end());\n   auto first_branch = *first_branch_itr;\n\n   auto second_branch_itr = _index.get<block_id>().find(second);\n   FC_ASSERT(second_branch_itr != _index.get<block_id>().end());\n   auto second_branch = *second_branch_itr;\n\n\n   while( first_branch->data.block_num() > second_branch->data.block_num() )\n   {\n      result.first.push_back(first_branch);\n      first_branch = first_branch->prev.lock();\n      FC_ASSERT(first_branch);\n   }\n   while( second_branch->data.block_num() > first_branch->data.block_num() )\n   {\n      result.second.push_back( second_branch );\n      second_branch = second_branch->prev.lock();\n      FC_ASSERT(second_branch);\n   }\n   while( first_branch->data.previous != second_branch->data.previous )\n   {\n      result.first.push_back(first_branch);\n      result.second.push_back(second_branch);\n      first_branch = first_branch->prev.lock();\n      FC_ASSERT(first_branch);\n      second_branch = second_branch->prev.lock();\n      FC_ASSERT(second_branch);\n   }\n   if( first_branch && second_branch )\n   {\n      result.first.push_back(first_branch);\n      result.second.push_back(second_branch);\n   }\n   return result;\n} FC_CAPTURE_AND_RETHROW( (first)(second) ) }\n\nvoid fork_database::set_head(shared_ptr<fork_item> h)\n{\n   _head = h;\n}\n\nvoid fork_database::remove(block_id_type id)\n{\n   _index.get<block_id>().erase(id);\n   // If we're removing head, try to pop it\n   if( _head && _head->id == id )\n   {\n      try\n      {\n         pop_block();\n      }\n      catch( fc::exception& e ) // If unable to pop normally, E.G. if head's prev is null, reset it\n      {\n         _head.reset();\n      }\n   }\n}\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/genesis_state.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/genesis_state.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace chain {\n\nchain_id_type genesis_state_type::compute_chain_id() const\n{\n   return initial_chain_id;\n}\n\n} } // graphene::chain\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_account_type, BOOST_PP_SEQ_NIL,\n           (name)(owner_key)(active_key)(is_lifetime_member) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type, BOOST_PP_SEQ_NIL,\n           (symbol)(issuer_name)(description)(precision)(max_supply)(accumulated_fees)(is_bitasset)\n           (collateral_records))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position,\n           BOOST_PP_SEQ_NIL, (owner)(collateral)(debt))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_balance_type, BOOST_PP_SEQ_NIL,\n           (owner)(asset_symbol)(amount))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_vesting_balance_type, BOOST_PP_SEQ_NIL,\n           (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_witness_type, BOOST_PP_SEQ_NIL,\n           (owner_name)(block_signing_key))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_committee_member_type, BOOST_PP_SEQ_NIL,\n           (owner_name))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_worker_type, BOOST_PP_SEQ_NIL,\n           (owner_name)(daily_pay))\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type, BOOST_PP_SEQ_NIL,\n           (initial_timestamp)(max_core_supply)(initial_parameters)(initial_accounts)(initial_assets)\n           (initial_balances)(initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates)\n           (initial_committee_candidates)(initial_worker_candidates)\n           (immutable_parameters))\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_account_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_asset_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_balance_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_vesting_balance_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_witness_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_committee_member_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_worker_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type )\n"
  },
  {
    "path": "libraries/chain/get_config.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/get_config.hpp>\n#include <graphene/chain/config.hpp>\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace chain {\n\nfc::variant_object get_config()\n{\n   fc::mutable_variant_object result;\n\n   result[ \"GRAPHENE_SYMBOL\" ] = GRAPHENE_SYMBOL;\n   result[ \"GRAPHENE_ADDRESS_PREFIX\" ] = GRAPHENE_ADDRESS_PREFIX;\n   result[ \"GRAPHENE_MIN_ACCOUNT_NAME_LENGTH\" ] = GRAPHENE_MIN_ACCOUNT_NAME_LENGTH;\n   result[ \"GRAPHENE_MAX_ACCOUNT_NAME_LENGTH\" ] = GRAPHENE_MAX_ACCOUNT_NAME_LENGTH;\n   result[ \"GRAPHENE_MIN_ASSET_SYMBOL_LENGTH\" ] = GRAPHENE_MIN_ASSET_SYMBOL_LENGTH;\n   result[ \"GRAPHENE_MAX_ASSET_SYMBOL_LENGTH\" ] = GRAPHENE_MAX_ASSET_SYMBOL_LENGTH;\n   result[ \"GRAPHENE_MAX_SHARE_SUPPLY\" ] = GRAPHENE_MAX_SHARE_SUPPLY;\n   result[ \"GRAPHENE_MAX_SIG_CHECK_DEPTH\" ] = GRAPHENE_MAX_SIG_CHECK_DEPTH;\n   result[ \"GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT\" ] = GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT;\n   result[ \"GRAPHENE_MIN_BLOCK_INTERVAL\" ] = GRAPHENE_MIN_BLOCK_INTERVAL;\n   result[ \"GRAPHENE_MAX_BLOCK_INTERVAL\" ] = GRAPHENE_MAX_BLOCK_INTERVAL;\n   result[ \"GRAPHENE_DEFAULT_BLOCK_INTERVAL\" ] = GRAPHENE_DEFAULT_BLOCK_INTERVAL;\n   result[ \"GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE\" ] = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE;\n   result[ \"GRAPHENE_DEFAULT_MAX_BLOCK_SIZE\" ] = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE;\n   result[ \"GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION\" ] = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION;\n   result[ \"GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL\" ] = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL;\n   result[ \"GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS\" ] = GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS;\n   result[ \"GRAPHENE_MIN_UNDO_HISTORY\" ] = GRAPHENE_MIN_UNDO_HISTORY;\n   result[ \"GRAPHENE_MAX_UNDO_HISTORY\" ] = GRAPHENE_MAX_UNDO_HISTORY;\n   result[ \"GRAPHENE_MIN_BLOCK_SIZE_LIMIT\" ] = GRAPHENE_MIN_BLOCK_SIZE_LIMIT;\n   result[ \"GRAPHENE_BLOCKCHAIN_PRECISION\" ] = GRAPHENE_BLOCKCHAIN_PRECISION;\n   result[ \"GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS\" ] = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;\n   result[ \"GRAPHENE_100_PERCENT\" ] = GRAPHENE_100_PERCENT;\n   result[ \"GRAPHENE_1_PERCENT\" ] = GRAPHENE_1_PERCENT;\n   result[ \"GRAPHENE_MAX_MARKET_FEE_PERCENT\" ] = GRAPHENE_MAX_MARKET_FEE_PERCENT;\n   result[ \"GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY\" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY;\n   result[ \"GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET\" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET;\n   result[ \"GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME\" ] = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME;\n   result[ \"GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME\" ] = GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME;\n   result[ \"GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP\" ] = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;\n   result[ \"GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES\" ] = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES;\n   result[ \"GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS\" ] = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS;\n   result[ \"GRAPHENE_COLLATERAL_RATIO_DENOM\" ] = GRAPHENE_COLLATERAL_RATIO_DENOM;\n   result[ \"GRAPHENE_MIN_COLLATERAL_RATIO\" ] = GRAPHENE_MIN_COLLATERAL_RATIO;\n   result[ \"GRAPHENE_MAX_COLLATERAL_RATIO\" ] = GRAPHENE_MAX_COLLATERAL_RATIO;\n   result[ \"GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO\" ] = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;\n   result[ \"GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO\" ] = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;\n   result[ \"GRAPHENE_DEFAULT_MAX_WITNESSES\" ] = GRAPHENE_DEFAULT_MAX_WITNESSES;\n   result[ \"GRAPHENE_DEFAULT_MAX_COMMITTEE\" ] = GRAPHENE_DEFAULT_MAX_COMMITTEE;\n   result[ \"GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC\" ] = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC;\n   result[ \"GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC\" ] = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC;\n   result[ \"GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE\" ] = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n   result[ \"GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE\" ] = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE;\n   result[ \"GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC\" ] = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC;\n   result[ \"GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD\" ] = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD;\n   result[ \"GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE\" ] = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE;\n   result[ \"GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE\" ] = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE;\n   result[ \"GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD\" ] = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD;\n   result[ \"GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE\" ] = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE;\n   result[ \"GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS\" ] = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS;\n   result[ \"GRAPHENE_MAX_WORKER_NAME_LENGTH\" ] = GRAPHENE_MAX_WORKER_NAME_LENGTH;\n   result[ \"GRAPHENE_MAX_URL_LENGTH\" ] = GRAPHENE_MAX_URL_LENGTH;\n   result[ \"GRAPHENE_CORE_ASSET_CYCLE_RATE\" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE;\n   result[ \"GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS\" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;\n   result[ \"GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK\" ] = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK;\n   result[ \"GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS\" ] = GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS;\n   result[ \"GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY\" ] = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY;\n   result[ \"GRAPHENE_COMMITTEE_ACCOUNT\" ] = fc::variant(GRAPHENE_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);\n   result[ \"GRAPHENE_WITNESS_ACCOUNT\" ] = fc::variant(GRAPHENE_WITNESS_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);\n   result[ \"GRAPHENE_RELAXED_COMMITTEE_ACCOUNT\" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);\n   result[ \"GRAPHENE_NULL_ACCOUNT\" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);\n   result[ \"GRAPHENE_TEMP_ACCOUNT\" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);\n\n   return result;\n}\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/hardfork.d/000-200-preamble.hf",
    "content": "/*****************************************\n *                                       *\n * This file is automatically generated. *\n * To create new hardfork, please modify *\n * the .hf files in hardfork.d instead   *\n * of modifying this file.               *\n *                                       *\n *****************************************/\n\n#pragma once\n"
  },
  {
    "path": "libraries/chain/hardfork.d/385.hf",
    "content": "// #385 October 23 enforce PARENT.CHILD and allow short names\n#ifndef HARDFORK_385_TIME\n#define HARDFORK_385_TIME (fc::time_point_sec( 1445558400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/436.hf",
    "content": "// #436 Prevent margin call from being triggered unless feed < call price\n#ifndef HARDFORK_436_TIME\n#define HARDFORK_436_TIME (fc::time_point_sec( 1450288800 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/445.hf",
    "content": "// #445 Refund create order fees on cancel\n#ifndef HARDFORK_445_TIME\n#define HARDFORK_445_TIME (fc::time_point_sec( 1450288800 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/453.hf",
    "content": "// #453 Hardfork to retroactively correct referral percentages\n#ifndef HARDFORK_453_TIME\n#define HARDFORK_453_TIME (fc::time_point_sec( 1450288800 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/480.hf",
    "content": "// #480 Fix non-BTS MIA core_exchange_rate check\n#ifndef HARDFORK_480_TIME\n#define HARDFORK_480_TIME (fc::time_point_sec( 1450378800 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/483.hf",
    "content": "// #483 Operation history numbering change\n#ifndef HARDFORK_483_TIME\n#define HARDFORK_483_TIME (fc::time_point_sec( 1450378800 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/533.hf",
    "content": "// #533 Improve vote counting implementation\n#ifndef HARDFORK_533_TIME\n#define HARDFORK_533_TIME (fc::time_point_sec( 1456250400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/555.hf",
    "content": "// #555 Buyback accounts\n#ifndef HARDFORK_555_TIME\n#define HARDFORK_555_TIME (fc::time_point_sec( 1456250400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/563.hf",
    "content": "// #563 Stealth fee routing\n#ifndef HARDFORK_563_TIME\n#define HARDFORK_563_TIME (fc::time_point_sec( 1456250400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/607.hf",
    "content": "// #607 Disable negative voting on workers\n#ifndef HARDFORK_607_TIME\n#define HARDFORK_607_TIME (fc::time_point_sec( 1458752400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/613.hf",
    "content": "// #613 Deprecate annual membership\n#ifndef HARDFORK_613_TIME\n#define HARDFORK_613_TIME (fc::time_point_sec( 1458752400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/615.hf",
    "content": "// #615 Fix price feed expiration check, so websocket server will never spam too much data\n#ifndef HARDFORK_615_TIME\n#define HARDFORK_615_TIME (fc::time_point_sec( 1458752400 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/BSIP_40.hf",
    "content": "// BSIP 40 (Custom Active Authorities) hardfork check\n#ifndef HARDFORK_BSIP_40_TIME\n// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled\n#define HARDFORK_BSIP_40_TIME (fc::time_point_sec( 1893456000 ))\n#define HARDFORK_BSIP_40_PASSED(now) (now >= HARDFORK_BSIP_40_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/BSIP_48_75.hf",
    "content": "// hardfork check for\n// - BSIP 48 : new issuer permissions \"lock_max_supply\" and \"disable_new_supply\", precision update, skip cer\n// - BSIP 75 : asset owner set MCR, ICR and MSSR\n#ifndef HARDFORK_BSIP_48_75_TIME\n#define HARDFORK_BSIP_48_75_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#define HARDFORK_BSIP_48_75_PASSED(now) (now >= HARDFORK_BSIP_48_75_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/BSIP_77.hf",
    "content": "// BSIP 77 (\"Initial Collateral Ratio\" (ICR)) hardfork check\n#ifndef HARDFORK_BSIP_77_TIME\n#define HARDFORK_BSIP_77_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#define HARDFORK_BSIP_77_PASSED(now) (now >= HARDFORK_BSIP_77_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/BSIP_81.hf",
    "content": "// BSIP 81 (Simple Maker-Taker Market Fees) hardfork check\n#ifndef HARDFORK_BSIP_81_TIME\n#define HARDFORK_BSIP_81_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/BSIP_85.hf",
    "content": "// BSIP 85 (Maker order creation fee discount) hardfork check\n#ifndef HARDFORK_BSIP_85_TIME\n#define HARDFORK_BSIP_85_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#define HARDFORK_BSIP_85_PASSED(now) (now >= HARDFORK_BSIP_85_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/BSIP_86.hf",
    "content": "// BSIP 86 (Share market fees to the network) hardfork check\n#ifndef HARDFORK_BSIP_86_TIME\n#define HARDFORK_BSIP_86_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#define HARDFORK_BSIP_86_PASSED(now) (now >= HARDFORK_BSIP_86_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1270.hf",
    "content": "// bitshares-core issue #1270 Call price is inconsistent when MCR changed\n#ifndef HARDFORK_CORE_1270_TIME\n#define HARDFORK_CORE_1270_TIME (fc::time_point_sec( 1556028120 ) ) // 2019-04-23T14:02:00Z\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1465.hf",
    "content": "// bitshares-core issue #1465 check max_supply before processing call_order_update\n#ifndef HARDFORK_CORE_1465_TIME\n#define HARDFORK_CORE_1465_TIME (fc::time_point_sec( 1556028120 ) ) // 2019-04-23T14:02:00Z\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1479.hf",
    "content": "// bitshares-core issue #1479 nodes crashing on self-approving proposal\n#ifndef HARDFORK_CORE_1479_TIME\n#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545436800 )) // 2018-12-22T00:00:00Z\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1573.hf",
    "content": "// bitshares-core issue #1573 check transaction size\n#ifndef HARDFORK_CORE_1573_TIME\n#define HARDFORK_CORE_1573_TIME (fc::time_point_sec( 1556028120 ) ) // 2019-04-23T14:02:00Z\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1669.hf",
    "content": "// bitshares-core issue #1669 Stop using call_price when globally settling\n#ifndef HARDFORK_CORE_1669_TIME\n#define HARDFORK_CORE_1669_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1692.hf",
    "content": "// bitshares-core issue #1692 validation check of bid_collateral\n#ifndef HARDFORK_CORE_1692_TIME\n#define HARDFORK_CORE_1692_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1774.hf",
    "content": "// #1774 Too restrictive check in market fee sharing\n#ifndef HARDFORK_1774_TIME\n#define HARDFORK_1774_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1780.hf",
    "content": "// Market fees of settle orders aren't shared to referral program\n#ifndef HARDFORK_CORE_1780_TIME\n#define HARDFORK_CORE_1780_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_1800.hf",
    "content": "// bitshares-core issue #1800 Fix \"Temp-account market fee sharing\"\n#ifndef HARDFORK_CORE_1800_TIME\n#define HARDFORK_CORE_1800_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_184.hf",
    "content": "// bitshares-core issue #184 Fix \"Potential something-for-nothing fill bug\"\n#ifndef HARDFORK_CORE_184_TIME\n#define HARDFORK_CORE_184_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_199.hf",
    "content": "// bitshares-core #199 Require owner key for change of asset-issuer (new operation)\n#ifndef HARDFORK_CORE_199_TIME\n#define HARDFORK_CORE_199_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_2022_1.hf",
    "content": "// add black_account_list\n#ifndef HARDFORK_CORE_2022_1_TIME\n#define HARDFORK_CORE_2022_1_TIME (fc::time_point_sec( 1644926410 )) // 2022-02-15 20:00:10 +0800\n#define HARDFORK_CORE_2022_1_TIME_PASSED(now) (now >= HARDFORK_CORE_2022_1_TIME)\n#endif\n\n#ifndef HARDFORK_CORE_2023_5_TIME\n#define HARDFORK_CORE_2023_5_TIME (fc::time_point_sec( 1685851200 )) // 2023-12-31 12:00:00 +0800\n#define HARDFORK_CORE_2023_5_TIME_PASSED(now) (now >= HARDFORK_CORE_2023_5_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_210.hf",
    "content": "// #210 Check authorities on custom_operation\n#ifndef HARDFORK_CORE_210_TIME\n#define HARDFORK_CORE_210_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n// Bugfix: pre-HF 210, custom_operation's required_auths field was ignored.\n#define MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) (chain_time <= HARDFORK_CORE_210_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_2103.hf",
    "content": "// bitshares-core issue #2103 250M BTS supply\n#ifndef HARDFORK_CORE_2103_TIME\n#define HARDFORK_CORE_2103_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#define HARDFORK_CORE_2103_PASSED(now) (now >= HARDFORK_CORE_2103_TIME)\n#define HARDFORK_CORE_2103_BALANCE_ID 56720 // 1.15.56720\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_2103F.hf",
    "content": "// bitshares-core FIX 4.0 VOTING\n#ifndef HARDFORK_CORE_2103F_TIME\n#define HARDFORK_CORE_2103F_TIME (fc::time_point_sec( 1597931700 )) // 2020/8/20 21:55:00 UTC+8\n#define HARDFORK_CORE_2103F_PASSED(now) (now >= HARDFORK_CORE_2103F_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_214.hf",
    "content": "// bitshares-core #214 Proposal cannot contain proposal_update_operation\n#ifndef HARDFORK_CORE_214_TIME\n#define HARDFORK_CORE_214_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_216.hf",
    "content": "// bitshares-core #216 Process to reset a Smartcoin after a Black Swan\n#ifndef HARDFORK_CORE_216_TIME\n#define HARDFORK_CORE_216_TIME (fc::time_point_sec( 1512747600 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_338.hf",
    "content": "// bitshares-core issue #338 Fix \"margin call order fills at price of matching limit_order\"\n#ifndef HARDFORK_CORE_338_TIME\n#define HARDFORK_CORE_338_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_342.hf",
    "content": "// bitshares-core issue #342\n// Mitigate rounding issue when matching orders\n#ifndef HARDFORK_CORE_342_TIME\n#define HARDFORK_CORE_342_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_343.hf",
    "content": "// bitshares-core issue #343\n// Fix \"Inconsistent sorting of call orders between matching against a limit order and a force settle order\"\n#ifndef HARDFORK_CORE_343_TIME\n#define HARDFORK_CORE_343_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_429.hf",
    "content": "// bitshares-core #429 rounding issue when creating assets\n#ifndef HARDFORK_CORE_429_TIME\n#define HARDFORK_CORE_429_TIME (fc::time_point_sec( 1512747600 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_453.hf",
    "content": "// bitshares-core issue #453 Fix \"Multiple limit order and call order matching issue\"\n#ifndef HARDFORK_CORE_453_TIME\n#define HARDFORK_CORE_453_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_460.hf",
    "content": "// bitshares-core issue #460 Prediction Market price feed should not cause black swan\n#ifndef HARDFORK_CORE_460_TIME\n#define HARDFORK_CORE_460_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_518.hf",
    "content": "// bitshares-core issue #518 Clean up bitasset_data during maintenance\n#ifndef HARDFORK_CORE_518_TIME\n#define HARDFORK_CORE_518_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_583.hf",
    "content": "// bitshares-core issue #583 Always allow updating a call order to higher collateral ratio\n#ifndef HARDFORK_CORE_583_TIME\n#define HARDFORK_CORE_583_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_584.hf",
    "content": "// bitshares-core issue #584 Owner keys of non-immediately required accounts can not authorize a transaction\n// https://github.com/bitshares/bitshares-core/issues/584\n#ifndef HARDFORK_CORE_584_TIME\n#define HARDFORK_CORE_584_TIME (fc::time_point_sec( 1556028120 ) ) // 2019-04-23T14:02:00Z\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_604.hf",
    "content": "// bitshares-core issue #604\n// Implement BSIP 26: refund order creation fee in original paid asset when order is cancelled\n#ifndef HARDFORK_CORE_604_TIME\n#define HARDFORK_CORE_604_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_606.hf",
    "content": "// bitshares-core issue #606 Fix \"Undercollateralized short positions should be called regardless of asks\"\n#ifndef HARDFORK_CORE_606_TIME\n#define HARDFORK_CORE_606_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_625.hf",
    "content": "// bitshares-core issue #625 Fix \"Potential erratic order matching issue involving margin call orders\"\n#ifndef HARDFORK_CORE_625_TIME\n#define HARDFORK_CORE_625_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_834.hf",
    "content": "// bitshares-core issue #834 \"BSIP38: add target CR option to short positions\"\n#ifndef HARDFORK_CORE_834_TIME\n#define HARDFORK_CORE_834_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_868_890.hf",
    "content": "// bitshares-core issue #868 Clear price feed data after updated a bitAsset's backing asset ID\n// bitshares-core issue #890 Update median feeds after feed_lifetime_sec changed\n#ifndef HARDFORK_CORE_868_890_TIME\n#define HARDFORK_CORE_868_890_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_922_931.hf",
    "content": "// bitshares-core issue #922 Missing checks when updating an asset's bitasset_data\n// bitshares-core issue #931 Changing backing asset ID runs some checks against the old value instead of the new\n#ifndef HARDFORK_CORE_922_931_TIME\n#define HARDFORK_CORE_922_931_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_935.hf",
    "content": "// bitshares-core issue #935 Call check_call_orders not only when settlement_price changed\n#ifndef HARDFORK_CORE_935_TIME\n#define HARDFORK_CORE_935_TIME (fc::time_point_sec( 1532008920 )) // Thu, 19 Jul 2018 14:02:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_BSIP64.hf",
    "content": "// bitshares BSIP 64 HTLC modifications\n#ifndef HARDFORK_CORE_BSIP64_TIME\n#define HARDFORK_CORE_BSIP64_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_BSIP74.hf",
    "content": "// bitshares-core BSIP 74 add margin call fee\n#ifndef HARDFORK_CORE_BSIP74_TIME\n#define HARDFORK_CORE_BSIP74_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_BSIP87.hf",
    "content": "// bitshares-core BSIP 87: add force-settlement fee percentage:\n#ifndef HARDFORK_CORE_BSIP87_TIME\n#define HARDFORK_CORE_BSIP87_TIME (fc::time_point_sec( 1596117300 )) // Thursday, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf",
    "content": "// This hardfork enables the extension to asset_claim_fees_operation to claim collateral-denominated fees.\n// These fees are collected by BSIPs 87 and 74.  This should be set to match the earlier of either\n// HARDFORK_CORE_BSIP87_TIME or HARDFORK_CORE_BSIP74_TIME.\n// This hardfork check should be removable after the hardfork date passes.\n#ifndef HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME\n#define HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME (fc::time_point_sec( 1596117300 )) // Thu, July 30, 2020 13:55:00 UTC\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/LIQUIDITY_POOL.hf",
    "content": "// Liquidity pool\n#ifndef HARDFORK_LIQUIDITY_POOL_TIME\n#define HARDFORK_LIQUIDITY_POOL_TIME (fc::time_point_sec( 1650024300 )) //UTC 2022-04-15 12:05:00 \n#define HARDFORK_LIQUIDITY_POOL_PASSED(now) (now >= HARDFORK_LIQUIDITY_POOL_TIME)\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/core-143.hf",
    "content": "// #143 Require voted entities to exist\n#ifndef HARDFORK_CORE_143_TIME\n#define HARDFORK_CORE_143_TIME (fc::time_point_sec( 1512747600 ))\n#endif\n"
  },
  {
    "path": "libraries/chain/hardfork.d/core-1468.hf",
    "content": "// HTLC implementation\n#ifndef HARDFORK_CORE_1468_TIME\n#define HARDFORK_CORE_1468_TIME (fc::time_point_sec( 1556028120 ) ) // 2019-04-23T14:02:00Z\n#endif\n"
  },
  {
    "path": "libraries/chain/htlc_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2018 jmjatlanta and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/htlc_evaluator.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\nnamespace graphene { \n   namespace chain {\n      namespace detail\n      {\n         void check_htlc_create_hf_bsip64(const fc::time_point_sec& block_time, \n               const htlc_create_operation& op, const asset_object& asset_to_transfer)\n         {\n            if (block_time < HARDFORK_CORE_BSIP64_TIME)\n            {\n               // memo field added at harfork BSIP64\n               // NOTE: both of these checks can be removed after hardfork time\n               FC_ASSERT( !op.extensions.value.memo.valid(), \n                     \"Memo unavailable until after HARDFORK BSIP64\");\n               // HASH160 added at hardfork BSIP64\n               FC_ASSERT( !op.preimage_hash.is_type<fc::hash160>(),\n                     \"HASH160 unavailable until after HARDFORK BSIP64\" );   \n            }\n            else\n            {\n               // this can be moved to the normal non-hf checks after HF_BSIP64\n               //  IF there were no restricted transfers before HF_BSIP64\n               FC_ASSERT( !asset_to_transfer.is_transfer_restricted()\n                     || op.from == asset_to_transfer.issuer || op.to == asset_to_transfer.issuer,\n                     \"Asset ${asset} cannot be transfered.\", (\"asset\", asset_to_transfer.id) );   \n            }\n         }\n\n         void check_htlc_redeem_hf_bsip64(const fc::time_point_sec& block_time, \n               const htlc_redeem_operation& op, const htlc_object* htlc_obj)\n         {\n            // TODO: The hardfork portion of this check can be removed if no HTLC redemptions are \n            // attempted on an HTLC with a 0 preimage size before the hardfork date.\n            if ( htlc_obj->conditions.hash_lock.preimage_size > 0U || \n                  block_time < HARDFORK_CORE_BSIP64_TIME )\n               FC_ASSERT(op.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, \n                     \"Preimage size mismatch.\");\n         }\n      } // end of graphene::chain::details\n\n      optional<htlc_options> get_committee_htlc_options(graphene::chain::database& db)\n      {\n         return db.get_global_properties().parameters.extensions.value.updatable_htlc_options;\n      }\n\n      void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o)\n      {\n         graphene::chain::database& d = db();\n         optional<htlc_options> htlc_options = get_committee_htlc_options(db());\n\n         FC_ASSERT(htlc_options, \"HTLC Committee options are not set.\");\n\n         // make sure the expiration is reasonable\n         FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, \n               \"HTLC Timeout exceeds allowed length\" );\n         // make sure the preimage length is reasonable\n         FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, \n               \"HTLC preimage length exceeds allowed length\" );\n         // make sure the sender has the funds for the HTLC\n         FC_ASSERT( d.get_balance( o.from, o.amount.asset_id) >= (o.amount), \"Insufficient funds\") ;\n         const auto& asset_to_transfer = o.amount.asset_id( d );\n         const auto& from_account = o.from( d );\n         const auto& to_account = o.to( d );\n         detail::check_htlc_create_hf_bsip64(d.head_block_time(), o, asset_to_transfer);\n         FC_ASSERT( is_authorized_asset( d, from_account, asset_to_transfer ), \n               \"Asset ${asset} is not authorized for account ${acct}.\", \n               ( \"asset\", asset_to_transfer.id )( \"acct\", from_account.id ) );\n         FC_ASSERT( is_authorized_asset( d, to_account, asset_to_transfer ), \n               \"Asset ${asset} is not authorized for account ${acct}.\", \n               ( \"asset\", asset_to_transfer.id )( \"acct\", to_account.id ) );\n         return void_result();\n      }\n\n      object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o)\n      {\n         try {\n            graphene::chain::database& dbase = db();\n            dbase.adjust_balance( o.from, -o.amount );\n\n            const htlc_object& esc = db().create<htlc_object>([&dbase,&o]( htlc_object& esc ) {\n               esc.transfer.from                  = o.from;\n               esc.transfer.to                    = o.to;\n               esc.transfer.amount                = o.amount.amount;\n               esc.transfer.asset_id              = o.amount.asset_id;\n               esc.conditions.hash_lock.preimage_hash = o.preimage_hash;\n               esc.conditions.hash_lock.preimage_size = o.preimage_size;\n               if ( o.extensions.value.memo.valid() )\n                  esc.memo = o.extensions.value.memo;\n               esc.conditions.time_lock.expiration    = dbase.head_block_time() + o.claim_period_seconds;\n            });\n            return  esc.id;\n\n         } FC_CAPTURE_AND_RETHROW( (o) )\n      }\n\n      class htlc_redeem_visitor\n      {\n      //private:\n         const std::vector<char>& data;\n      public:\n         typedef bool result_type;\n\n         htlc_redeem_visitor( const std::vector<char>& preimage )\n            : data( preimage ) {}\n\n         template<typename T>\n         bool operator()( const T& preimage_hash )const\n         {\n            return T::hash( (const char*)data.data(), (uint32_t) data.size() ) == preimage_hash;\n         }\n      };\n\n      void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o)\n      {\n         auto& d = db();\n         htlc_obj = &d.get<htlc_object>(o.htlc_id);\n         detail::check_htlc_redeem_hf_bsip64(d.head_block_time(), o, htlc_obj);\n\n         const htlc_redeem_visitor vtor( o.preimage );\n         FC_ASSERT( htlc_obj->conditions.hash_lock.preimage_hash.visit( vtor ), \n               \"Provided preimage does not generate correct hash.\");\n\n         return void_result();\n      }\n\n      void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o)\n      {\n         const auto amount = asset(htlc_obj->transfer.amount, htlc_obj->transfer.asset_id);\n         db().adjust_balance(htlc_obj->transfer.to, amount);\n         // notify related parties\n         htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->transfer.from, htlc_obj->transfer.to, o.redeemer,\n               amount, htlc_obj->conditions.hash_lock.preimage_hash, htlc_obj->conditions.hash_lock.preimage_size,\n               o.preimage );\n         db().push_applied_operation( virt_op );\n         db().remove(*htlc_obj);\n         return void_result();\n      }\n\n      void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o)\n      {\n         htlc_obj = &db().get<htlc_object>(o.htlc_id);\n         FC_ASSERT(o.update_issuer == htlc_obj->transfer.from, \"HTLC may only be extended by its creator.\");\n         optional<htlc_options> htlc_options = get_committee_htlc_options(db());\n         FC_ASSERT( htlc_obj->conditions.time_lock.expiration.sec_since_epoch() \n               + static_cast<uint64_t>(o.seconds_to_add) < fc::time_point_sec::maximum().sec_since_epoch(), \n               \"Extension would cause an invalid date\");\n         FC_ASSERT( htlc_obj->conditions.time_lock.expiration + o.seconds_to_add\n                <=  db().head_block_time() + htlc_options->max_timeout_secs, \n                \"Extension pushes contract too far into the future\" );\n         return void_result();\n      }\n\n      void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o)\n      {\n         db().modify(*htlc_obj, [&o](htlc_object& db_obj) {\n            db_obj.conditions.time_lock.expiration += o.seconds_to_add;\n         });\n\n         return void_result();\n      }\n\n   } // namespace chain\n} // namespace graphene\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/account_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/account_object.hpp>\n\nnamespace graphene { namespace chain {\n\nclass account_create_evaluator : public evaluator<account_create_evaluator>\n{\npublic:\n   typedef account_create_operation operation_type;\n\n   void_result do_evaluate( const account_create_operation& o );\n   object_id_type do_apply( const account_create_operation& o ) ;\n};\n\nclass account_update_evaluator : public evaluator<account_update_evaluator>\n{\npublic:\n   typedef account_update_operation operation_type;\n\n   void_result do_evaluate( const account_update_operation& o );\n   void_result do_apply( const account_update_operation& o );\n\n   const account_object* acnt;\n};\n\nclass account_upgrade_evaluator : public evaluator<account_upgrade_evaluator>\n{\npublic:\n   typedef account_upgrade_operation operation_type;\n\n   void_result do_evaluate(const operation_type& o);\n   void_result do_apply(const operation_type& o);\n\n   const account_object* account;\n};\n\nclass account_whitelist_evaluator : public evaluator<account_whitelist_evaluator>\n{\npublic:\n   typedef account_whitelist_operation operation_type;\n\n   void_result do_evaluate( const account_whitelist_operation& o);\n   void_result do_apply( const account_whitelist_operation& o);\n\n   const account_object* listed_account;\n};\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/account_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n#include <graphene/protocol/account.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n   class database;\n   class account_object;\n   class vesting_balance_object;\n\n   /**\n    * @class account_statistics_object\n    * @ingroup object\n    * @ingroup implementation\n    *\n    * This object contains regularly updated statistical data about an account. It is provided for the purpose of\n    * separating the account data that changes frequently from the account data that is mostly static, which will\n    * minimize the amount of data that must be backed up as part of the undo history everytime a transfer is made.\n    */\n   class account_statistics_object : public graphene::db::abstract_object<account_statistics_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_account_statistics_object_type;\n\n         account_id_type  owner;\n\n         string           name; ///< redundantly store account name here for better maintenance performance\n\n         /**\n          * Keep the most recent operation as a root pointer to a linked list of the transaction history.\n          */\n         account_transaction_history_id_type most_recent_op;\n         /** Total operations related to this account. */\n         uint64_t                            total_ops = 0;\n         /** Total operations related to this account that has been removed from the database. */\n         uint64_t                            removed_ops = 0;\n\n         /**\n          * When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for\n          * transfers). Rather than maintaining an index of [asset,owner,order_id] we will simply maintain the running\n          * total here and update it every time an order is created or modified.\n          */\n         share_type total_core_in_orders;\n\n         /// Total amount of core token in inactive lock_forever tickets\n         share_type total_core_inactive;\n\n         /// Total amount of core token in active lock_forever tickets\n         share_type total_core_pob;\n\n         /// Total amount of core token in other tickets\n         share_type total_core_pol;\n\n         /// Total value of tickets whose current type is lock_forever\n         share_type total_pob_value;\n\n         /// Total value of tickets whose current type is not lock_forever\n         share_type total_pol_value;\n\n         /// Redundantly store core balance in this object for better maintenance performance.\n         /// Only updates on maintenance.\n         share_type core_in_balance;\n\n         bool has_cashback_vb = false; ///< redundantly store this for better maintenance performance\n\n         bool is_voting = false; ///< redundately store \"if this account is voting\" for better maintenance performance\n\n         time_point_sec last_vote_time; ///< last time voted\n\n         /// Whether this account owns some CORE asset and is voting\n         inline bool has_some_core_voting() const\n         {\n            return is_voting && ( total_core_in_orders > 0 || core_in_balance > 0 || has_cashback_vb\n                                  || total_core_pol > 0 );\n         }\n\n         /**\n          * Tracks the total fees paid by this account for the purpose of calculating bulk discounts.\n          */\n         share_type lifetime_fees_paid;\n\n         /**\n          * Tracks the fees paid by this account which have not been disseminated to the various parties that receive\n          * them yet (registrar, referrer, lifetime referrer, network, etc). This is used as an optimization to avoid\n          * doing massive amounts of uint128 arithmetic on each and every operation.\n          *\n          * These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance\n          * interval.\n          */\n         share_type pending_fees;\n         /**\n          * Same as @ref pending_fees, except these fees will be paid out as pre-vested cash-back (immediately\n          * available for withdrawal) rather than requiring the normal vesting period.\n          */\n         share_type pending_vested_fees;\n\n         /// Whether this account has pending fees, no matter vested or not\n         inline bool has_pending_fees() const { return pending_fees > 0 || pending_vested_fees > 0; }\n\n         /// Whether need to process this account during the maintenance interval\n         inline bool need_maintenance() const { return has_some_core_voting() || has_pending_fees(); }\n\n         /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees\n         void process_fees(const account_object& a, database& d) const;\n\n         /**\n          * Core fees are paid into the account_statistics_object by this method\n          */\n         void pay_fee( share_type core_fee, share_type cashback_vesting_threshold );\n   };\n\n   /**\n    * @brief Tracks the balance of a single account/asset pair\n    * @ingroup object\n    *\n    * This object is indexed on owner and asset_type so that black swan\n    * events in asset_type can be processed quickly.\n    */\n   class account_balance_object : public abstract_object<account_balance_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_account_balance_object_type;\n\n         account_id_type   owner;\n         asset_id_type     asset_type;\n         share_type        balance;\n         bool              maintenance_flag = false; ///< Whether need to process this balance object in maintenance interval\n\n         asset get_balance()const { return asset(balance, asset_type); }\n         void  adjust_balance(const asset& delta);\n   };\n\n\n   /**\n    * @brief This class represents an account on the object graph\n    * @ingroup object\n    * @ingroup protocol\n    *\n    * Accounts are the primary unit of authority on the graphene system. Users must have an account in order to use\n    * assets, trade in the markets, vote for committee_members, etc.\n    */\n   class account_object : public graphene::db::abstract_object<account_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id  = account_object_type;\n\n         /**\n          * The time at which this account's membership expires.\n          * If set to any time in the past, the account is a basic account.\n          * If set to time_point_sec::maximum(), the account is a lifetime member.\n          * If set to any time not in the past less than time_point_sec::maximum(), the account is an annual member.\n          *\n          * See @ref is_lifetime_member, @ref is_basic_account, @ref is_annual_member, and @ref is_member\n          */\n         time_point_sec membership_expiration_date;\n\n         ///The account that paid the fee to register this account. Receives a percentage of referral rewards.\n         account_id_type registrar;\n         /// The account credited as referring this account. Receives a percentage of referral rewards.\n         account_id_type referrer;\n         /// The lifetime member at the top of the referral tree. Receives a percentage of referral rewards.\n         account_id_type lifetime_referrer;\n\n         /// Percentage of fee which should go to network.\n         uint16_t network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;\n         /// Percentage of fee which should go to lifetime referrer.\n         uint16_t lifetime_referrer_fee_percentage = 0;\n         /// Percentage of referral rewards (leftover fee after paying network and lifetime referrer) which should go\n         /// to referrer. The remainder of referral rewards goes to the registrar.\n         uint16_t referrer_rewards_percentage = 0;\n\n         /// The account's name. This name must be unique among all account names on the graph. May not be empty.\n         string name;\n\n         /**\n          * The owner authority represents absolute control over the account. Usually the keys in this authority will\n          * be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes\n          * complete and irrevocable loss of the account. Generally the only time the owner authority is required is to\n          * update the active authority.\n          */\n         authority owner;\n         /// The owner authority contains the hot keys of the account. This authority has control over nearly all\n         /// operations the account may perform.\n         authority active;\n\n         typedef account_options  options_type;\n         account_options options;\n\n         /// Pre-calculated for better performance on chain maintenance\n         uint16_t num_committee_voted;\n\n         /// The reference implementation records the account's statistics in a separate object. This field contains the\n         /// ID of that object.\n         account_statistics_id_type statistics;\n\n         /**\n          * This is a set of all accounts which have 'whitelisted' this account. Whitelisting is only used in core\n          * validation for the purpose of authorizing accounts to hold and transact in whitelisted assets. This\n          * account cannot update this set, except by transferring ownership of the account, which will clear it. Other\n          * accounts may add or remove their IDs from this set.\n          */\n         flat_set<account_id_type> whitelisting_accounts;\n\n         /**\n          * Optionally track all of the accounts this account has whitelisted or blacklisted, these should\n          * be made Immutable so that when the account object is cloned no deep copy is required.  This state is\n          * tracked for GUI display purposes.\n          *\n          * TODO: move white list tracking to its own multi-index container rather than having 4 fields on an\n          * account.   This will scale better because under the current design if you whitelist 2000 accounts,\n          * then every time someone fetches this account object they will get the full list of 2000 accounts.\n          */\n         ///@{\n         set<account_id_type> whitelisted_accounts;\n         set<account_id_type> blacklisted_accounts;\n         ///@}\n\n\n         /**\n          * This is a set of all accounts which have 'blacklisted' this account. Blacklisting is only used in core\n          * validation for the purpose of forbidding accounts from holding and transacting in whitelisted assets. This\n          * account cannot update this set, and it will be preserved even if the account is transferred. Other accounts\n          * may add or remove their IDs from this set.\n          */\n         flat_set<account_id_type> blacklisting_accounts;\n\n         /**\n          * Vesting balance which receives cashback_reward deposits.\n          */\n         optional<vesting_balance_id_type> cashback_vb;\n\n         special_authority owner_special_authority = no_special_authority();\n         special_authority active_special_authority = no_special_authority();\n\n         /**\n          * This flag is set when the top_n logic sets both authorities,\n          * and gets reset when authority or special_authority is set.\n          */\n         uint8_t top_n_control_flags = 0;\n         static const uint8_t top_n_control_owner  = 1;\n         static const uint8_t top_n_control_active = 2;\n\n         /**\n          * This is a set of assets which the account is allowed to have.\n          * This is utilized to restrict buyback accounts to the assets that trade in their markets.\n          * In the future we may expand this to allow accounts to e.g. voluntarily restrict incoming transfers.\n          */\n         optional< flat_set<asset_id_type> > allowed_assets;\n\n         bool has_special_authority()const\n         {\n            return (!owner_special_authority.is_type< no_special_authority >())\n                || (!active_special_authority.is_type< no_special_authority >());\n         }\n\n         template<typename DB>\n         const vesting_balance_object& cashback_balance(const DB& db)const\n         {\n            FC_ASSERT(cashback_vb);\n            return db.get(*cashback_vb);\n         }\n\n         /// @return true if this is a lifetime member account; false otherwise.\n         bool is_lifetime_member()const\n         {\n            return membership_expiration_date == time_point_sec::maximum();\n         }\n         /// @return true if this is a basic account; false otherwise.\n         bool is_basic_account(time_point_sec now)const\n         {\n            return now > membership_expiration_date;\n         }\n         /// @return true if the account is an unexpired annual member; false otherwise.\n         /// @note This method will return false for lifetime members.\n         bool is_annual_member(time_point_sec now)const\n         {\n            return !is_lifetime_member() && !is_basic_account(now);\n         }\n         /// @return true if the account is an annual or lifetime member; false otherwise.\n         bool is_member(time_point_sec now)const\n         {\n            return !is_basic_account(now);\n         }\n\n         account_id_type get_id()const { return id; }\n   };\n\n   /**\n    *  @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account\n    *  is an potential signing authority.\n    */\n   class account_member_index : public secondary_index\n   {\n      public:\n         virtual void object_inserted( const object& obj ) override;\n         virtual void object_removed( const object& obj ) override;\n         virtual void about_to_modify( const object& before ) override;\n         virtual void object_modified( const object& after  ) override;\n\n\n         /** given an account or key, map it to the set of accounts that reference it in an active or owner authority */\n         map< account_id_type, set<account_id_type> >                    account_to_account_memberships;\n         map< public_key_type, set<account_id_type>, pubkey_comparator > account_to_key_memberships;\n         /** some accounts use address authorities in the genesis block */\n         map< address, set<account_id_type> >                            account_to_address_memberships;\n\n\n      protected:\n         set<account_id_type>                    get_account_members( const account_object& a )const;\n         set<public_key_type, pubkey_comparator> get_key_members( const account_object& a )const;\n         set<address>                            get_address_members( const account_object& a )const;\n\n         set<account_id_type>                    before_account_members;\n         set<public_key_type, pubkey_comparator> before_key_members;\n         set<address>                            before_address_members;\n   };\n\n\n   /**\n    *  @brief This secondary index will allow fast access to the balance objects\n    *         that belonging to an account.\n    */\n   class balances_by_account_index : public secondary_index\n   {\n      public:\n         virtual void object_inserted( const object& obj ) override;\n         virtual void object_removed( const object& obj ) override;\n         virtual void about_to_modify( const object& before ) override;\n         virtual void object_modified( const object& after  ) override;\n\n         const map< asset_id_type, const account_balance_object* >& get_account_balances( const account_id_type& acct )const;\n         const account_balance_object* get_account_balance( const account_id_type& acct, const asset_id_type& asset )const;\n\n      private:\n         static const uint8_t  bits;\n         static const uint64_t mask;\n\n         /** Maps each account to its balance objects */\n         vector< vector< map< asset_id_type, const account_balance_object* > > > balances;\n         std::stack< object_id_type > ids_being_modified;\n   };\n\n   struct by_asset_balance;\n   struct by_maintenance_flag;\n   /**\n    * @ingroup object_index\n    */\n   typedef multi_index_container<\n      account_balance_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_non_unique< tag<by_maintenance_flag>,\n                             member< account_balance_object, bool, &account_balance_object::maintenance_flag > >,\n         ordered_unique< tag<by_asset_balance>,\n            composite_key<\n               account_balance_object,\n               member<account_balance_object, asset_id_type, &account_balance_object::asset_type>,\n               member<account_balance_object, share_type, &account_balance_object::balance>,\n               member<account_balance_object, account_id_type, &account_balance_object::owner>\n            >,\n            composite_key_compare<\n               std::less< asset_id_type >,\n               std::greater< share_type >,\n               std::less< account_id_type >\n            >\n         >\n      >\n   > account_balance_object_multi_index_type;\n\n   /**\n    * @ingroup object_index\n    */\n   typedef generic_index<account_balance_object, account_balance_object_multi_index_type> account_balance_index;\n\n   struct by_name;\n\n   /**\n    * @ingroup object_index\n    */\n   typedef multi_index_container<\n      account_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_unique< tag<by_name>, member<account_object, string, &account_object::name> >\n      >\n   > account_multi_index_type;\n\n   /**\n    * @ingroup object_index\n    */\n   typedef generic_index<account_object, account_multi_index_type> account_index;\n\n   struct by_maintenance_seq;\n\n   /**\n    * @ingroup object_index\n    */\n   typedef multi_index_container<\n      account_statistics_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_unique< tag<by_maintenance_seq>,\n            composite_key<\n               account_statistics_object,\n               const_mem_fun<account_statistics_object, bool, &account_statistics_object::need_maintenance>,\n               member<account_statistics_object, string, &account_statistics_object::name>\n            >\n         >\n      >\n   > account_stats_multi_index_type;\n\n   /**\n    * @ingroup object_index\n    */\n   typedef generic_index<account_statistics_object, account_stats_multi_index_type> account_stats_index;\n\n}}\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::account_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::account_balance_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::account_statistics_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::account_object )\nFC_REFLECT_TYPENAME( graphene::chain::account_balance_object )\nFC_REFLECT_TYPENAME( graphene::chain::account_statistics_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::account_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::account_balance_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::account_statistics_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/assert_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace chain {\n\n   class assert_evaluator : public evaluator<assert_evaluator>\n   {\n      public:\n         typedef assert_operation operation_type;\n\n         void_result do_evaluate( const assert_operation& o );\n         void_result do_apply( const assert_operation& o );\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/asset_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n#include <locale>\n\nnamespace graphene { namespace chain {\n\n   class asset_create_evaluator : public evaluator<asset_create_evaluator>\n   {\n      public:\n         typedef asset_create_operation operation_type;\n\n         void_result do_evaluate( const asset_create_operation& o );\n         object_id_type do_apply( const asset_create_operation& o );\n\n         /** override the default behavior defined by generic_evalautor which is to\n          * post the fee to fee_paying_account_stats.pending_fees\n          */\n         virtual void pay_fee() override;\n      private:\n         bool fee_is_odd;\n   };\n\n   class asset_issue_evaluator : public evaluator<asset_issue_evaluator>\n   {\n      public:\n         typedef asset_issue_operation operation_type;\n         void_result do_evaluate( const asset_issue_operation& o );\n         void_result do_apply( const asset_issue_operation& o );\n\n         const asset_dynamic_data_object* asset_dyn_data = nullptr;\n         const account_object*            to_account = nullptr;\n   };\n\n   class asset_reserve_evaluator : public evaluator<asset_reserve_evaluator>\n   {\n      public:\n         typedef asset_reserve_operation operation_type;\n         void_result do_evaluate( const asset_reserve_operation& o );\n         void_result do_apply( const asset_reserve_operation& o );\n\n         const asset_dynamic_data_object* asset_dyn_data = nullptr;\n         const account_object*            from_account = nullptr;\n   };\n\n\n   class asset_update_evaluator : public evaluator<asset_update_evaluator>\n   {\n      public:\n         typedef asset_update_operation operation_type;\n\n         void_result do_evaluate( const asset_update_operation& o );\n         void_result do_apply( const asset_update_operation& o );\n\n         const asset_object* asset_to_update = nullptr;\n         const asset_bitasset_data_object* bitasset_data = nullptr;\n   };\n\n   class asset_update_issuer_evaluator : public evaluator<asset_update_issuer_evaluator>\n   {\n      public:\n         typedef asset_update_issuer_operation operation_type;\n\n         void_result do_evaluate( const asset_update_issuer_operation& o );\n         void_result do_apply( const asset_update_issuer_operation& o );\n\n         const asset_object* asset_to_update = nullptr;\n   };\n\n   class asset_update_bitasset_evaluator : public evaluator<asset_update_bitasset_evaluator>\n   {\n      public:\n         typedef asset_update_bitasset_operation operation_type;\n\n         void_result do_evaluate( const asset_update_bitasset_operation& o );\n         void_result do_apply( const asset_update_bitasset_operation& o );\n\n         const asset_bitasset_data_object* bitasset_to_update = nullptr;\n         const asset_object* asset_to_update = nullptr;\n   };\n\n   class asset_update_feed_producers_evaluator : public evaluator<asset_update_feed_producers_evaluator>\n   {\n      public:\n         typedef asset_update_feed_producers_operation operation_type;\n\n         void_result do_evaluate( const operation_type& o );\n         void_result do_apply( const operation_type& o );\n\n         const asset_object* asset_to_update = nullptr;\n   };\n\n   class asset_fund_fee_pool_evaluator : public evaluator<asset_fund_fee_pool_evaluator>\n   {\n      public:\n         typedef asset_fund_fee_pool_operation operation_type;\n\n         void_result do_evaluate(const asset_fund_fee_pool_operation& op);\n         void_result do_apply(const asset_fund_fee_pool_operation& op);\n\n         const asset_dynamic_data_object* asset_dyn_data = nullptr;\n   };\n\n   class asset_global_settle_evaluator : public evaluator<asset_global_settle_evaluator>\n   {\n      public:\n         typedef asset_global_settle_operation operation_type;\n\n         void_result do_evaluate(const operation_type& op);\n         void_result do_apply(const operation_type& op);\n\n         const asset_object* asset_to_settle = nullptr;\n   };\n   class asset_settle_evaluator : public evaluator<asset_settle_evaluator>\n   {\n      public:\n         typedef asset_settle_operation operation_type;\n\n         void_result do_evaluate(const operation_type& op);\n         operation_result do_apply(const operation_type& op);\n\n         const asset_object* asset_to_settle = nullptr;\n   };\n\n   class asset_publish_feeds_evaluator : public evaluator<asset_publish_feeds_evaluator>\n   {\n      public:\n         typedef asset_publish_feed_operation operation_type;\n\n         void_result do_evaluate( const asset_publish_feed_operation& o );\n         void_result do_apply( const asset_publish_feed_operation& o );\n\n         const asset_object* asset_ptr = nullptr;\n         const asset_bitasset_data_object* bitasset_ptr = nullptr;\n   };\n\n   class asset_claim_fees_evaluator : public evaluator<asset_claim_fees_evaluator>\n   {\n      public:\n         typedef asset_claim_fees_operation operation_type;\n\n         void_result do_evaluate( const asset_claim_fees_operation& o );\n         void_result do_apply( const asset_claim_fees_operation& o );\n\n         const asset_object* container_asset = nullptr;\n         const asset_dynamic_data_object* container_ddo = nullptr;\n   };\n\n   class asset_claim_pool_evaluator : public evaluator<asset_claim_pool_evaluator>\n   {\n      public:\n         typedef asset_claim_pool_operation operation_type;\n\n         void_result do_evaluate( const asset_claim_pool_operation& o );\n         void_result do_apply( const asset_claim_pool_operation& o );\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/asset_object.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n#include <graphene/protocol/asset_ops.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\n/**\n * @defgroup prediction_market Prediction Market\n *\n * A prediction market is a specialized BitAsset such that total debt and total collateral are always equal amounts\n * (although asset IDs differ). No margin calls or force settlements may be performed on a prediction market asset. A\n * prediction market is globally settled by the issuer after the event being predicted resolves, thus a prediction\n * market must always have the @ref global_settle permission enabled. The maximum price for global settlement or short\n * sale of a prediction market asset is 1-to-1.\n */\n\nnamespace graphene { namespace chain {\n   class asset_bitasset_data_object;\n   class database;\n   using namespace graphene::db;\n\n   /**\n    *  @brief tracks the asset information that changes frequently\n    *  @ingroup object\n    *  @ingroup implementation\n    *\n    *  Because the asset_object is very large it doesn't make sense to save an undo state\n    *  for all of the parameters that never change.   This object factors out the parameters\n    *  of an asset that change in almost every transaction that involves the asset.\n    *\n    *  This object exists as an implementation detail and its ID should never be referenced by\n    *  a blockchain operation.\n    */\n   class asset_dynamic_data_object : public abstract_object<asset_dynamic_data_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_asset_dynamic_data_object_type;\n\n         /// The number of shares currently in existence\n         share_type current_supply;\n         share_type confidential_supply; ///< total asset held in confidential balances\n         share_type accumulated_fees; ///< fees accumulate to be paid out over time\n         share_type accumulated_collateral_fees; ///< accumulated collateral-denominated fees (for bitassets)\n         share_type fee_pool;         ///< in core asset\n   };\n\n   /**\n    *  @brief tracks the parameters of an asset\n    *  @ingroup object\n    *\n    *  All assets have a globally unique symbol name that controls how they are traded and an issuer who\n    *  has authority over the parameters of the asset.\n    */\n   class asset_object : public graphene::db::abstract_object<asset_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id  = asset_object_type;\n\n         /// This function does not check if any registered asset has this symbol or not; it simply checks whether the\n         /// symbol would be valid.\n         /// @return true if symbol is a valid ticker symbol; false otherwise.\n         static bool is_valid_symbol( const string& symbol );\n\n         /// @return true if this is a market-issued asset; false otherwise.\n         bool is_market_issued()const { return bitasset_data_id.valid(); }\n         /// @return true if this is a share asset of a liquidity pool; false otherwise.\n         bool is_liquidity_pool_share_asset()const { return for_liquidity_pool.valid(); }\n         /// @return true if users may request force-settlement of this market-issued asset; false otherwise\n         bool can_force_settle()const { return !(options.flags & disable_force_settle); }\n         /// @return true if the issuer of this market-issued asset may globally settle the asset; false otherwise\n         bool can_global_settle()const { return options.issuer_permissions & global_settle; }\n         /// @return true if this asset charges a fee for the issuer on market operations; false otherwise\n         bool charges_market_fees()const { return options.flags & charge_market_fee; }\n         /// @return true if this asset may only be transferred to/from the issuer or market orders\n         bool is_transfer_restricted()const { return options.flags & transfer_restricted; }\n         bool can_override()const { return options.flags & override_authority; }\n         bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); }\n         /// @return true if max supply of the asset can be updated\n         bool can_update_max_supply()const { return !(options.flags & lock_max_supply); }\n         /// @return true if can create new supply for the asset\n         bool can_create_new_supply()const { return !(options.flags & disable_new_supply); }\n         /// @return true if the asset owner can update MCR directly\n         bool can_owner_update_mcr()const { return !(options.issuer_permissions & disable_mcr_update); }\n         /// @return true if the asset owner can update ICR directly\n         bool can_owner_update_icr()const { return !(options.issuer_permissions & disable_icr_update); }\n         /// @return true if the asset owner can update MSSR directly\n         bool can_owner_update_mssr()const { return !(options.issuer_permissions & disable_mssr_update); }\n\n         /// Helper function to get an asset object with the given amount in this asset's type\n         asset amount(share_type a)const { return asset(a, id); }\n         /// Convert a string amount (i.e. \"123.45\") to an asset object with this asset's type\n         /// The string may have a decimal and/or a negative sign.\n         asset amount_from_string(string amount_string)const;\n         /// Convert an asset to a textual representation, i.e. \"123.45\"\n         string amount_to_string(share_type amount)const;\n         /// Convert an asset to a textual representation, i.e. \"123.45\"\n         string amount_to_string(const asset& amount)const\n         { FC_ASSERT(amount.asset_id == get_id()); return amount_to_string(amount.amount); }\n         /// Convert an asset to a textual representation with symbol, i.e. \"123.45 USD\"\n         string amount_to_pretty_string(share_type amount)const\n         { return amount_to_string(amount) + \" \" + symbol; }\n         /// Convert an asset to a textual representation with symbol, i.e. \"123.45 USD\"\n         string amount_to_pretty_string(const asset &amount)const\n         { FC_ASSERT(amount.asset_id == get_id()); return amount_to_pretty_string(amount.amount); }\n\n         /// Ticker symbol for this asset, i.e. \"USD\"\n         string symbol;\n         /// Maximum number of digits after the decimal point (must be <= 12)\n         uint8_t precision = 0;\n         /// ID of the account which issued this asset.\n         account_id_type issuer;\n\n         asset_options options;\n\n\n         /// Current supply, fee pool, and collected fees are stored in a separate object as they change frequently.\n         asset_dynamic_data_id_type  dynamic_asset_data_id;\n         /// Extra data associated with BitAssets. This field is non-null if and only if is_market_issued() returns true\n         optional<asset_bitasset_data_id_type> bitasset_data_id;\n\n         optional<account_id_type> buyback_account;\n\n         /// The ID of the liquidity pool if the asset is the share asset of a liquidity pool\n         optional<liquidity_pool_id_type> for_liquidity_pool;\n\n         asset_id_type get_id()const { return id; }\n\n         void validate()const\n         {\n            // UIAs may not be prediction markets, have force settlement, or global settlements\n            if( !is_market_issued() )\n            {\n               FC_ASSERT(!(options.flags & disable_force_settle || options.flags & global_settle));\n               FC_ASSERT(!(options.issuer_permissions & disable_force_settle || options.issuer_permissions & global_settle));\n            }\n         }\n\n         template<class DB>\n         const asset_bitasset_data_object& bitasset_data(const DB& db)const\n         {\n            FC_ASSERT( bitasset_data_id.valid(),\n                       \"Asset ${a} (${id}) is not a market issued asset.\",\n                       (\"a\",this->symbol)(\"id\",this->id) );\n            return db.get( *bitasset_data_id );\n         }\n\n         template<class DB>\n         const asset_dynamic_data_object& dynamic_data(const DB& db)const\n         { return db.get(dynamic_asset_data_id); }\n\n         /**\n          *  The total amount of an asset that is reserved for future issuance. \n          */\n         template<class DB>\n         share_type reserved( const DB& db )const\n         { return options.max_supply - dynamic_data(db).current_supply; }\n\n         /// @return true if asset can accumulate fees in the given denomination\n         template<class DB>\n         bool can_accumulate_fee(const DB& db, const asset& fee) const {\n            return (( fee.asset_id == get_id() ) ||\n                    ( is_market_issued() && fee.asset_id == bitasset_data(db).options.short_backing_asset ));\n         }\n\n         /***\n          * @brief receive a fee asset to accrue in dynamic_data object\n          *\n          * Asset owners define various fees (market fees, force-settle fees, etc.) to be\n          * collected for the asset owners. These fees are typically denominated in the asset\n          * itself, but for bitassets some of the fees are denominated in the collateral\n          * asset. This will place the fee in the right container.\n          */\n         template<class DB>\n         void accumulate_fee(DB& db, const asset& fee) const\n         {\n            if (fee.amount == 0) return;\n            FC_ASSERT( fee.amount >= 0, \"Fee amount must be non-negative.\" );\n            const auto& dyn_data = dynamic_asset_data_id(db);\n            if (fee.asset_id == get_id()) { // fee same as asset\n               db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){\n                  obj.accumulated_fees += fee.amount;\n               });\n            } else { // fee different asset; perhaps collateral-denominated fee\n               FC_ASSERT( is_market_issued(),\n                          \"Asset ${a} (${id}) cannot accept fee of asset (${fid}).\",\n                          (\"a\",this->symbol)(\"id\",this->id)(\"fid\",fee.asset_id) );\n               const auto & bad = bitasset_data(db);\n               FC_ASSERT( fee.asset_id == bad.options.short_backing_asset,\n                          \"Asset ${a} (${id}) cannot accept fee of asset (${fid}).\",\n                          (\"a\",this->symbol)(\"id\",this->id)(\"fid\",fee.asset_id) );\n               db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){\n                  obj.accumulated_collateral_fees += fee.amount;\n               });\n            }\n         }\n\n   };\n\n   /**\n    *  @brief defines market parameters for margin positions, extended with an initial_collateral_ratio field\n    */\n   struct price_feed_with_icr : public price_feed\n   {\n      /// After BSIP77, when creating a new debt position or updating an existing position,\n      /// the position will be checked against this parameter.\n      /// Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM\n      uint16_t initial_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;\n\n      price_feed_with_icr()\n      : price_feed(), initial_collateral_ratio( maintenance_collateral_ratio )\n      {}\n\n      price_feed_with_icr( const price_feed& pf, const optional<uint16_t>& icr = {} )\n      : price_feed( pf ), initial_collateral_ratio( icr.valid() ? *icr : pf.maintenance_collateral_ratio )\n      {}\n\n      /// The result will be used to check new debt positions and position updates.\n      /// Calculation: ~settlement_price * initial_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM\n      price calculate_initial_collateralization()const;\n   };\n\n   /**\n    *  @brief contains properties that only apply to bitassets (market issued assets)\n    *\n    *  @ingroup object\n    *  @ingroup implementation\n    */\n   class asset_bitasset_data_object : public abstract_object<asset_bitasset_data_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_asset_bitasset_data_object_type;\n\n         /// The asset this object belong to\n         asset_id_type asset_id;\n\n         /// The tunable options for BitAssets are stored in this field.\n         bitasset_options options;\n\n         /// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing\n         /// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map\n         /// should be treated as an implementation detail. The timestamp on each feed is the time it was published.\n         flat_map<account_id_type, pair<time_point_sec,price_feed_with_icr>> feeds;\n         /// This is the currently active price feed, calculated as the median of values from the currently active\n         /// feeds.\n         price_feed_with_icr current_feed;\n         /// This is the publication time of the oldest feed which was factored into current_feed.\n         time_point_sec current_feed_publication_time;\n\n         /// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin\n         /// call territory.\n         /// This value is derived from @ref current_feed for better performance and should be kept consistent.\n         price current_maintenance_collateralization;\n         /// After BSIP77, when creating a new debt position or updating an existing position, the position\n         /// will be checked against the `initial_collateral_ratio` (ICR) parameter in the bitasset options.\n         /// This value is derived from @ref current_feed (which includes `ICR`) for better performance and\n         /// should be kept consistent.\n         price current_initial_collateralization;\n\n         /// Derive @ref current_maintenance_collateralization and @ref current_initial_collateralization from\n         /// other member variables.\n         void refresh_cache();\n\n         /// True if this asset implements a @ref prediction_market\n         bool is_prediction_market = false;\n\n         /// This is the volume of this asset which has been force-settled this maintanence interval\n         share_type force_settled_volume;\n         /// Calculate the maximum force settlement volume per maintenance interval, given the current share supply\n         share_type max_force_settlement_volume(share_type current_supply)const;\n\n         /** return true if there has been a black swan, false otherwise */\n         bool has_settlement()const { return !settlement_price.is_null(); }\n\n         /**\n          *  In the event of a black swan, the swan price is saved in the settlement price, and all margin positions\n          *  are settled at the same price with the siezed collateral being moved into the settlement fund. From this\n          *  point on no further updates to the asset are permitted (no feeds, etc) and forced settlement occurs\n          *  immediately when requested, using the settlement price and fund.\n          */\n         ///@{\n         /// Price at which force settlements of a black swanned asset will occur\n         price settlement_price;\n         /// Amount of collateral which is available for force settlement\n         share_type settlement_fund;\n         ///@}\n\n         /// Track whether core_exchange_rate in corresponding asset_object has updated\n         bool asset_cer_updated = false;\n\n         /// Track whether core exchange rate in current feed has updated\n         bool feed_cer_updated = false;\n\n         /// Whether need to update core_exchange_rate in asset_object\n         bool need_to_update_cer() const\n         {\n            return ( ( feed_cer_updated || asset_cer_updated ) && !current_feed.core_exchange_rate.is_null() );\n         }\n\n         /// The time when @ref current_feed would expire\n         time_point_sec feed_expiration_time()const\n         {\n            uint32_t current_feed_seconds = current_feed_publication_time.sec_since_epoch();\n            if( std::numeric_limits<uint32_t>::max() - current_feed_seconds <= options.feed_lifetime_sec )\n               return time_point_sec::maximum();\n            else\n               return current_feed_publication_time + options.feed_lifetime_sec;\n         }\n         bool feed_is_expired_before_hardfork_615(time_point_sec current_time)const\n         { return feed_expiration_time() >= current_time; }\n         bool feed_is_expired(time_point_sec current_time)const\n         { return feed_expiration_time() <= current_time; }\n\n         /******\n          * @brief calculate the median feed\n          *\n          * This calculates the median feed from @ref feeds, feed_lifetime_sec\n          * in @ref options, and the given parameters.\n          * It may update the current_feed_publication_time, current_feed and\n          * current_maintenance_collateralization member variables.\n          *\n          * @param current_time the current time to use in the calculations\n          * @param next_maintenance_time the next chain maintenance time\n          */\n         void update_median_feeds(time_point_sec current_time, time_point_sec next_maintenance_time);\n   };\n\n   // key extractor for short backing asset\n   struct bitasset_short_backing_asset_extractor\n   {\n      typedef asset_id_type result_type;\n      result_type operator() (const asset_bitasset_data_object& obj) const\n      {\n         return obj.options.short_backing_asset;\n      }\n   };\n\n   struct by_short_backing_asset;\n   struct by_feed_expiration;\n   struct by_cer_update;\n\n   typedef multi_index_container<\n      asset_bitasset_data_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_non_unique< tag<by_short_backing_asset>, bitasset_short_backing_asset_extractor >,\n         ordered_unique< tag<by_feed_expiration>,\n            composite_key< asset_bitasset_data_object,\n               const_mem_fun< asset_bitasset_data_object, time_point_sec, &asset_bitasset_data_object::feed_expiration_time >,\n               member< asset_bitasset_data_object, asset_id_type, &asset_bitasset_data_object::asset_id >\n            >\n         >,\n         ordered_non_unique< tag<by_cer_update>,\n                             const_mem_fun< asset_bitasset_data_object, bool, &asset_bitasset_data_object::need_to_update_cer >\n         >\n      >\n   > asset_bitasset_data_object_multi_index_type;\n   typedef generic_index<asset_bitasset_data_object, asset_bitasset_data_object_multi_index_type> asset_bitasset_data_index;\n\n   struct by_symbol;\n   struct by_type;\n   struct by_issuer;\n   typedef multi_index_container<\n      asset_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >,\n         ordered_unique< tag<by_type>,\n            composite_key< asset_object,\n                const_mem_fun<asset_object, bool, &asset_object::is_market_issued>,\n                member< object, object_id_type, &object::id >\n            >\n         >,\n         ordered_unique< tag<by_issuer>,\n            composite_key< asset_object,\n                member< asset_object, account_id_type, &asset_object::issuer >,\n                member< object, object_id_type, &object::id >\n            >\n         >\n      >\n   > asset_object_multi_index_type;\n   typedef generic_index<asset_object, asset_object_multi_index_type> asset_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_dynamic_data_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_bitasset_data_object)\n\nFC_REFLECT_DERIVED( graphene::chain::price_feed_with_icr, (graphene::protocol::price_feed),\n                    (initial_collateral_ratio) )\n\nFC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),\n                    (symbol)\n                    (precision)\n                    (issuer)\n                    (options)\n                    (dynamic_asset_data_id)\n                    (bitasset_data_id)\n                    (buyback_account)\n                    (for_liquidity_pool)\n                  )\n\nFC_REFLECT_TYPENAME( graphene::chain::asset_bitasset_data_object )\nFC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::price_feed_with_icr )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/balance_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/transaction.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/exceptions.hpp>\n\nnamespace graphene { namespace chain {\n\nclass balance_claim_evaluator : public evaluator<balance_claim_evaluator>\n{\npublic:\n   typedef balance_claim_operation operation_type;\n\n   const balance_object* balance = nullptr;\n\n   void_result do_evaluate(const balance_claim_operation& op);\n\n   /**\n    * @note the fee is always 0 for this particular operation because once the\n    * balance is claimed it frees up memory and it cannot be used to spam the network\n    */\n   void_result do_apply(const balance_claim_operation& op);\n};\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/balance_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/vesting_balance_object.hpp>\n\nnamespace graphene { namespace chain {\n\n   class balance_object : public abstract_object<balance_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id  = balance_object_type;\n\n         bool is_vesting_balance()const\n         { return vesting_policy.valid(); }\n         asset available(fc::time_point_sec now)const\n         {\n            return is_vesting_balance()? vesting_policy->get_allowed_withdraw({balance, now, {}})\n                                       : balance;\n         }\n\n         address owner;\n         asset   balance;\n         optional<linear_vesting_policy> vesting_policy;\n         time_point_sec last_claim_date;\n         asset_id_type asset_type()const { return balance.asset_id; }\n   };\n\n   struct by_owner;\n\n   /**\n    * @ingroup object_index\n    */\n   using balance_multi_index_type = multi_index_container<\n      balance_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_non_unique< tag<by_owner>, composite_key<\n            balance_object,\n            member<balance_object, address, &balance_object::owner>,\n            const_mem_fun<balance_object, asset_id_type, &balance_object::asset_type>\n         > >\n      >\n   >;\n\n   /**\n    * @ingroup object_index\n    */\n   using balance_index = generic_index<balance_object, balance_multi_index_type>;\n} }\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::balance_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::balance_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::balance_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/block_database.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <fstream>\n#include <graphene/protocol/block.hpp>\n\n#include <fc/filesystem.hpp>\n\nnamespace graphene { namespace chain {\n   struct index_entry;\n   using namespace graphene::protocol;\n\n   class block_database \n   {\n      public:\n         void open( const fc::path& dbdir );\n         bool is_open()const;\n         void flush();\n         void close();\n\n         void store( const block_id_type& id, const signed_block& b );\n         void remove( const block_id_type& id );\n\n         bool                   contains( const block_id_type& id )const;\n         block_id_type          fetch_block_id( uint32_t block_num )const;\n         optional<signed_block> fetch_optional( const block_id_type& id )const;\n         optional<signed_block> fetch_by_number( uint32_t block_num )const;\n         optional<signed_block> last()const;\n         optional<block_id_type> last_id()const;\n         size_t                 blocks_current_position()const;\n         size_t                 total_block_size()const;\n      private:\n         optional<index_entry> last_index_entry()const;\n         fc::path _index_filename;\n         mutable std::fstream _blocks;\n         mutable std::fstream _block_num_to_pos;\n   };\n} }\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/block_summary_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/types.hpp>\n#include <graphene/db/object.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace graphene::db;\n\n   /**\n    *  @brief tracks minimal information about past blocks to implement TaPOS\n    *  @ingroup object\n    *\n    *  When attempting to calculate the validity of a transaction we need to\n    *  lookup a past block and check its block hash and the time it occurred\n    *  so we can calculate whether the current transaction is valid and at\n    *  what time it should expire.\n    */\n   class block_summary_object : public abstract_object<block_summary_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_block_summary_object_type;\n\n         block_id_type      block_id;\n   };\n\n} }\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::block_summary_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::block_summary_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::block_summary_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/budget_record_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\nnamespace graphene { namespace chain {\n\nstruct budget_record\n{\n   uint64_t time_since_last_budget = 0;\n\n   // sources of budget\n   share_type from_initial_reserve = 0;\n   share_type from_accumulated_fees = 0;\n   share_type from_unused_witness_budget = 0;\n\n   // witness budget requested by the committee\n   share_type requested_witness_budget = 0;\n\n   // funds that can be released from reserve at maximum rate\n   share_type total_budget = 0;\n\n   // sinks of budget, should sum up to total_budget\n   share_type witness_budget = 0;\n   share_type worker_budget = 0;\n\n   // unused budget\n   share_type leftover_worker_funds = 0;\n\n   // change in supply due to budget operations\n   share_type supply_delta = 0;\n};\n\nclass budget_record_object : public graphene::db::abstract_object<budget_record_object>\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id = impl_budget_record_object_type;\n\n      fc::time_point_sec time;\n      budget_record record;\n};\n\n} }\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::budget_record_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::budget_record )\nFC_REFLECT_TYPENAME( graphene::chain::budget_record_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::budget_record )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::budget_record_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/buyback.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/buyback.hpp>\n\nnamespace graphene { namespace chain {\n\nclass database;\n\nvoid evaluate_buyback_account_options( const database& db, const buyback_account_options& auth );\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/buyback_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n * buyback_authority_object only exists to help with a specific indexing problem.\n * We want to be able to iterate over all assets that have a buyback program.\n * However, assets which have a buyback program are very rare.  So rather\n * than indexing asset_object by the buyback field (requiring additional\n * bookkeeping for every asset), we instead maintain a buyback_object\n * pointing to each asset which has buyback (requiring additional\n * bookkeeping only for every asset which has buyback).\n *\n * This class is an implementation detail.\n */\n\nclass buyback_object : public graphene::db::abstract_object< buyback_object >\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id = impl_buyback_object_type;\n\n      asset_id_type asset_to_buy;\n};\n\nstruct by_asset;\n\ntypedef multi_index_container<\n   buyback_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_asset>, member< buyback_object, asset_id_type, &buyback_object::asset_to_buy> >\n   >\n> buyback_multi_index_type;\n\ntypedef generic_index< buyback_object, buyback_multi_index_type > buyback_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::buyback_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::buyback_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::buyback_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/chain_property_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/immutable_chain_parameters.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n * Contains invariants which are set at genesis and never changed.\n */\nclass chain_property_object : public abstract_object<chain_property_object>\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id  = impl_chain_property_object_type;\n\n      chain_id_type chain_id;\n      immutable_chain_parameters immutable_parameters;\n};\n\n} }\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::chain_property_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::chain_property_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::chain_property_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/committee_member_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n\nnamespace graphene { namespace chain {\n\n   class committee_member_create_evaluator : public evaluator<committee_member_create_evaluator>\n   {\n      public:\n         typedef committee_member_create_operation operation_type;\n\n         void_result do_evaluate( const committee_member_create_operation& o );\n         object_id_type do_apply( const committee_member_create_operation& o );\n   };\n\n   class committee_member_update_evaluator : public evaluator<committee_member_update_evaluator>\n   {\n      public:\n         typedef committee_member_update_operation operation_type;\n\n         void_result do_evaluate( const committee_member_update_operation& o );\n         void_result do_apply( const committee_member_update_operation& o );\n   };\n\n   class committee_member_update_global_parameters_evaluator : public evaluator<committee_member_update_global_parameters_evaluator>\n   {\n      public:\n         typedef committee_member_update_global_parameters_operation operation_type;\n\n         void_result do_evaluate( const committee_member_update_global_parameters_operation& o );\n         void_result do_apply( const committee_member_update_global_parameters_operation& o );\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/committee_member_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n#include <graphene/protocol/vote.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace graphene::db;\n\n   /**\n    *  @brief tracks information about a committee_member account.\n    *  @ingroup object\n    *\n    *  A committee_member is responsible for setting blockchain parameters and has\n    *  dynamic multi-sig control over the committee account.  The current set of\n    *  active committee_members has control.\n    *\n    *  committee_members were separated into a separate object to make iterating over\n    *  the set of committee_member easy.\n    */\n   class committee_member_object : public abstract_object<committee_member_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id  = committee_member_object_type;\n\n         account_id_type  committee_member_account;\n         vote_id_type     vote_id;\n         uint64_t         total_votes = 0;\n         string           url;\n   };\n\n   struct by_account;\n   struct by_vote_id;\n   using committee_member_multi_index_type = multi_index_container<\n      committee_member_object,\n      indexed_by<\n         ordered_unique< tag<by_id>,\n            member<object, object_id_type, &object::id>\n         >,\n         ordered_unique< tag<by_account>,\n            member<committee_member_object, account_id_type, &committee_member_object::committee_member_account>\n         >,\n         ordered_unique< tag<by_vote_id>,\n            member<committee_member_object, vote_id_type, &committee_member_object::vote_id>\n         >\n      >\n   >;\n   using committee_member_index = generic_index<committee_member_object, committee_member_multi_index_type>;\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::committee_member_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::committee_member_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::committee_member_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/confidential_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/types.hpp>\n\nnamespace graphene { namespace chain {\n\nclass transfer_to_blind_evaluator : public evaluator<transfer_to_blind_evaluator>\n{\n   public:\n      typedef transfer_to_blind_operation operation_type;\n\n      void_result do_evaluate( const transfer_to_blind_operation& o );\n      void_result do_apply( const transfer_to_blind_operation& o ) ;\n\n      virtual void pay_fee() override;\n};\n\nclass transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evaluator>\n{\n   public:\n      typedef transfer_from_blind_operation operation_type;\n\n      void_result do_evaluate( const transfer_from_blind_operation& o );\n      void_result do_apply( const transfer_from_blind_operation& o ) ;\n\n      virtual void pay_fee() override;\n};\n\nclass blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>\n{\n   public:\n      typedef blind_transfer_operation operation_type;\n\n      void_result do_evaluate( const blind_transfer_operation& o );\n      void_result do_apply( const blind_transfer_operation& o ) ;\n\n      virtual void pay_fee() override;\n};\n\n} } // namespace graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/confidential_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/authority.hpp>\n#include <graphene/protocol/types.hpp>\n\n#include <graphene/db/generic_index.hpp>\n\n#include <fc/crypto/elliptic.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n * @class blinded_balance_object\n * @brief tracks a blinded balance commitment\n * @ingroup object\n * @ingroup protocol\n */\nclass blinded_balance_object : public graphene::db::abstract_object<blinded_balance_object>\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id  = impl_blinded_balance_object_type;\n\n      fc::ecc::commitment_type                commitment;\n      asset_id_type                           asset_id;\n      authority                               owner;\n};\n\nstruct by_commitment;\n\n/**\n * @ingroup object_index\n */\ntypedef multi_index_container<\n   blinded_balance_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_commitment>, member<blinded_balance_object, commitment_type, &blinded_balance_object::commitment> >\n   >\n> blinded_balance_object_multi_index_type;\ntypedef generic_index<blinded_balance_object, blinded_balance_object_multi_index_type> blinded_balance_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::blinded_balance_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::blinded_balance_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::blinded_balance_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/config.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/config.hpp>\n\n#define GRAPHENE_MIN_UNDO_HISTORY 10\n#define GRAPHENE_MAX_UNDO_HISTORY 10000\n\n#define GRAPHENE_MAX_NESTED_OBJECTS (200)\n\n#define GRAPHENE_CURRENT_DB_VERSION                          \"20211100\"\n\n#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT             4\n#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT             3\n\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/custom_authority_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include <graphene/chain/evaluator.hpp>\n\nnamespace graphene { namespace chain {\nclass custom_authority_object;\n\nclass custom_authority_create_evaluator : public evaluator<custom_authority_create_evaluator> {\npublic:\n   using operation_type = custom_authority_create_operation;\n\n   void_result do_evaluate(const operation_type& op);\n   object_id_type do_apply(const operation_type& op);\n};\n\nclass custom_authority_update_evaluator : public evaluator<custom_authority_update_evaluator> {\npublic:\n   using operation_type = custom_authority_update_operation;\n   const custom_authority_object* old_object = nullptr;\n\n   void_result do_evaluate(const operation_type& op);\n   void_result do_apply(const operation_type& op);\n};\n\nclass custom_authority_delete_evaluator : public evaluator<custom_authority_delete_evaluator> {\npublic:\n   using operation_type = custom_authority_delete_operation;\n   const custom_authority_object* old_object = nullptr;\n\n   void_result do_evaluate(const operation_type& op);\n   void_result do_apply(const operation_type& op);\n};\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/custom_authority_object.hpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/authority.hpp>\n#include <graphene/protocol/custom_authority.hpp>\n#include <graphene/protocol/restriction_predicate.hpp>\n#include <graphene/db/generic_index.hpp>\n#include <graphene/chain/types.hpp>\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n\n   /**\n    * @brief Tracks account custom authorities\n    * @ingroup object\n    *\n    */\n   class custom_authority_object : public abstract_object<custom_authority_object> {\n      /// Unreflected field to store a cache of the predicate function\n      /// Note that this cache can be modified when the object is const!\n      mutable optional<restriction_predicate_function> predicate_cache;\n\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id = custom_authority_object_type;\n\n      account_id_type account;\n      bool enabled;\n      time_point_sec valid_from;\n      time_point_sec valid_to;\n      unsigned_int operation_type;\n      authority auth;\n      flat_map<uint16_t, restriction> restrictions;\n      uint16_t restriction_counter = 0;\n\n      /// Check whether the custom authority is valid\n      bool is_valid(time_point_sec now) const { return enabled && now >= valid_from && now < valid_to; }\n\n      /// Get the restrictions as a vector rather than a map\n      vector<restriction> get_restrictions() const {\n         vector<restriction> rs;\n         std::transform(restrictions.begin(), restrictions.end(),\n                        std::back_inserter(rs), [](auto i) { return i.second; });\n         return rs;\n      }\n      /// Get predicate, from cache if possible, and update cache if not (modifies const object!)\n      restriction_predicate_function get_predicate() const {\n         if (!predicate_cache.valid())\n            update_predicate_cache();\n\n         return *predicate_cache;\n      }\n      /// Regenerate predicate function and update predicate cache\n      void update_predicate_cache() const {\n         predicate_cache = get_restriction_predicate(get_restrictions(), operation_type);\n      }\n      /// Clear the cache of the predicate function\n      void clear_predicate_cache() { predicate_cache.reset(); }\n   };\n\n   struct by_account_custom;\n   struct by_expiration;\n\n   /**\n    * @ingroup object_index\n    */\n   typedef multi_index_container<\n      custom_authority_object,\n      indexed_by<\n         ordered_unique<tag<by_id>, member<object, object_id_type, &object::id>>,\n         ordered_unique<tag<by_account_custom>,\n            composite_key<custom_authority_object,\n               member<custom_authority_object, account_id_type, &custom_authority_object::account>,\n               member<custom_authority_object, unsigned_int, &custom_authority_object::operation_type>,\n               member<custom_authority_object, bool, &custom_authority_object::enabled>,\n               member<object, object_id_type, &object::id>\n            >>,\n         ordered_unique<tag<by_expiration>,\n            composite_key<custom_authority_object,\n               member<custom_authority_object, time_point_sec, &custom_authority_object::valid_to>,\n               member<object, object_id_type, &object::id>\n            >\n         >\n      >\n   > custom_authority_multi_index_type;\n\n   /**\n    * @ingroup object_index\n    */\n   using custom_authority_index = generic_index<custom_authority_object, custom_authority_multi_index_type>;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::custom_authority_object)\n\nFC_REFLECT_TYPENAME(graphene::chain::custom_authority_object)\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION(graphene::chain::custom_authority_object)\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/custom_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace chain {\n\n   class custom_evaluator : public evaluator<custom_evaluator>\n   {\n      public:\n         typedef custom_operation operation_type;\n\n         void_result do_evaluate( const custom_operation& o ){ return void_result(); }\n         void_result do_apply( const custom_operation& o ){ return void_result(); }\n   };\n} }\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/database.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/node_property_object.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/fork_database.hpp>\n#include <graphene/chain/block_database.hpp>\n#include <graphene/chain/genesis_state.hpp>\n#include <graphene/chain/evaluator.hpp>\n\n#include <graphene/db/object_database.hpp>\n#include <graphene/db/object.hpp>\n#include <graphene/db/simple_index.hpp>\n#include <fc/signals.hpp>\n\n#include <fc/log/logger.hpp>\n\n#include <map>\n\nnamespace graphene { namespace protocol { struct predicate_result; } }\n\nnamespace graphene { namespace chain {\n   using graphene::db::abstract_object;\n   using graphene::db::object;\n   class op_evaluator;\n   class transaction_evaluation_state;\n   class proposal_object;\n   class operation_history_object;\n   class chain_property_object;\n   class witness_schedule_object;\n   class witness_object;\n   class force_settlement_object;\n   class limit_order_object;\n   class collateral_bid_object;\n   class call_order_object;\n\n   struct budget_record;\n   enum class vesting_balance_type;\n\n   /**\n    *   @class database\n    *   @brief tracks the blockchain state in an extensible manner\n    */\n   class database : public db::object_database\n   {\n      public:\n         //////////////////// db_management.cpp ////////////////////\n\n         database();\n         ~database();\n\n         enum validation_steps\n         {\n            skip_nothing                = 0,\n            skip_witness_signature      = 1 << 0,  ///< used while reindexing\n            skip_transaction_signatures = 1 << 1,  ///< used by non-witness nodes\n            skip_transaction_dupe_check = 1 << 2,  ///< used while reindexing\n            skip_block_size_check       = 1 << 4,  ///< used when applying locally generated transactions\n            skip_tapos_check            = 1 << 5,  ///< used while reindexing -- note this skips expiration check as well\n            // skip_authority_check        = 1 << 6,  ///< removed because effectively identical to skip_transaction_signatures\n            skip_merkle_check           = 1 << 7,  ///< used while reindexing\n            skip_assert_evaluation      = 1 << 8,  ///< used while reindexing\n            skip_undo_history_check     = 1 << 9,  ///< used while reindexing\n            skip_witness_schedule_check = 1 << 10 ///< used while reindexing\n         };\n\n         /**\n          * @brief Open a database, creating a new one if necessary\n          *\n          * Opens a database in the specified directory. If no initialized database is found, genesis_loader is called\n          * and its return value is used as the genesis state when initializing the new database\n          *\n          * genesis_loader will not be called if an existing database is found.\n          *\n          * @param data_dir Path to open or create database in\n          * @param genesis_loader A callable object which returns the genesis state to initialize new databases on\n          * @param db_version a version string that changes when the internal database format and/or logic is modified\n          */\n          void open(\n             const fc::path& data_dir,\n             std::function<genesis_state_type()> genesis_loader,\n             const std::string& db_version );\n\n         /**\n          * @brief Rebuild object graph from block history and open detabase\n          *\n          * This method may be called after or instead of @ref database::open, and will rebuild the object graph by\n          * replaying blockchain history. When this method exits successfully, the database will be open.\n          */\n         void reindex(fc::path data_dir);\n\n         /**\n          * @brief wipe Delete database from disk, and potentially the raw chain as well.\n          * @param include_blocks If true, delete the raw chain as well as the database.\n          *\n          * Will close the database before wiping. Database will be closed when this function returns.\n          */\n         void wipe(const fc::path& data_dir, bool include_blocks);\n         void close(bool rewind = true);\n\n         //////////////////// db_block.cpp ////////////////////\n\n         /**\n          *  @return true if the block is in our fork DB or saved to disk as\n          *  part of the official chain, otherwise return false\n          */\n         bool                       is_known_block( const block_id_type& id )const;\n         bool                       is_known_transaction( const transaction_id_type& id )const;\n         block_id_type              get_block_id_for_num( uint32_t block_num )const;\n         optional<signed_block>     fetch_block_by_id( const block_id_type& id )const;\n         optional<signed_block>     fetch_block_by_number( uint32_t num )const;\n         const signed_transaction&  get_recent_transaction( const transaction_id_type& trx_id )const;\n         std::vector<block_id_type> get_block_ids_on_fork(block_id_type head_of_fork) const;\n\n         /**\n          *  Calculate the percent of block production slots that were missed in the\n          *  past 128 blocks, not including the current block.\n          */\n         uint32_t witness_participation_rate()const;\n\n         void                              add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts );\n         const flat_map<uint32_t,block_id_type> get_checkpoints()const { return _checkpoints; }\n         bool before_last_checkpoint()const;\n\n         bool push_block( const signed_block& b, uint32_t skip = skip_nothing );\n         processed_transaction push_transaction( const precomputable_transaction& trx, uint32_t skip = skip_nothing );\n         bool _push_block( const signed_block& b );\n         processed_transaction _push_transaction( const precomputable_transaction& trx );\n\n         ///@throws fc::exception if the proposed transaction fails to apply.\n         processed_transaction push_proposal( const proposal_object& proposal );\n\n         signed_block generate_block(\n            const fc::time_point_sec when,\n            witness_id_type witness_id,\n            const fc::ecc::private_key& block_signing_private_key,\n            uint32_t skip\n            );\n         signed_block _generate_block(\n            const fc::time_point_sec when,\n            witness_id_type witness_id,\n            const fc::ecc::private_key& block_signing_private_key\n            );\n\n         void pop_block();\n         void clear_pending();\n\n         /**\n          *  This method is used to track appied operations during the evaluation of a block, these\n          *  operations should include any operation actually included in a transaction as well\n          *  as any implied/virtual operations that resulted, such as filling an order.  The\n          *  applied operations is cleared after applying each block and calling the block\n          *  observers which may want to index these operations.\n          *\n          *  @return the op_id which can be used to set the result after it has finished being applied.\n          */\n         uint32_t  push_applied_operation( const operation& op );\n         void      set_applied_operation_result( uint32_t op_id, const operation_result& r );\n         const vector<optional< operation_history_object > >& get_applied_operations()const;\n\n         string to_pretty_string( const asset& a )const;\n\n         /**\n          *  This signal is emitted after all operations and virtual operation for a\n          *  block have been applied but before the get_applied_operations() are cleared.\n          *\n          *  You may not yield from this callback because the blockchain is holding\n          *  the write lock and may be in an \"inconstant state\" until after it is\n          *  released.\n          */\n         fc::signal<void(const signed_block&)>           applied_block;\n\n         /**\n          * This signal is emitted any time a new transaction is added to the pending\n          * block state.\n          */\n         fc::signal<void(const signed_transaction&)>     on_pending_transaction;\n\n         /**\n          *  Emitted After a block has been applied and committed.  The callback\n          *  should not yield and should execute quickly.\n          */\n         fc::signal<void(const vector<object_id_type>&, const flat_set<account_id_type>&)> new_objects;\n\n         /**\n          *  Emitted After a block has been applied and committed.  The callback\n          *  should not yield and should execute quickly.\n          */\n         fc::signal<void(const vector<object_id_type>&, const flat_set<account_id_type>&)> changed_objects;\n\n         /** this signal is emitted any time an object is removed and contains a\n          * pointer to the last value of every object that was removed.\n          */\n         fc::signal<void(const vector<object_id_type>&, const vector<const object*>&, const flat_set<account_id_type>&)>  removed_objects;\n\n         //////////////////// db_witness_schedule.cpp ////////////////////\n\n         /**\n          * @brief Get the witness scheduled for block production in a slot.\n          *\n          * slot_num always corresponds to a time in the future.\n          *\n          * If slot_num == 1, returns the next scheduled witness.\n          * If slot_num == 2, returns the next scheduled witness after\n          * 1 block gap.\n          *\n          * Use the get_slot_time() and get_slot_at_time() functions\n          * to convert between slot_num and timestamp.\n          *\n          * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS\n          */\n         witness_id_type get_scheduled_witness(uint32_t slot_num)const;\n\n         /**\n          * Get the time at which the given slot occurs.\n          *\n          * If slot_num == 0, return time_point_sec().\n          *\n          * If slot_num == N for N > 0, return the Nth next\n          * block-interval-aligned time greater than head_block_time().\n          */\n         fc::time_point_sec get_slot_time(uint32_t slot_num)const;\n\n         /**\n          * Get the last slot which occurs AT or BEFORE the given time.\n          *\n          * The return value is the greatest value N such that\n          * get_slot_time( N ) <= when.\n          *\n          * If no such N exists, return 0.\n          */\n         uint32_t get_slot_at_time(fc::time_point_sec when)const;\n\n         void update_witness_schedule();\n\n         //////////////////// db_getter.cpp ////////////////////\n\n         const chain_id_type&                   get_chain_id()const;\n         const asset_object&                    get_core_asset()const;\n         const asset_dynamic_data_object&       get_core_dynamic_data()const;\n         const chain_property_object&           get_chain_properties()const;\n         const global_property_object&          get_global_properties()const;\n         const dynamic_global_property_object&  get_dynamic_global_properties()const;\n         const node_property_object&            get_node_properties()const;\n         const fee_schedule&                    current_fee_schedule()const;\n         const account_statistics_object&       get_account_stats_by_owner( account_id_type owner )const;\n         const witness_schedule_object&         get_witness_schedule_object()const;\n\n         time_point_sec   head_block_time()const;\n         uint32_t         head_block_num()const;\n         block_id_type    head_block_id()const;\n         witness_id_type  head_block_witness()const;\n\n         decltype( chain_parameters::block_interval ) block_interval( )const;\n\n         node_property_object& node_properties();\n\n         /**\n          * @brief Get a list of custom authorities which can validate the provided operation for the provided account\n          * @param account The account whose authority is required\n          * @param op The operation requring the specified account's authority\n          * @param rejected_authorities [Optional] A pointer to a map that should be populated with the custom\n          * authorities which were valid, but rejected because the operation did not comply with the restrictions\n          * @return A vector of authorities which can be used to authorize op in place of account\n          */\n         vector<authority> get_viable_custom_authorities(\n                 account_id_type account, const operation& op,\n                 rejected_predicate_map* rejected_authorities = nullptr )const;\n\n         uint32_t last_non_undoable_block_num() const;\n         //////////////////// db_init.cpp ////////////////////\n\n         void initialize_evaluators();\n         /// Reset the object graph in-memory\n         void initialize_indexes();\n         void init_genesis(const genesis_state_type& genesis_state = genesis_state_type());\n\n         template<typename EvaluatorType>\n         void register_evaluator()\n         {\n            _operation_evaluators[\n               operation::tag<typename EvaluatorType::operation_type>::value].reset( new op_evaluator_impl<EvaluatorType>() );\n         }\n\n         //////////////////// db_balance.cpp ////////////////////\n\n         /**\n          * @brief Retrieve a particular account's balance in a given asset\n          * @param owner Account whose balance should be retrieved\n          * @param asset_id ID of the asset to get balance in\n          * @return owner's balance in asset\n          */\n         asset get_balance(account_id_type owner, asset_id_type asset_id)const;\n         /// This is an overloaded method.\n         asset get_balance(const account_object& owner, const asset_object& asset_obj)const;\n\n         /**\n          * @brief Adjust a particular account's balance in a given asset by a delta\n          * @param account ID of account whose balance should be adjusted\n          * @param delta Asset ID and amount to adjust balance by\n          */\n         void adjust_balance(account_id_type account, asset delta);\n\n         void deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta);\n        /**\n          * @brief Retrieve a particular account's market fee vesting balance in a given asset\n          * @param owner Account whose balance should be retrieved\n          * @param asset_id ID of the asset to get balance in\n          * @return owner's balance in asset\n          */\n         asset get_market_fee_vesting_balance(const account_id_type &account_id, const asset_id_type &asset_id);\n\n         /**\n          * @brief Helper to make lazy deposit to CDD VBO.\n          *\n          * If the given optional VBID is not valid(),\n          * or it does not have a CDD vesting policy,\n          * or the owner / vesting_seconds of the policy\n          * does not match the parameter, then credit amount\n          * to newly created VBID and return it.\n          *\n          * Otherwise, credit amount to ovbid.\n          * \n          * @return ID of newly created VBO, but only if VBO was created.\n          */\n         optional< vesting_balance_id_type > deposit_lazy_vesting(\n            const optional< vesting_balance_id_type >& ovbid,\n            share_type amount,\n            uint32_t req_vesting_seconds,\n            vesting_balance_type balance_type,\n            account_id_type req_owner,\n            bool require_vesting );\n\n         // helper to handle cashback rewards\n         void deposit_cashback(const account_object& acct, share_type amount, bool require_vesting = true);\n         // helper to handle witness pay\n         void deposit_witness_pay(const witness_object& wit, share_type amount);\n\n         //////////////////// db_debug.cpp ////////////////////\n\n         void debug_dump();\n         void apply_debug_updates();\n         void debug_update( const fc::variant_object& update );\n\n         //////////////////// db_market.cpp ////////////////////\n\n         /// @{ @group Market Helpers\n         void globally_settle_asset( const asset_object& bitasset, const price& settle_price );\n         void cancel_settle_order(const force_settlement_object& order, bool create_virtual_op = true);\n         void cancel_limit_order(const limit_order_object& order, bool create_virtual_op = true, bool skip_cancel_fee = false);\n         void revive_bitasset( const asset_object& bitasset );\n         void cancel_bid(const collateral_bid_object& bid, bool create_virtual_op = true);\n         void execute_bid( const collateral_bid_object& bid, share_type debt_covered, share_type collateral_from_fund, const price_feed& current_feed );\n\n      private:\n         template<typename IndexType>\n         void globally_settle_asset_impl( const asset_object& bitasset,\n                                          const price& settle_price,\n                                          const IndexType& call_index );\n\n      public:\n         /**\n          * @brief Process a new limit order through the markets\n          * @param order The new order to process\n          * @return true if order was completely filled; false otherwise\n          *\n          * This function takes a new limit order, and runs the markets attempting to match it with existing orders\n          * already on the books.\n          */\n         ///@{\n         bool apply_order_before_hardfork_625(const limit_order_object& new_order_object, bool allow_black_swan = true);\n         bool apply_order(const limit_order_object& new_order_object, bool allow_black_swan = true);\n         ///@}\n\n         /**\n          * Matches the two orders, the first parameter is taker, the second is maker.\n          *\n          * @return a bit field indicating which orders were filled (and thus removed)\n          *\n          * 0 - no orders were matched\n          * 1 - taker was filled\n          * 2 - maker was filled\n          * 3 - both were filled\n          */\n         ///@{\n         int match( const limit_order_object& taker, const limit_order_object& maker, const price& trade_price );\n         /***\n          * @brief Match limit order as taker to a call order as maker\n          * @param taker the order that is removing liquidity from the book\n          * @param maker the order that put liquidity on the book\n          * @param trade_price the price the trade should execute at\n          * @param feed_price the price of the current feed\n          * @param maintenance_collateral_ratio the maintenance collateral ratio\n          * @param maintenance_collateralization the maintenance collateralization\n          * @param call_pays_price price call order pays. Call order may pay more collateral\n          *    than limit order takes if call order subject to a margin call fee.\n          * @returns 0 if no orders were matched, 1 if taker was filled, 2 if maker was filled, 3 if both were filled\n          */\n         int match( const limit_order_object& taker, const call_order_object& maker, const price& trade_price,\n                    const price& feed_price, const uint16_t maintenance_collateral_ratio,\n                    const optional<price>& maintenance_collateralization,\n                    const price& call_pays_price);\n         // If separate call_pays_price not provided, assume call pays at trade_price:\n         int match( const limit_order_object& taker, const call_order_object& maker, const price& trade_price,\n                    const price& feed_price, const uint16_t maintenance_collateral_ratio,\n                    const optional<price>& maintenance_collateralization) {\n            return match(taker, maker, trade_price, feed_price, maintenance_collateral_ratio,\n                         maintenance_collateralization, trade_price);\n         }\n\n         ///@}\n\n         /// Matches the two orders, the first parameter is taker, the second is maker.\n         /// @return the amount of asset settled\n         asset match(const call_order_object& call,\n                   const force_settlement_object& settle,\n                   const price& match_price,\n                   asset max_settlement,\n                   const price& fill_price);\n\n         /**\n          * @brief fills limit order\n          * @param order the order\n          * @param pays what the account is paying\n          * @param receives what the account is receiving\n          * @param cull_if_small take care of dust\n          * @param fill_price the transaction price\n          * @param is_maker TRUE if this order is maker, FALSE if taker\n          * @return true if the order was completely filled and thus freed.\n          */\n         bool fill_limit_order( const limit_order_object& order, const asset& pays, const asset& receives,\n               bool cull_if_small, const price& fill_price, const bool is_maker );\n         /***\n          * @brief attempt to fill a call order\n          * @param order the order\n          * @param pays what the buyer pays for the collateral\n          * @param receives the collateral received by the buyer\n          * @param fill_price the price the transaction executed at\n          * @param is_maker TRUE if the buyer is the maker, FALSE if the buyer is the taker\n          * @param margin_fee Margin call fees paid in collateral asset\n          * @returns TRUE if the order was completely filled\n          */\n         bool fill_call_order( const call_order_object& order, const asset& pays, const asset& receives,\n                               const price& fill_price, const bool is_maker, const asset& margin_fee );\n\n         // Overload provides compatible default value for margin_fee: (margin_fee.asset_id == pays.asset_id)\n         bool fill_call_order( const call_order_object& order, const asset& pays, const asset& receives,\n                               const price& fill_price, const bool is_maker )\n         {\n            return fill_call_order( order, pays, receives, fill_price, is_maker, asset(0, pays.asset_id) );\n         }\n\n         bool fill_settle_order( const force_settlement_object& settle, const asset& pays, const asset& receives,\n                                 const price& fill_price, const bool is_maker );\n\n         bool check_call_orders( const asset_object& mia, bool enable_black_swan = true, bool for_new_limit_order = false,\n                                 const asset_bitasset_data_object* bitasset_ptr = nullptr );\n\n         // helpers to fill_order\n         void pay_order( const account_object& receiver, const asset& receives, const asset& pays );\n\n         /**\n          * @brief Calculate the market fee that is to be taken\n          * @param trade_asset the asset (passed in to avoid a lookup)\n          * @param trade_amount the quantity that the fee calculation is based upon\n          * @param is_maker TRUE if this is the fee for a maker, FALSE if taker\n          */\n         asset calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount,\n                                     const bool& is_maker )const;\n         asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives,\n                               const bool& is_maker, const optional<asset>& calculated_market_fees = {});\n         asset pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives);\n         ///@}\n\n\n         ///@{\n         /**\n          *  This method validates transactions without adding it to the pending state.\n          *  @return true if the transaction would validate\n          */\n         processed_transaction validate_transaction( const signed_transaction& trx );\n\n\n         /** when popping a block, the transactions that were removed get cached here so they\n          * can be reapplied at the proper time */\n         std::deque< precomputable_transaction > _popped_tx;\n\n         /**\n          * @}\n          */\n\n         /// Enable or disable tracking of votes of standby witnesses and committee members\n         inline void enable_standby_votes_tracking(bool enable)  { _track_standby_votes = enable; }\n\n         /** Precomputes digests, signatures and operation validations depending\n          *  on skip flags. \"Expensive\" computations may be done in a parallel\n          *  thread.\n          *\n          * @param block the block to preprocess\n          * @param skip indicates which computations can be skipped\n          * @return a future that will resolve to the input block with\n          *         precomputations applied\n          */\n         fc::future<void> precompute_parallel( const signed_block& block, const uint32_t skip = skip_nothing )const;\n\n         /** Precomputes digests, signatures and operation validations.\n          *  \"Expensive\" computations may be done in a parallel thread.\n          *\n          * @param trx the transaction to preprocess\n          * @return a future that will resolve to the input transaction with\n          *         precomputations applied\n          */\n         fc::future<void> precompute_parallel( const precomputable_transaction& trx )const;\n   private:\n         template<typename Trx>\n         void _precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const;\n\n   protected:\n         //Mark pop_undo() as protected -- we do not want outside calling pop_undo(); it should call pop_block() instead\n         void pop_undo() { object_database::pop_undo(); }\n         void notify_applied_block( const signed_block& block );\n         void notify_on_pending_transaction( const signed_transaction& tx );\n         void notify_changed_objects();\n\n      private:\n         optional<undo_database::session>       _pending_tx_session;\n         vector< unique_ptr<op_evaluator> >     _operation_evaluators;\n\n         template<class Index>\n         vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(size_t count)const;\n\n         //////////////////// db_block.cpp ////////////////////\n\n      public:\n         // these were formerly private, but they have a fairly well-defined API, so let's make them public\n         void                  apply_block( const signed_block& next_block, uint32_t skip = skip_nothing );\n         processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing );\n         operation_result      apply_operation( transaction_evaluation_state& eval_state, const operation& op );\n\n      private:\n         void                  _apply_block( const signed_block& next_block );\n         processed_transaction _apply_transaction( const signed_transaction& trx );\n         void                  _cancel_bids_and_revive_mpa( const asset_object& bitasset, const asset_bitasset_data_object& bad );\n\n         ///Steps involved in applying a new block\n         ///@{\n\n         const witness_object& validate_block_header( uint32_t skip, const signed_block& next_block )const;\n         const witness_object& _validate_block_header( const signed_block& next_block )const;\n         void verify_signing_witness( const signed_block& new_block, const fork_item& fork_entry )const;\n         void update_witnesses( fork_item& fork_entry )const;\n         void create_block_summary(const signed_block& next_block);\n\n         //////////////////// db_witness_schedule.cpp ////////////////////\n\n         uint32_t update_witness_missed_blocks( const signed_block& b );\n\n         //////////////////// db_update.cpp ////////////////////\n      public:\n         generic_operation_result process_tickets();\n      private:\n         void update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks );\n         void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block);\n         void update_last_irreversible_block();\n         void clear_expired_transactions();\n         void clear_expired_proposals();\n         void clear_expired_orders();\n         void update_expired_feeds();\n         void update_core_exchange_rates();\n         void update_maintenance_flag( bool new_maintenance_flag );\n         void update_withdraw_permissions();\n         bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true,\n                                   const asset_bitasset_data_object* bitasset_ptr = nullptr );\n         void clear_expired_htlcs();\n\n         ///Steps performed only at maintenance intervals\n         ///@{\n\n         //////////////////// db_maint.cpp ////////////////////\n\n         void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const;\n         void process_budget();\n         void pay_workers( share_type& budget );\n         void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);\n         void update_active_witnesses();\n         void update_active_committee_members();\n         void update_worker_votes();\n         void process_bids( const asset_bitasset_data_object& bad );\n         void process_bitassets();\n\n         template<class Type>\n         void perform_account_maintenance( Type tally_helper );\n         ///@}\n         ///@}\n\n         vector< processed_transaction >        _pending_tx;\n         fork_database                          _fork_db;\n\n         /**\n          *  Note: we can probably store blocks by block num rather than\n          *  block id because after the undo window is past the block ID\n          *  is no longer relevant and its number is irreversible.\n          *\n          *  During the \"fork window\" we can cache blocks in memory\n          *  until the fork is resolved.  This should make maintaining\n          *  the fork tree relatively simple.\n          */\n         block_database   _block_id_to_block;\n\n         /**\n          * Contains the set of ops that are in the process of being applied from\n          * the current block.  It contains real and virtual operations in the\n          * order they occur and is cleared after the applied_block signal is\n          * emited.\n          */\n         vector<optional<operation_history_object> >  _applied_ops;\n\n         uint32_t                          _current_block_num    = 0;\n         uint16_t                          _current_trx_in_block = 0;\n         uint16_t                          _current_op_in_trx    = 0;\n         uint32_t                          _current_virtual_op   = 0;\n\n         vector<uint64_t>                  _vote_tally_buffer;\n         vector<uint64_t>                  _witness_count_histogram_buffer;\n         vector<uint64_t>                  _committee_count_histogram_buffer;\n         uint64_t                          _total_voting_stake[2]; // 0=committee, 1=witness,\n                                                                   // as in vote_id_type::vote_type\n\n         flat_map<uint32_t,block_id_type>  _checkpoints;\n\n         node_property_object              _node_property_object;\n\n         /// Whether to update votes of standby witnesses and committee members when performing chain maintenance.\n         /// Set it to true to provide accurate data to API clients, set to false to have better performance.\n         bool                              _track_standby_votes = true;\n\n         /**\n          * Whether database is successfully opened or not.\n          *\n          * The database is considered open when there's no exception\n          * or assertion fail during database::open() method, and\n          * database::close() has not been called, or failed during execution.\n          */\n         bool                              _opened = false;\n\n         // Counts nested proposal updates\n         uint32_t                           _push_proposal_nesting_depth = 0;\n\n         /// Tracks assets affected by bitshares-core issue #453 before hard fork #615 in one block\n         flat_set<asset_id_type>           _issue_453_affected_assets;\n\n         /// Pointers to core asset object and global objects who will have immutable addresses after created\n         ///@{\n         const asset_object*                    _p_core_asset_obj          = nullptr;\n         const asset_dynamic_data_object*       _p_core_dynamic_data_obj   = nullptr;\n         const global_property_object*          _p_global_prop_obj         = nullptr;\n         const dynamic_global_property_object*  _p_dyn_global_prop_obj     = nullptr;\n         const chain_property_object*           _p_chain_property_obj      = nullptr;\n         const witness_schedule_object*         _p_witness_schedule_obj    = nullptr;\n         ///@}\n   };\n\n   namespace detail\n   {\n       template<int... Is>\n       struct seq { };\n\n       template<int N, int... Is>\n       struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };\n\n       template<int... Is>\n       struct gen_seq<0, Is...> : seq<Is...> { };\n\n       template<typename T, int... Is>\n       void for_each(T&& t, const account_object& a, seq<Is...>)\n       {\n           auto l = { (std::get<Is>(t)(a), 0)... };\n           (void)l;\n       }\n   }\n\n} }\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/db_with.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/database.hpp>\n\n/*\n * This file provides with() functions which modify the database\n * temporarily, then restore it.  These functions are mostly internal\n * implementation detail of the database.\n *\n * Essentially, we want to be able to use \"finally\" to restore the\n * database regardless of whether an exception is thrown or not, but there\n * is no \"finally\" in C++.  Instead, C++ requires us to create a struct\n * and put the finally block in a destructor.  Aagh!\n */\n\nnamespace graphene { namespace chain { namespace detail {\n/**\n * Class used to help the with_skip_flags implementation.\n * It must be defined in this header because it must be\n * available to the with_skip_flags implementation,\n * which is a template and therefore must also be defined\n * in this header.\n */\nstruct skip_flags_restorer\n{\n   skip_flags_restorer( node_property_object& npo, uint32_t old_skip_flags )\n      : _npo( npo ), _old_skip_flags( old_skip_flags )\n   {}\n\n   ~skip_flags_restorer()\n   {\n      _npo.skip_flags = _old_skip_flags;\n   }\n\n   node_property_object& _npo;\n   uint32_t _old_skip_flags;      // initialized in ctor\n};\n\n/**\n * Class used to help the without_pending_transactions\n * implementation.\n *\n * TODO:  Change the name of this class to better reflect the fact\n * that it restores popped transactions as well as pending transactions.\n */\nstruct pending_transactions_restorer\n{\n   pending_transactions_restorer( database& db, std::vector<processed_transaction>&& pending_transactions )\n      : _db(db), _pending_transactions( std::move(pending_transactions) )\n   {\n      _db.clear_pending();\n   }\n\n   ~pending_transactions_restorer()\n   {\n      for( const auto& tx : _db._popped_tx )\n      {\n         try {\n            if( !_db.is_known_transaction( tx.id() ) ) {\n               _db._push_transaction( tx );\n            }\n         } catch ( const fc::exception& ) { // ignore invalid transactions\n         }\n      }\n      _db._popped_tx.clear();\n      for( const processed_transaction& tx : _pending_transactions )\n      {\n         try\n         {\n            if( !_db.is_known_transaction( tx.id() ) ) {\n               _db._push_transaction( tx );\n            }\n         }\n         catch( const fc::exception& )\n         { // ignore invalid transactions\n         }\n      }\n   }\n\n   database& _db;\n   std::vector< processed_transaction > _pending_transactions;\n};\n\n/**\n * Set the skip_flags to the given value, call callback,\n * then reset skip_flags to their previous value after\n * callback is done.\n */\ntemplate< typename Lambda >\nvoid with_skip_flags(\n   database& db,\n   uint32_t skip_flags,\n   Lambda callback )\n{\n   node_property_object& npo = db.node_properties();\n   skip_flags_restorer restorer( npo, npo.skip_flags );\n   npo.skip_flags = skip_flags;\n   callback();\n   return;\n}\n\n/**\n * Empty pending_transactions, call callback,\n * then reset pending_transactions after callback is done.\n *\n * Pending transactions which no longer validate will be culled.\n */\ntemplate< typename Lambda >\nvoid without_pending_transactions(\n   database& db,\n   std::vector<processed_transaction>&& pending_transactions,\n   Lambda callback )\n{\n    pending_transactions_restorer restorer( db, std::move(pending_transactions) );\n    callback();\n    return;\n}\n\n} } } // graphene::chain::detail\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/transaction_evaluation_state.hpp>\n#include <graphene/protocol/operations.hpp>\n\nnamespace graphene { namespace chain {\n\n   class database;\n   class generic_evaluator;\n   class transaction_evaluation_state;\n   class account_object;\n   class account_statistics_object;\n   class asset_object;\n   class asset_dynamic_data_object;\n\n   class generic_evaluator\n   {\n   public:\n      virtual ~generic_evaluator(){}\n\n      virtual int get_type()const = 0;\n      virtual operation_result start_evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply);\n\n      /**\n       * @note derived classes should ASSUME that the default validation that is\n       * indepenent of chain state should be performed by op.validate() and should\n       * not perform these extra checks.\n       */\n      virtual operation_result evaluate(const operation& op) = 0;\n      virtual operation_result apply(const operation& op) = 0;\n\n      /**\n       * Routes the fee to where it needs to go.  The default implementation\n       * routes the fee to the account_statistics_object of the fee_paying_account.\n       *\n       * Before pay_fee() is called, the fee is computed by prepare_fee() and has been\n       * moved out of the fee_paying_account and (if paid in a non-CORE asset) converted\n       * by the asset's fee pool.\n       *\n       * Therefore, when pay_fee() is called, the fee only exists in this->core_fee_paid.\n       * So pay_fee() need only increment the receiving balance.\n       *\n       * The default implementation simply calls account_statistics_object->pay_fee() to\n       * increment pending_fees or pending_vested_fees.\n       */\n      virtual void pay_fee();\n\n      database& db()const;\n\n      //void check_required_authorities(const operation& op);\n   protected:\n      /**\n       * @brief Fetch objects relevant to fee payer and set pointer members\n       * @param account_id Account which is paying the fee\n       * @param fee The fee being paid. May be in assets other than core.\n       *\n       * This method verifies that the fee is valid and sets the object pointer members and the fee fields. It should\n       * be called during do_evaluate.\n       *\n       * In particular, core_fee_paid field is set by prepare_fee().\n       */\n      void prepare_fee(account_id_type account_id, asset fee);\n\n      /**\n       * Convert the fee into BTS through the exchange pool.\n       *\n       * Reads core_fee_paid field for how much CORE is deducted from the exchange pool,\n       * and fee_from_account for how much USD is added to the pool.\n       *\n       * Since prepare_fee() does the validation checks ensuring the account and fee pool\n       * have sufficient balance and the exchange rate is correct,\n       * those validation checks are not replicated here.\n       *\n       * Rather than returning a value, this method fills in core_fee_paid field.\n       */\n      virtual void convert_fee();\n\n      object_id_type get_relative_id( object_id_type rel_id )const;\n\n      /**\n       * pay_fee() for FBA subclass should simply call this method\n       */\n      void pay_fba_fee( uint64_t fba_id );\n\n      // the next two functions are helpers that allow template functions declared in this \n      // header to call db() without including database.hpp, which would\n      // cause a circular dependency\n      share_type calculate_fee_for_operation(const operation& op) const;\n      void db_adjust_balance(const account_id_type& fee_payer, asset fee_from_account);\n\n      asset                            fee_from_account;\n      share_type                       core_fee_paid;\n      const account_object*            fee_paying_account = nullptr;\n      const account_statistics_object* fee_paying_account_statistics = nullptr;\n      const asset_object*              fee_asset          = nullptr;\n      const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;\n      transaction_evaluation_state*    trx_state;\n   };\n\n   class op_evaluator\n   {\n   public:\n      virtual ~op_evaluator(){}\n      virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply) = 0;\n   };\n\n   template<typename T>\n   class op_evaluator_impl : public op_evaluator\n   {\n   public:\n      virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply = true) override\n      {\n         T eval;\n         return eval.start_evaluate(eval_state, op, apply);\n      }\n   };\n\n   template<typename DerivedEvaluator>\n   class evaluator : public generic_evaluator\n   {\n   public:\n      virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; }\n\n      virtual operation_result evaluate(const operation& o) final override\n      {\n         auto* eval = static_cast<DerivedEvaluator*>(this);\n         const auto& op = o.get<typename DerivedEvaluator::operation_type>();\n\n         prepare_fee(op.fee_payer(), op.fee);\n         if( !trx_state->skip_fee_schedule_check )\n         {\n            share_type required_fee = calculate_fee_for_operation(op);\n            GRAPHENE_ASSERT( core_fee_paid >= required_fee,\n                       insufficient_fee,\n                       \"Insufficient Fee Paid\",\n                       (\"core_fee_paid\",core_fee_paid)(\"required\", required_fee) );\n         }\n\n         return eval->do_evaluate(op);\n      }\n\n      virtual operation_result apply(const operation& o) final override\n      {\n         auto* eval = static_cast<DerivedEvaluator*>(this);\n         const auto& op = o.get<typename DerivedEvaluator::operation_type>();\n\n         convert_fee();\n         pay_fee();\n\n         auto result = eval->do_apply(op);\n\n         db_adjust_balance(op.fee_payer(), -fee_from_account);\n\n         return result;\n      }\n   };\n} }\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/exceptions.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/exception/exception.hpp>\n#include <graphene/protocol/exceptions.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/types.hpp>\n\n#define GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( op_name )                \\\n   FC_DECLARE_DERIVED_EXCEPTION(                                      \\\n      op_name ## _validate_exception,                                 \\\n      graphene::chain::operation_validate_exception,                  \\\n      3040000 + 100 * operation::tag< op_name ## _operation >::value  \\\n      )                                                               \\\n   FC_DECLARE_DERIVED_EXCEPTION(                                      \\\n      op_name ## _evaluate_exception,                                 \\\n      graphene::chain::operation_evaluate_exception,                  \\\n      3050000 + 100 * operation::tag< op_name ## _operation >::value  \\\n      )\n\n#define GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( op_name )              \\\n   FC_IMPLEMENT_DERIVED_EXCEPTION(                                    \\\n      op_name ## _validate_exception,                                 \\\n      graphene::chain::operation_validate_exception,                  \\\n      3040000 + 100 * operation::tag< op_name ## _operation >::value, \\\n      #op_name \"_operation validation exception\"                      \\\n      )                                                               \\\n   FC_IMPLEMENT_DERIVED_EXCEPTION(                                    \\\n      op_name ## _evaluate_exception,                                 \\\n      graphene::chain::operation_evaluate_exception,                  \\\n      3050000 + 100 * operation::tag< op_name ## _operation >::value, \\\n      #op_name \"_operation evaluation exception\"                      \\\n      )\n\n#define GRAPHENE_DECLARE_OP_VALIDATE_EXCEPTION( exc_name, op_name, seqnum ) \\\n   FC_DECLARE_DERIVED_EXCEPTION(                                      \\\n      op_name ## _ ## exc_name,                                       \\\n      graphene::chain::op_name ## _validate_exception,                \\\n      3040000 + 100 * operation::tag< op_name ## _operation >::value  \\\n         + seqnum                                                     \\\n      )\n\n#define GRAPHENE_IMPLEMENT_OP_VALIDATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \\\n   FC_IMPLEMENT_DERIVED_EXCEPTION(                                    \\\n      op_name ## _ ## exc_name,                                       \\\n      graphene::chain::op_name ## _validate_exception,                \\\n      3040000 + 100 * operation::tag< op_name ## _operation >::value  \\\n         + seqnum,                                                    \\\n      msg                                                             \\\n      )\n\n#define GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( exc_name, op_name, seqnum ) \\\n   FC_DECLARE_DERIVED_EXCEPTION(                                      \\\n      op_name ## _ ## exc_name,                                       \\\n      graphene::chain::op_name ## _evaluate_exception,                \\\n      3050000 + 100 * operation::tag< op_name ## _operation >::value  \\\n         + seqnum                                                     \\\n      )\n\n#define GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \\\n   FC_IMPLEMENT_DERIVED_EXCEPTION(                                    \\\n      op_name ## _ ## exc_name,                                       \\\n      graphene::chain::op_name ## _evaluate_exception,                \\\n      3050000 + 100 * operation::tag< op_name ## _operation >::value  \\\n         + seqnum,                                                    \\\n      msg                                                             \\\n      )\n\n#define GRAPHENE_TRY_NOTIFY( signal, ... )                                    \\\n   try                                                                        \\\n   {                                                                          \\\n      signal( __VA_ARGS__ );                                                  \\\n   }                                                                          \\\n   catch( const graphene::chain::plugin_exception& e )                        \\\n   {                                                                          \\\n      elog( \"Caught plugin exception: ${e}\", (\"e\", e.to_detail_string() ) );  \\\n      throw;                                                                  \\\n   }                                                                          \\\n   catch( ... )                                                               \\\n   {                                                                          \\\n      wlog( \"Caught unexpected exception in plugin\" );                        \\\n   }\n\nnamespace graphene { namespace chain {\n\n   FC_DECLARE_EXCEPTION( chain_exception, 3000000 )\n\n   FC_DECLARE_DERIVED_EXCEPTION( database_query_exception,     chain_exception, 3010000 )\n   FC_DECLARE_DERIVED_EXCEPTION( block_validate_exception,     chain_exception, 3020000 )\n   FC_DECLARE_DERIVED_EXCEPTION( transaction_process_exception,chain_exception, 3030000 )\n   FC_DECLARE_DERIVED_EXCEPTION( operation_validate_exception, chain_exception, 3040000 )\n   FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, chain_exception, 3050000 )\n   FC_DECLARE_DERIVED_EXCEPTION( utility_exception,            chain_exception, 3060000 )\n   FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception,      chain_exception, 3070000 )\n   FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception,   chain_exception, 3080000 )\n   FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception,         chain_exception, 3090000 )\n   FC_DECLARE_DERIVED_EXCEPTION( plugin_exception,             chain_exception, 3100000 )\n\n   FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds,           chain_exception, 37006 )\n\n   FC_DECLARE_DERIVED_EXCEPTION( duplicate_transaction,        transaction_process_exception, 3030001 )\n\n   FC_DECLARE_DERIVED_EXCEPTION( pop_empty_chain,              undo_database_exception, 3070001 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( restricted_transfer_asset, transfer, 3 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_create );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( kill_unfilled, limit_order_create, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( market_not_whitelisted, limit_order_create, 2 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( market_blacklisted, limit_order_create, 3 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( selling_asset_unauthorized, limit_order_create, 4 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( receiving_asset_unauthorized, limit_order_create, 5 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( insufficient_balance, limit_order_create, 6 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_cancel );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( nonexist_order, limit_order_cancel, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, limit_order_cancel, 2 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( call_order_update );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_too_many_markets, account_create, 5 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_update, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_update, 2 )\n\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_whitelist );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_upgrade );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_transfer );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_create );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_bitasset );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_feed_producers );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_issue );\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_on_mia, asset_reserve, 1 )\n\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_fund_fee_pool );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_settle );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_global_settle );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_publish_feed );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( committee_member_create );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_create );\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_create );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2 )\n\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_update );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_delete );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_create );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_update );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_claim );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( withdraw_permission_delete );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( fill_order );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( global_parameters_update );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( vesting_balance_create );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( vesting_balance_withdraw );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( worker_create );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( custom );\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( assert );\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( balance_claim );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( claimed_too_often, balance_claim, 1 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_claim_amount, balance_claim, 2 )\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, balance_claim, 3 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1 )\n\n   GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer );\n   GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1 )\n\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer_from_blind_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_claim_fees_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( bid_collateral_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_claim_pool_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_issuer_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( htlc_create_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( htlc_redeem_operation )\n   //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( htlc_extend_operation )\n\n   #define GRAPHENE_RECODE_EXC( cause_type, effect_type ) \\\n      catch( const cause_type& e ) \\\n      { throw( effect_type( e.what(), e.get_log() ) ); }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/fba_accumulator_id.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n * An object will be created at genesis for each of these FBA accumulators.\n */\nenum graphene_fba_accumulator_id_enum\n{\n   fba_accumulator_id_transfer_to_blind = 0,\n   fba_accumulator_id_blind_transfer,\n   fba_accumulator_id_transfer_from_blind,\n   fba_accumulator_id_count\n};\n\n} }\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/fba_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\nnamespace graphene { namespace chain {\n\nclass database;\n\n/**\n * fba_accumulator_object accumulates fees to be paid out via buyback or other FBA mechanism.\n */\n\nclass fba_accumulator_object : public graphene::db::abstract_object< fba_accumulator_object >\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id = impl_fba_accumulator_object_type;\n\n      share_type accumulated_fba_fees;\n      optional< asset_id_type > designated_asset;\n\n      bool is_configured( const database& db )const;\n};\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::fba_accumulator_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::fba_accumulator_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/fork_database.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/block.hpp>\n\n#include <graphene/chain/types.hpp>\n\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n\nnamespace graphene { namespace chain {\n   using boost::multi_index_container;\n   using namespace boost::multi_index;\n\n   struct fork_item\n   {\n      fork_item( signed_block d )\n      :num(d.block_num()),id(d.id()),data( std::move(d) ){}\n\n      block_id_type previous_id()const { return data.previous; }\n\n      weak_ptr< fork_item > prev;\n      uint32_t              num;    // initialized in ctor\n      block_id_type         id;\n      signed_block          data;\n\n      // contains witness block signing keys scheduled *after* the block has been applied\n      shared_ptr< vector< pair< witness_id_type, public_key_type > > > scheduled_witnesses;\n      uint64_t                                                         next_block_aslot = 0;\n      fc::time_point_sec                                               next_block_time;\n   };\n   typedef shared_ptr<fork_item> item_ptr;\n\n\n   /**\n    *  As long as blocks are pushed in order the fork\n    *  database will maintain a linked tree of all blocks\n    *  that branch from the start_block.  The tree will\n    *  have a maximum depth of 1024 blocks after which\n    *  the database will start lopping off forks.\n    *\n    *  Every time a block is pushed into the fork DB the\n    *  block with the highest block_num will be returned.\n    */\n   class fork_database\n   {\n      public:\n         typedef vector<item_ptr> branch_type;\n         /// The maximum number of blocks that may be skipped in an out-of-order push\n         const static int MAX_BLOCK_REORDERING = 1024;\n\n         fork_database();\n         void reset();\n\n         void                             start_block(signed_block b);\n         void                             remove(block_id_type b);\n         void                             set_head(shared_ptr<fork_item> h);\n         bool                             is_known_block(const block_id_type& id)const;\n         shared_ptr<fork_item>            fetch_block(const block_id_type& id)const;\n         vector<item_ptr>                 fetch_block_by_number(uint32_t n)const;\n\n         /**\n          *  @return the new head block ( the longest fork )\n          */\n         shared_ptr<fork_item>            push_block(const signed_block& b);\n         shared_ptr<fork_item>            head()const { return _head; }\n         void                             pop_block();\n\n         /**\n          *  Given two head blocks, return two branches of the fork graph that\n          *  end with a common ancestor (same prior block)\n          */\n         pair< branch_type, branch_type >  fetch_branch_from(block_id_type first,\n                                                             block_id_type second)const;\n\n         struct block_id;\n         struct block_num;\n         typedef multi_index_container<\n            item_ptr,\n            indexed_by<\n               hashed_unique<tag<block_id>, member<fork_item, block_id_type, &fork_item::id>, std::hash<fc::ripemd160>>,\n               ordered_non_unique<tag<block_num>, member<fork_item,uint32_t,&fork_item::num>>\n            >\n         > fork_multi_index_type;\n\n         void set_max_size( uint32_t s );\n\n      private:\n         /** @return a pointer to the newly pushed item */\n         void _push_block(const item_ptr& b );\n         void _push_next(const item_ptr& newly_inserted);\n\n         uint32_t                 _max_size = 1024;\n\n         fork_multi_index_type    _index;\n         shared_ptr<fork_item>    _head;\n   };\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/genesis_state.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/address.hpp>\n#include <graphene/protocol/chain_parameters.hpp>\n#include <graphene/chain/types.hpp>\n#include <graphene/chain/immutable_chain_parameters.hpp>\n\n#include <fc/crypto/sha256.hpp>\n\n#include <string>\n#include <vector>\n\nnamespace graphene { namespace chain {\nusing std::string;\nusing std::vector;\n\nstruct genesis_state_type {\n   struct initial_account_type {\n      initial_account_type(const string& name = string(),\n                           const public_key_type& owner_key = public_key_type(),\n                           const public_key_type& active_key = public_key_type(),\n                           bool is_lifetime_member = false)\n         : name(name),\n           owner_key(owner_key),\n           active_key(active_key == public_key_type()? owner_key : active_key),\n           is_lifetime_member(is_lifetime_member)\n      {}\n      string name;\n      public_key_type owner_key;\n      public_key_type active_key;\n      bool is_lifetime_member = false;\n   };\n   struct initial_asset_type {\n      struct initial_collateral_position {\n         address owner;\n         share_type collateral;\n         share_type debt;\n      };\n\n      string symbol;\n      string issuer_name;\n\n      string description;\n      uint8_t precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;\n\n      share_type max_supply;\n      share_type accumulated_fees;\n\n      bool is_bitasset = false;\n      vector<initial_collateral_position> collateral_records;\n   };\n   struct initial_balance_type {\n      address owner;\n      string asset_symbol;\n      share_type amount;\n   };\n   struct initial_vesting_balance_type {\n      address owner;\n      string asset_symbol;\n      share_type amount;\n      time_point_sec begin_timestamp;\n      uint32_t vesting_duration_seconds = 0;\n      share_type begin_balance;\n   };\n   struct initial_witness_type {\n      /// Must correspond to one of the initial accounts\n      string owner_name;\n      public_key_type block_signing_key;\n   };\n   struct initial_committee_member_type {\n      /// Must correspond to one of the initial accounts\n      string owner_name;\n   };\n   struct initial_worker_type {\n      /// Must correspond to one of the initial accounts\n      string owner_name;\n      share_type daily_pay;\n   };\n\n   time_point_sec                           initial_timestamp;\n   share_type                               max_core_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n   chain_parameters                         initial_parameters;\n   immutable_chain_parameters               immutable_parameters;\n   vector<initial_account_type>             initial_accounts;\n   vector<initial_asset_type>               initial_assets;\n   vector<initial_balance_type>             initial_balances;\n   vector<initial_vesting_balance_type>     initial_vesting_balances;\n   uint64_t                                 initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;\n   vector<initial_witness_type>             initial_witness_candidates;\n   vector<initial_committee_member_type>    initial_committee_candidates;\n   vector<initial_worker_type>              initial_worker_candidates;\n\n   /**\n    * Temporary, will be moved elsewhere.\n    */\n   chain_id_type                            initial_chain_id;\n\n   /**\n    * Get the chain_id corresponding to this genesis state.\n    *\n    * This is the SHA256 serialization of the genesis_state.\n    */\n   chain_id_type compute_chain_id() const;\n};\n\n} } // namespace graphene::chain\n\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_account_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_asset_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_balance_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_vesting_balance_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_witness_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_committee_member_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type::initial_worker_type )\nFC_REFLECT_TYPENAME( graphene::chain::genesis_state_type )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_account_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_asset_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_balance_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_vesting_balance_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_witness_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_committee_member_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type::initial_worker_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::genesis_state_type )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/get_config.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/variant_object.hpp>\n\nnamespace graphene { namespace chain {\n\nfc::variant_object get_config();\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/global_property_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/chain_parameters.hpp>\n#include <graphene/chain/types.hpp>\n#include <graphene/db/object.hpp>\n\nnamespace graphene { namespace chain {\n\n   /**\n    * @class global_property_object\n    * @brief Maintains global state information (committee_member list, current fees)\n    * @ingroup object\n    * @ingroup implementation\n    *\n    * This is an implementation detail. The values here are set by committee_members to tune the blockchain parameters.\n    */\n   class global_property_object : public graphene::db::abstract_object<global_property_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_global_property_object_type;\n\n         chain_parameters           parameters;\n         optional<chain_parameters> pending_parameters;\n\n         uint32_t                           next_available_vote_id = 0;\n         vector<committee_member_id_type>   active_committee_members; // updated once per maintenance interval\n         flat_set<witness_id_type>          active_witnesses; // updated once per maintenance interval\n         // n.b. witness scheduling is done by witness_schedule object\n   };\n\n   /**\n    * @class dynamic_global_property_object\n    * @brief Maintains global state information (committee_member list, current fees)\n    * @ingroup object\n    * @ingroup implementation\n    *\n    * This is an implementation detail. The values here are calculated during normal chain operations and reflect the\n    * current values of global blockchain properties.\n    */\n   class dynamic_global_property_object : public abstract_object<dynamic_global_property_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_dynamic_global_property_object_type;\n\n         uint32_t          head_block_number = 0;\n         block_id_type     head_block_id;\n         time_point_sec    time;\n         witness_id_type   current_witness;\n         time_point_sec    next_maintenance_time;\n         time_point_sec    last_budget_time;\n         share_type        witness_budget;\n         share_type        total_pob;\n         share_type        total_inactive;\n         uint32_t          accounts_registered_this_interval = 0;\n         /**\n          *  Every time a block is missed this increases by\n          *  RECENTLY_MISSED_COUNT_INCREMENT,\n          *  every time a block is found it decreases by\n          *  RECENTLY_MISSED_COUNT_DECREMENT.  It is\n          *  never less than 0.\n          */\n         uint32_t          recently_missed_count = 0;\n\n         /**\n          * The current absolute slot number.  Equal to the total\n          * number of slots since genesis.  Also equal to the total\n          * number of missed slots plus head_block_number.\n          */\n         uint64_t                current_aslot = 0;\n\n         /**\n          * used to compute witness participation.\n          */\n         fc::uint128_t recent_slots_filled;\n\n         /**\n          * dynamic_flags specifies chain state properties that can be\n          * expressed in one bit.\n          */\n         uint32_t dynamic_flags = 0;\n\n         uint32_t last_irreversible_block_num = 0;\n\n         enum dynamic_flag_bits\n         {\n            /**\n             * If maintenance_flag is set, then the head block is a\n             * maintenance block.  This means\n             * get_time_slot(1) - head_block_time() will have a gap\n             * due to maintenance duration.\n             *\n             * This flag answers the question, \"Was maintenance\n             * performed in the last call to apply_block()?\"\n             */\n            maintenance_flag = 0x01\n         };\n   };\n}}\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::dynamic_global_property_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::global_property_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_object )\nFC_REFLECT_TYPENAME( graphene::chain::global_property_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::dynamic_global_property_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::global_property_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/hardfork_visitor.hpp",
    "content": "#pragma once\n/*\n * Copyright (c) 2019 Contributors\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/operations.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/reflect/typelist.hpp>\n\n#include <type_traits>\n#include <functional>\n\nnamespace graphene { namespace chain {\nusing namespace protocol;\nnamespace TL { using namespace fc::typelist; }\n\n/**\n * @brief The hardfork_visitor struct checks whether a given operation type has been hardforked in or not\n *\n * This visitor can be invoked in several different ways, including operation::visit, typelist::runtime::dispatch, or\n * direct invocation by calling the visit() method passing an operation variant, narrow operation type, operation tag,\n * or templating on the narrow operation type\n */\nstruct hardfork_visitor {\n   using result_type = bool;\n   using first_unforked_op = custom_authority_create_operation;\n   using BSIP_40_ops = TL::list<custom_authority_create_operation, custom_authority_update_operation,\n                                custom_authority_delete_operation>;\n   using hf2103_ops = TL::list<ticket_create_operation, ticket_update_operation>;\n   using liquidity_pool_ops = TL::list< liquidity_pool_create_operation,\n                                        liquidity_pool_delete_operation,\n                                        liquidity_pool_deposit_operation,\n                                        liquidity_pool_withdraw_operation,\n                                        liquidity_pool_exchange_operation >;\n   fc::time_point_sec now;\n\n   hardfork_visitor(fc::time_point_sec now) : now(now) {}\n\n   /// The real visitor implementations. Future operation types get added in here.\n   /// @{\n   template<typename Op>\n   std::enable_if_t<operation::tag<Op>::value < operation::tag<first_unforked_op>::value, bool>\n   visit() { return true; }\n   template<typename Op>\n   std::enable_if_t<TL::contains<BSIP_40_ops, Op>(), bool>\n   visit() { return HARDFORK_BSIP_40_PASSED(now); }\n   template<typename Op>\n   std::enable_if_t<TL::contains<hf2103_ops, Op>(), bool>\n   visit() { return HARDFORK_CORE_2103_PASSED(now); }\n   template<typename Op>\n   std::enable_if_t<TL::contains<liquidity_pool_ops, Op>(), bool>\n   visit() { return HARDFORK_LIQUIDITY_POOL_PASSED(now); }\n   /// @}\n\n   /// typelist::runtime::dispatch adaptor\n   template<class W, class Op=typename W::type>\n   std::enable_if_t<TL::contains<operation::list, Op>(), bool>\n   operator()(W) { return visit<Op>(); }\n   /// static_variant::visit adaptor\n   template<class Op>\n   std::enable_if_t<TL::contains<operation::list, Op>(), bool>\n   operator()(const Op&) { return visit<Op>(); }\n   /// Tag adaptor\n   bool visit(operation::tag_type tag) {\n      return TL::runtime::dispatch(operation::list(), (size_t)tag, *this);\n   }\n   /// operation adaptor\n   bool visit(const operation& op) {\n      return visit(op.which());\n   }\n};\n\n} } // namespace graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/htlc_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2018 jmjatlanta and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/htlc_object.hpp>\n\nnamespace graphene { \n   namespace chain {\n\n      class htlc_create_evaluator : public evaluator<htlc_create_evaluator>\n      {\n         public:\n    \t      typedef htlc_create_operation operation_type;\n\n    \t      void_result do_evaluate( const htlc_create_operation& o);\n    \t      object_id_type do_apply( const htlc_create_operation& o);\n      };\n\n      class htlc_redeem_evaluator : public evaluator<htlc_redeem_evaluator>\n      {\n         public:\n    \t      typedef htlc_redeem_operation operation_type;\n\n    \t      void_result do_evaluate( const htlc_redeem_operation& o);\n    \t      void_result do_apply( const htlc_redeem_operation& o);\n    \t      const htlc_object* htlc_obj = nullptr;\n      };\n\n      class htlc_extend_evaluator : public evaluator<htlc_extend_evaluator>\n      {\n         public:\n    \t      typedef htlc_extend_operation operation_type;\n\n    \t      void_result do_evaluate( const htlc_extend_operation& o);\n    \t      void_result do_apply( const htlc_extend_operation& o);\n    \t      const htlc_object* htlc_obj = nullptr;\n      };\n   } // namespace graphene\n} // namespace graphene\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/htlc_object.hpp",
    "content": "/*\n * Copyright (c) 2018 jmjatlanta and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/htlc.hpp>\n#include <graphene/db/generic_index.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace protocol;\n\n   /**\n    * @brief database object to store HTLCs\n    * \n    * This object is stored in the database while an HTLC is active. The HTLC will\n    * become inactive at expiration or when unlocked via the preimage.\n    */\n   class htlc_object : public graphene::db::abstract_object<htlc_object> {\n      public:\n         // uniquely identify this object in the database\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id  = htlc_object_type;\n\n         struct transfer_info {\n            account_id_type from;\n            account_id_type to;\n            share_type amount;\n            asset_id_type asset_id;\n         } transfer;\n         struct condition_info {\n            struct hash_lock_info {  \n               htlc_hash preimage_hash;\n               uint16_t preimage_size;\n            } hash_lock;\n            struct time_lock_info {\n               fc::time_point_sec expiration;\n            } time_lock;\n         } conditions;\n\n         fc::optional<memo_data> memo;\n\n      /****\n       * Index helper for timelock\n       */\n      struct timelock_extractor {\n         typedef fc::time_point_sec result_type;\n         const result_type& operator()(const htlc_object& o)const { return o.conditions.time_lock.expiration; }\n      };\n\n      /*****\n       * Index helper for from\n       */\n      struct from_extractor {\n         typedef account_id_type result_type;\n         const result_type& operator()(const htlc_object& o)const { return o.transfer.from; }\n      };\n\n      /*****\n       * Index helper for to\n       */\n      struct to_extractor {\n         typedef account_id_type result_type;\n         const result_type& operator()(const htlc_object& o)const { return o.transfer.to; }\n      };\n   };\n\n   struct by_from_id;\n   struct by_expiration;\n   struct by_to_id;\n   typedef multi_index_container<\n         htlc_object,\n         indexed_by<\n            ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,\n\n            ordered_unique< tag< by_expiration >, \n                  composite_key< htlc_object,\n                  htlc_object::timelock_extractor,\n                  member< object, object_id_type, &object::id > > >,\n\n            ordered_unique< tag< by_from_id >,\n                  composite_key< htlc_object, \n                  htlc_object::from_extractor,\n                  member< object, object_id_type, &object::id > > >,\n\n            ordered_unique< tag< by_to_id >,\n                  composite_key< htlc_object,\n                  htlc_object::to_extractor,\n                  member< object, object_id_type, &object::id > > >\n      >\n\n   > htlc_object_index_type;\n\n   typedef generic_index< htlc_object, htlc_object_index_type > htlc_index;\n\n} } // namespace graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::htlc_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::htlc_object::condition_info::hash_lock_info )\nFC_REFLECT_TYPENAME( graphene::chain::htlc_object::condition_info::time_lock_info )\nFC_REFLECT_TYPENAME( graphene::chain::htlc_object::condition_info )\nFC_REFLECT_TYPENAME( graphene::chain::htlc_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::htlc_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/config.hpp>\n#include <graphene/chain/types.hpp>\n\nnamespace graphene { namespace chain {\n\nstruct immutable_chain_parameters\n{\n   uint16_t min_committee_member_count = GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT;\n   uint16_t min_witness_count = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;\n   uint32_t num_special_accounts = 0;\n   uint32_t num_special_assets = 0;\n};\n\n} } // graphene::chain\n\nFC_REFLECT_TYPENAME( graphene::chain::immutable_chain_parameters )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::immutable_chain_parameters )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/impacted.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/container/flat.hpp>\n#include <graphene/protocol/operations.hpp>\n#include <graphene/protocol/transaction.hpp>\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid operation_get_impacted_accounts( const graphene::chain::operation& op,\n                                      fc::flat_set<graphene::chain::account_id_type>& result,\n                                      bool ignore_custom_operation_required_auths );\n\nvoid transaction_get_impacted_accounts( const graphene::chain::transaction& tx,\n                                        fc::flat_set<graphene::chain::account_id_type>& result,\n                                        bool ignore_custom_operation_required_auths );\n\n} } // graphene::app\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/internal_exceptions.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/exceptions.hpp>\n\n#define GRAPHENE_DECLARE_INTERNAL_EXCEPTION( exc_name, seqnum, msg )  \\\n   FC_DECLARE_DERIVED_EXCEPTION(                                      \\\n      internal_ ## exc_name,                                          \\\n      graphene::chain::internal_exception,                            \\\n      3990000 + seqnum                                                \\\n      )\n\n#define GRAPHENE_IMPLEMENT_INTERNAL_EXCEPTION( exc_name, seqnum, msg )  \\\n   FC_IMPLEMENT_DERIVED_EXCEPTION(                                      \\\n      internal_ ## exc_name,                                          \\\n      graphene::chain::internal_exception,                            \\\n      3990000 + seqnum,                                               \\\n      msg                                                             \\\n      )\n\nnamespace graphene { namespace chain {\n\nFC_DECLARE_DERIVED_EXCEPTION( internal_exception, graphene::chain::chain_exception, 3990000 )\n\nGRAPHENE_DECLARE_INTERNAL_EXCEPTION( verify_auth_max_auth_exceeded, 1, \"Exceeds max authority fan-out\" )\nGRAPHENE_DECLARE_INTERNAL_EXCEPTION( verify_auth_account_not_found, 2, \"Auth account not found\" )\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/is_authorized_asset.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\nnamespace graphene { namespace chain {\n\nclass account_object;\nclass asset_object;\nclass database;\n\nnamespace detail {\n\nbool _is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj);\n\n}\n\n/**\n * @return true if the account is whitelisted and not blacklisted to transact in the provided asset; false\n * otherwise.\n */\n\ninline bool is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj)\n{\n   bool fast_check = !(asset_obj.options.flags & white_list);\n   fast_check &= !(acct.allowed_assets.valid());\n\n   if( fast_check )\n      return true;\n\n   bool slow_check = detail::_is_authorized_asset( d, acct, asset_obj );\n   return slow_check;\n}\n\n} }\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/liquidity_pool_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n\n#include <graphene/protocol/liquidity_pool.hpp>\n\nnamespace graphene { namespace chain {\n\n   class asset_object;\n   class asset_dynamic_data_object;\n   class liquidity_pool_object;\n\n   class liquidity_pool_create_evaluator : public evaluator<liquidity_pool_create_evaluator>\n   {\n      public:\n         typedef liquidity_pool_create_operation operation_type;\n\n         void_result do_evaluate( const liquidity_pool_create_operation& op );\n         generic_operation_result do_apply( const liquidity_pool_create_operation& op );\n\n         const asset_object* _share_asset = nullptr;\n   };\n\n   class liquidity_pool_delete_evaluator : public evaluator<liquidity_pool_delete_evaluator>\n   {\n      public:\n         typedef liquidity_pool_delete_operation operation_type;\n\n         void_result do_evaluate( const liquidity_pool_delete_operation& op );\n         generic_operation_result do_apply( const liquidity_pool_delete_operation& op );\n\n         const liquidity_pool_object* _pool = nullptr;\n         const asset_object* _share_asset = nullptr;\n   };\n\n   class liquidity_pool_deposit_evaluator : public evaluator<liquidity_pool_deposit_evaluator>\n   {\n      public:\n         typedef liquidity_pool_deposit_operation operation_type;\n\n         void_result do_evaluate( const liquidity_pool_deposit_operation& op );\n         generic_exchange_operation_result do_apply( const liquidity_pool_deposit_operation& op );\n\n         const liquidity_pool_object* _pool = nullptr;\n         const asset_dynamic_data_object* _share_asset_dyn_data = nullptr;\n         asset _account_receives;\n         asset _pool_receives_a;\n         asset _pool_receives_b;\n   };\n\n   class liquidity_pool_withdraw_evaluator : public evaluator<liquidity_pool_withdraw_evaluator>\n   {\n      public:\n         typedef liquidity_pool_withdraw_operation operation_type;\n\n         void_result do_evaluate( const liquidity_pool_withdraw_operation& op );\n         generic_exchange_operation_result do_apply( const liquidity_pool_withdraw_operation& op );\n\n         const liquidity_pool_object* _pool = nullptr;\n         const asset_dynamic_data_object* _share_asset_dyn_data = nullptr;\n         asset _pool_pays_a;\n         asset _pool_pays_b;\n   };\n\n   class liquidity_pool_exchange_evaluator : public evaluator<liquidity_pool_exchange_evaluator>\n   {\n      public:\n         typedef liquidity_pool_exchange_operation operation_type;\n\n         void_result do_evaluate( const liquidity_pool_exchange_operation& op );\n         generic_exchange_operation_result do_apply( const liquidity_pool_exchange_operation& op );\n\n         const liquidity_pool_object* _pool = nullptr;\n         const asset_object* _pool_pays_asset = nullptr;\n         const asset_object* _pool_receives_asset = nullptr;\n         asset _pool_pays;\n         asset _pool_receives;\n         asset _account_receives;\n         asset _maker_market_fee;\n         asset _taker_market_fee;\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/liquidity_pool_object.hpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/liquidity_pool.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n\nusing namespace graphene::db;\n\n/**\n *  @brief A liquidity pool\n *  @ingroup object\n *  @ingroup protocol\n *\n */\nclass liquidity_pool_object : public abstract_object<liquidity_pool_object>\n{\n   public:\n      static constexpr uint8_t space_id = protocol_ids;\n      static constexpr uint8_t type_id  = liquidity_pool_object_type;\n\n      asset_id_type   asset_a;                     ///< Type of the first asset in the pool\n      asset_id_type   asset_b;                     ///< Type of the second asset in the pool\n      share_type      balance_a;                   ///< The balance of the first asset in the pool\n      share_type      balance_b;                   ///< The balance of the second asset in the pool\n      asset_id_type   share_asset;                 ///< Type of the share asset aka the LP token\n      uint16_t        taker_fee_percent = 0;       ///< Taker fee percent\n      uint16_t        withdrawal_fee_percent = 0;  ///< Withdrawal fee percent\n      fc::uint128_t   virtual_value = 0;           ///< Virtual value of the pool\n\n      void update_virtual_value()\n      {\n         virtual_value = fc::uint128_t( balance_a.value ) * balance_b.value;\n      }\n};\n\nstruct by_share_asset;\nstruct by_asset_a;\nstruct by_asset_b;\nstruct by_asset_ab;\n\n/**\n* @ingroup object_index\n*/\ntypedef multi_index_container<\n   liquidity_pool_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_share_asset>,\n                      member< liquidity_pool_object, asset_id_type, &liquidity_pool_object::share_asset > >,\n      ordered_unique< tag<by_asset_a>,\n         composite_key< liquidity_pool_object,\n            member< liquidity_pool_object, asset_id_type, &liquidity_pool_object::asset_a >,\n            member< object, object_id_type, &object::id>\n         >\n      >,\n      ordered_unique< tag<by_asset_b>,\n         composite_key< liquidity_pool_object,\n            member< liquidity_pool_object, asset_id_type, &liquidity_pool_object::asset_b >,\n            member< object, object_id_type, &object::id>\n         >\n      >,\n      ordered_unique< tag<by_asset_ab>,\n         composite_key< liquidity_pool_object,\n            member< liquidity_pool_object, asset_id_type, &liquidity_pool_object::asset_a >,\n            member< liquidity_pool_object, asset_id_type, &liquidity_pool_object::asset_b >,\n            member< object, object_id_type, &object::id>\n         >\n      >\n   >\n> liquidity_pool_multi_index_type;\n\n/**\n* @ingroup object_index\n*/\ntypedef generic_index<liquidity_pool_object, liquidity_pool_multi_index_type> liquidity_pool_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE( graphene::chain::liquidity_pool_object )\n\nFC_REFLECT_TYPENAME( graphene::chain::liquidity_pool_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::liquidity_pool_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/market_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/types.hpp>\n\nnamespace graphene { namespace chain {\n\n   class account_object;\n   class asset_object;\n   class asset_bitasset_data_object;\n   class call_order_object;\n   class limit_order_object;\n   class collateral_bid_object;\n\n   class limit_order_create_evaluator : public evaluator<limit_order_create_evaluator>\n   {\n      public:\n         typedef limit_order_create_operation operation_type;\n\n         void_result do_evaluate( const limit_order_create_operation& o );\n         object_id_type do_apply( const limit_order_create_operation& o );\n\n         /** override the default behavior defined by generic_evalautor\n          */\n         virtual void convert_fee() override;\n\n         /** override the default behavior defined by generic_evalautor which is to\n          * post the fee to fee_paying_account_stats.pending_fees\n          */\n         virtual void pay_fee() override;\n\n         share_type                          _deferred_fee  = 0;\n         asset                               _deferred_paid_fee;\n         const limit_order_create_operation* _op            = nullptr;\n         const account_object*               _seller        = nullptr;\n         const asset_object*                 _sell_asset    = nullptr;\n         const asset_object*                 _receive_asset = nullptr;\n   };\n\n   class limit_order_cancel_evaluator : public evaluator<limit_order_cancel_evaluator>\n   {\n      public:\n         typedef limit_order_cancel_operation operation_type;\n\n         void_result do_evaluate( const limit_order_cancel_operation& o );\n         asset do_apply( const limit_order_cancel_operation& o );\n\n         const limit_order_object* _order;\n   };\n\n   class call_order_update_evaluator : public evaluator<call_order_update_evaluator>\n   {\n      public:\n         typedef call_order_update_operation operation_type;\n\n         void_result do_evaluate( const call_order_update_operation& o );\n         object_id_type do_apply( const call_order_update_operation& o );\n\n         bool _closing_order = false;\n         const asset_object* _debt_asset = nullptr;\n         const account_object* _paying_account = nullptr;\n         const call_order_object* _order = nullptr;\n         const asset_bitasset_data_object* _bitasset_data = nullptr;\n         const asset_dynamic_data_object*  _dynamic_data_obj = nullptr;\n   };\n\n   class bid_collateral_evaluator : public evaluator<bid_collateral_evaluator>\n   {\n      public:\n         typedef bid_collateral_operation operation_type;\n\n         void_result do_evaluate( const bid_collateral_operation& o );\n         void_result do_apply( const bid_collateral_operation& o );\n\n         const asset_object* _debt_asset = nullptr;\n         const asset_bitasset_data_object* _bitasset_data = nullptr;\n         const account_object* _paying_account = nullptr;\n         const collateral_bid_object* _bid = nullptr;\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/market_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n#include <graphene/protocol/asset.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n\nusing namespace graphene::db;\n\n/**\n *  @brief an offer to sell a amount of a asset at a specified exchange rate by a certain time\n *  @ingroup object\n *  @ingroup protocol\n *  @ingroup market\n *\n *  This limit_order_objects are indexed by @ref expiration and is automatically deleted on the first block after expiration.\n */\nclass limit_order_object : public abstract_object<limit_order_object>\n{\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id  = limit_order_object_type;\n\n      time_point_sec   expiration;\n      account_id_type  seller;\n      share_type       for_sale; ///< asset id is sell_price.base.asset_id\n      price            sell_price;\n      share_type       deferred_fee; ///< fee converted to CORE\n      asset            deferred_paid_fee; ///< originally paid fee\n\n      pair<asset_id_type,asset_id_type> get_market()const\n      {\n         auto tmp = std::make_pair( sell_price.base.asset_id, sell_price.quote.asset_id );\n         if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );\n         return tmp;\n      }\n\n      asset amount_for_sale()const   { return asset( for_sale, sell_price.base.asset_id ); }\n      asset amount_to_receive()const { return amount_for_sale() * sell_price; }\n      asset_id_type sell_asset_id()const    { return sell_price.base.asset_id;  }\n      asset_id_type receive_asset_id()const { return sell_price.quote.asset_id; }\n};\n\nstruct by_price;\nstruct by_expiration;\nstruct by_account;\nstruct by_account_price;\ntypedef multi_index_container<\n   limit_order_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_expiration>,\n         composite_key< limit_order_object,\n            member< limit_order_object, time_point_sec, &limit_order_object::expiration>,\n            member< object, object_id_type, &object::id>\n         >\n      >,\n      ordered_unique< tag<by_price>,\n         composite_key< limit_order_object,\n            member< limit_order_object, price, &limit_order_object::sell_price>,\n            member< object, object_id_type, &object::id>\n         >,\n         composite_key_compare< std::greater<price>, std::less<object_id_type> >\n      >,\n      // index used by APIs\n      ordered_unique< tag<by_account>,\n         composite_key< limit_order_object,\n            member<limit_order_object, account_id_type, &limit_order_object::seller>,\n            member<object, object_id_type, &object::id>\n         >\n      >,\n      // index used by APIs\n      ordered_unique< tag<by_account_price>,\n         composite_key< limit_order_object,\n            member<limit_order_object, account_id_type, &limit_order_object::seller>,\n            member<limit_order_object, price, &limit_order_object::sell_price>,\n            member<object, object_id_type, &object::id>\n         >,\n         composite_key_compare<std::less<account_id_type>, std::greater<price>, std::less<object_id_type>>\n      >\n   >\n> limit_order_multi_index_type;\n\ntypedef generic_index<limit_order_object, limit_order_multi_index_type> limit_order_index;\n\n/**\n * @class call_order_object\n * @brief tracks debt and call price information\n *\n * There should only be one call_order_object per asset pair per account and\n * they will all have the same call price.\n */\nclass call_order_object : public abstract_object<call_order_object>\n{\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id  = call_order_object_type;\n\n      asset get_collateral()const { return asset( collateral, call_price.base.asset_id ); }\n      asset get_debt()const { return asset( debt, debt_type() ); }\n      asset amount_to_receive()const { return get_debt(); }\n      asset_id_type debt_type()const { return call_price.quote.asset_id; }\n      asset_id_type collateral_type()const { return call_price.base.asset_id; }\n      price collateralization()const { return get_collateral() / get_debt(); }\n\n      account_id_type  borrower;\n      share_type       collateral;  ///< call_price.base.asset_id, access via get_collateral\n      share_type       debt;        ///< call_price.quote.asset_id, access via get_debt\n      price            call_price;  ///< Collateral / Debt\n\n      optional<uint16_t> target_collateral_ratio; ///< maximum CR to maintain when selling collateral on margin call\n\n      pair<asset_id_type,asset_id_type> get_market()const\n      {\n         auto tmp = std::make_pair( call_price.base.asset_id, call_price.quote.asset_id );\n         if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );\n         return tmp;\n      }\n\n      /**\n       *  Calculate maximum quantity of debt to cover to satisfy @ref target_collateral_ratio.\n       *\n       *  @param match_price the matching price if this call order is margin called\n       *  @param feed_price median settlement price of debt asset\n       *  @param maintenance_collateral_ratio median maintenance collateral ratio of debt asset\n       *  @param maintenance_collateralization maintenance collateralization of debt asset,\n       *                                       should only be valid after core-1270 hard fork\n       *  @return maximum amount of debt that can be called\n       */\n      share_type get_max_debt_to_cover( price match_price,\n                                        price feed_price,\n                                        const uint16_t maintenance_collateral_ratio,\n                                        const optional<price>& maintenance_collateralization = optional<price>() )const;\n};\n\n/**\n *  @brief tracks bitassets scheduled for force settlement at some point in the future.\n *\n *  On the @ref settlement_date the @ref balance will be converted to the collateral asset\n *  and paid to @ref owner and then this object will be deleted.\n */\nclass force_settlement_object : public abstract_object<force_settlement_object>\n{\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id  = force_settlement_object_type;\n\n      account_id_type   owner;\n      asset             balance;\n      time_point_sec    settlement_date;\n\n      asset_id_type settlement_asset_id()const\n      { return balance.asset_id; }\n};\n\n/**\n * @class collateral_bid_object\n * @brief bids of collateral for debt after a black swan\n *\n * There should only be one collateral_bid_object per asset per account, and\n * only for smartcoin assets that have a global settlement_price.\n */\nclass collateral_bid_object : public abstract_object<collateral_bid_object>\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id  = impl_collateral_bid_object_type;\n\n      asset get_additional_collateral()const { return inv_swan_price.base; }\n      asset get_debt_covered()const { return inv_swan_price.quote; }\n      asset_id_type debt_type()const { return inv_swan_price.quote.asset_id; }\n\n      account_id_type  bidder;\n      price            inv_swan_price;  // Collateral / Debt\n};\n\nstruct by_collateral;\nstruct by_account;\nstruct by_price;\ntypedef multi_index_container<\n   call_order_object,\n   indexed_by<\n      ordered_unique< tag<by_id>,\n         member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_price>,\n         composite_key< call_order_object,\n            member< call_order_object, price, &call_order_object::call_price>,\n            member< object, object_id_type, &object::id>\n         >,\n         composite_key_compare< std::less<price>, std::less<object_id_type> >\n      >,\n      ordered_unique< tag<by_account>,\n         composite_key< call_order_object,\n            member< call_order_object, account_id_type, &call_order_object::borrower >,\n            const_mem_fun< call_order_object, asset_id_type, &call_order_object::debt_type>\n         >\n      >,\n      ordered_unique< tag<by_collateral>,\n         composite_key< call_order_object,\n            const_mem_fun< call_order_object, price, &call_order_object::collateralization >,\n            member< object, object_id_type, &object::id >\n         >\n      >\n   >\n> call_order_multi_index_type;\n\nstruct by_expiration;\ntypedef multi_index_container<\n   force_settlement_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_account>,\n         composite_key< force_settlement_object,\n            member<force_settlement_object, account_id_type, &force_settlement_object::owner>,\n            member< object, object_id_type, &object::id >\n         >\n      >,\n      ordered_unique< tag<by_expiration>,\n         composite_key< force_settlement_object,\n            const_mem_fun<force_settlement_object, asset_id_type, &force_settlement_object::settlement_asset_id>,\n            member<force_settlement_object, time_point_sec, &force_settlement_object::settlement_date>,\n            member< object, object_id_type, &object::id >\n         >\n      >\n   >\n> force_settlement_object_multi_index_type;\n\ntypedef multi_index_container<\n   collateral_bid_object,\n   indexed_by<\n      ordered_unique< tag<by_id>,\n         member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_account>,\n         composite_key< collateral_bid_object,\n            const_mem_fun< collateral_bid_object, asset_id_type, &collateral_bid_object::debt_type>,\n            member< collateral_bid_object, account_id_type, &collateral_bid_object::bidder>\n         >\n      >,\n      ordered_unique< tag<by_price>,\n         composite_key< collateral_bid_object,\n            const_mem_fun< collateral_bid_object, asset_id_type, &collateral_bid_object::debt_type>,\n            member< collateral_bid_object, price, &collateral_bid_object::inv_swan_price >,\n            member< object, object_id_type, &object::id >\n         >,\n         composite_key_compare< std::less<asset_id_type>, std::greater<price>, std::less<object_id_type> >\n      >\n   >\n> collateral_bid_object_multi_index_type;\n\ntypedef generic_index<call_order_object, call_order_multi_index_type>                      call_order_index;\ntypedef generic_index<force_settlement_object, force_settlement_object_multi_index_type>   force_settlement_index;\ntypedef generic_index<collateral_bid_object, collateral_bid_object_multi_index_type>       collateral_bid_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::limit_order_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::call_order_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::force_settlement_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::collateral_bid_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::limit_order_object )\nFC_REFLECT_TYPENAME( graphene::chain::call_order_object )\nFC_REFLECT_TYPENAME( graphene::chain::force_settlement_object )\nFC_REFLECT_TYPENAME( graphene::chain::collateral_bid_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::limit_order_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::call_order_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::force_settlement_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::collateral_bid_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/node_property_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/db/object.hpp>\n\nnamespace graphene { namespace chain {\n\n   /**\n    * @brief Contains per-node database configuration.\n    *\n    *  Transactions are evaluated differently based on per-node state.\n    *  Settings here may change based on whether the node is syncing or up-to-date.\n    *  Or whether the node is a witness node. Or if we're processing a\n    *  transaction in a witness-signed block vs. a fresh transaction\n    *  from the p2p network.  Or configuration-specified tradeoffs of\n    *  performance/hardfork resilience vs. paranoia.\n    */\n   class node_property_object\n   {\n      public:\n         node_property_object(){}\n         ~node_property_object(){}\n\n         uint32_t skip_flags = 0;\n         std::map< block_id_type, std::vector< fc::variant_object > > debug_updates;\n   };\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/operation_history_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/operations.hpp>\n#include <graphene/db/object.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n\n   /**\n    * @brief tracks the history of all logical operations on blockchain state\n    * @ingroup object\n    * @ingroup implementation\n    *\n    *  All operations and virtual operations result in the creation of an\n    *  operation_history_object that is maintained on disk as a stack.  Each\n    *  real or virtual operation is assigned a unique ID / sequence number that\n    *  it can be referenced by.\n    *\n    *  @note  by default these objects are not tracked, the account_history_plugin must\n    *  be loaded fore these objects to be maintained.\n    *\n    *  @note  this object is READ ONLY it can never be modified\n    */\n   class operation_history_object : public abstract_object<operation_history_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id  = operation_history_object_type;\n\n         operation_history_object( const operation& o ):op(o){}\n         operation_history_object(){}\n\n         operation         op;\n         operation_result  result;\n         /** the block that caused this operation */\n         uint32_t          block_num = 0;\n         /** the transaction in the block */\n         uint16_t          trx_in_block = 0;\n         /** the operation within the transaction */\n         uint16_t          op_in_trx = 0;\n         /** any virtual operations implied by operation in block */\n         uint32_t          virtual_op = 0;\n   };\n\n   /**\n    *  @brief a node in a linked list of operation_history_objects\n    *  @ingroup implementation\n    *  @ingroup object\n    *\n    *  Account history is important for users and wallets even though it is\n    *  not part of \"core validation\".   Account history is maintained as\n    *  a linked list stored on disk in a stack.  Each account will point to the\n    *  most recent account history object by ID.  When a new operation relativent\n    *  to that account is processed a new account history object is allcoated at\n    *  the end of the stack and intialized to point to the prior object.\n    *\n    *  This data is never accessed as part of chain validation and therefore\n    *  can be kept on disk as a memory mapped file.  Using a memory mapped file\n    *  will help the operating system better manage / cache / page files and\n    *  also accelerates load time.\n    *\n    *  When the transaction history for a particular account is requested the\n    *  linked list can be traversed with relatively effecient disk access because\n    *  of the use of a memory mapped stack.\n    */\n   class account_transaction_history_object :  public abstract_object<account_transaction_history_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_account_transaction_history_object_type;\n         account_id_type                      account; /// the account this operation applies to\n         operation_history_id_type            operation_id;\n         uint64_t                             sequence = 0; /// the operation position within the given account\n         account_transaction_history_id_type  next;\n   };\n\n   typedef multi_index_container<\n      operation_history_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >\n      >\n   > operation_history_multi_index_type;\n\n   typedef generic_index<operation_history_object, operation_history_multi_index_type> operation_history_index;\n\n   struct by_seq;\n   struct by_op;\n   struct by_opid;\n\n   typedef multi_index_container<\n      account_transaction_history_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_unique< tag<by_seq>,\n            composite_key< account_transaction_history_object,\n               member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,\n               member< account_transaction_history_object, uint64_t, &account_transaction_history_object::sequence>\n            >\n         >,\n         ordered_unique< tag<by_op>,\n            composite_key< account_transaction_history_object,\n               member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,\n               member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>\n            >\n         >,\n         ordered_non_unique< tag<by_opid>,\n            member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>\n         >\n      >\n   > account_transaction_history_multi_index_type;\n\n   typedef generic_index<account_transaction_history_object, account_transaction_history_multi_index_type> account_transaction_history_index;\n\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::operation_history_object)\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::account_transaction_history_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::operation_history_object )\nFC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::operation_history_object )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::account_transaction_history_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/proposal_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/evaluator.hpp>\n\nnamespace graphene { namespace chain {\n\n   class hardfork_visitor_1479\n   {\n   public:\n      typedef void result_type;\n\n      uint64_t max_update_instance = 0;\n      uint64_t nested_update_count = 0;\n\n      template<typename T>\n      void operator()(const T &v) const {}\n\n      void operator()(const proposal_update_operation &v);\n\n      void operator()(const proposal_delete_operation &v);\n\n      // loop and self visit in proposals\n      void operator()(const graphene::chain::proposal_create_operation &v);\n   };\n\n   class proposal_create_evaluator : public evaluator<proposal_create_evaluator>\n   {\n      public:\n         typedef proposal_create_operation operation_type;\n\n         void_result do_evaluate( const proposal_create_operation& o );\n         object_id_type do_apply( const proposal_create_operation& o );\n\n         transaction _proposed_trx;\n         flat_set<account_id_type> _required_active_auths;\n         flat_set<account_id_type> _required_owner_auths;\n\n         hardfork_visitor_1479 vtor_1479;\n   };\n\n   class proposal_update_evaluator : public evaluator<proposal_update_evaluator>\n   {\n      public:\n         typedef proposal_update_operation operation_type;\n\n         void_result do_evaluate( const proposal_update_operation& o );\n         void_result do_apply( const proposal_update_operation& o );\n\n         const proposal_object* _proposal = nullptr;\n         processed_transaction _processed_transaction;\n         bool _executed_proposal = false;\n         bool _proposal_failed = false;\n   };\n\n   class proposal_delete_evaluator : public evaluator<proposal_delete_evaluator>\n   {\n      public:\n         typedef proposal_delete_operation operation_type;\n\n         void_result do_evaluate( const proposal_delete_operation& o );\n         void_result do_apply(const proposal_delete_operation&);\n\n         const proposal_object* _proposal = nullptr;\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/proposal_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n#include <graphene/protocol/transaction.hpp>\n#include <graphene/db/generic_index.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n   class database;\n\n/**\n *  @brief tracks the approval of a partially approved transaction \n *  @ingroup object\n *  @ingroup protocol\n */\nclass proposal_object : public abstract_object<proposal_object>\n{\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id = proposal_object_type;\n\n      time_point_sec                expiration_time;\n      optional<time_point_sec>      review_period_time;\n      transaction                   proposed_transaction;\n      flat_set<account_id_type>     required_active_approvals;\n      flat_set<account_id_type>     available_active_approvals;\n      flat_set<account_id_type>     required_owner_approvals;\n      flat_set<account_id_type>     available_owner_approvals;\n      flat_set<public_key_type>     available_key_approvals;\n      account_id_type               proposer;\n      std::string                   fail_reason;\n\n      bool is_authorized_to_execute(database& db) const;\n};\n\n/**\n *  @brief tracks all of the proposal objects that requrie approval of\n *  an individual account.   \n *\n *  @ingroup object\n *  @ingroup protocol\n *\n *  This is a secondary index on the proposal_index\n *\n *  @note the set of required approvals is constant\n */\nclass required_approval_index : public secondary_index\n{\n   public:\n      virtual void object_inserted( const object& obj ) override;\n      virtual void object_removed( const object& obj ) override;\n      virtual void about_to_modify( const object& before ) override;\n      virtual void object_modified( const object& after  ) override;\n\n      map<account_id_type, set<proposal_id_type> > _account_to_proposals;\n\n   private:\n      void remove( account_id_type a, proposal_id_type p );\n      void insert_or_remove_delta( proposal_id_type p, const flat_set<account_id_type>& before,\n                                   const flat_set<account_id_type>& after );\n      flat_set<account_id_type> available_active_before_modify;\n      flat_set<account_id_type> available_owner_before_modify;\n};\n\nstruct by_expiration{};\ntypedef boost::multi_index_container<\n   proposal_object,\n   indexed_by<\n      ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,\n      //ordered_non_unique< tag< by_expiration >, member< proposal_object, time_point_sec, &proposal_object::expiration_time > >\n      ordered_unique<tag<by_expiration>,\n         composite_key<proposal_object,\n            member<proposal_object, time_point_sec, &proposal_object::expiration_time>,\n            member< object, object_id_type, &object::id >\n         >\n      >\n   >\n> proposal_multi_index_container;\ntypedef generic_index<proposal_object, proposal_multi_index_container> proposal_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::proposal_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::proposal_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::proposal_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/special_authority_evaluation.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/special_authority.hpp>\n\nnamespace graphene { namespace chain {\n\nclass database;\nusing namespace protocol;\n\nvoid evaluate_special_authority( const database& db, const special_authority& auth );\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/special_authority_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n * special_authority_object only exists to help with a specific indexing problem.\n * We want to be able to iterate over all accounts that contain a special authority.\n * However, accounts which have a special_authority are very rare.  So rather\n * than indexing account_object by the special_authority fields (requiring additional\n * bookkeeping for every account), we instead maintain a special_authority_object\n * pointing to each account which has special_authority (requiring additional\n * bookkeeping only for every account which has special_authority).\n *\n * This class is an implementation detail.\n */\n\nclass special_authority_object : public graphene::db::abstract_object<special_authority_object>\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id = impl_special_authority_object_type;\n\n      account_id_type account;\n};\n\nstruct by_account;\n\ntypedef multi_index_container<\n   special_authority_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_account>, member< special_authority_object, account_id_type, &special_authority_object::account> >\n   >\n> special_authority_multi_index_type;\n\ntypedef generic_index< special_authority_object, special_authority_multi_index_type > special_authority_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::special_authority_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::special_authority_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::special_authority_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/ticket_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n\n#include <graphene/protocol/ticket.hpp>\n\nnamespace graphene { namespace chain {\n\n   class ticket_object;\n\n   class ticket_create_evaluator : public evaluator<ticket_create_evaluator>\n   {\n      public:\n         typedef ticket_create_operation operation_type;\n\n         void_result do_evaluate( const ticket_create_operation& op );\n         object_id_type do_apply( const ticket_create_operation& op );\n   };\n\n   class ticket_update_evaluator : public evaluator<ticket_update_evaluator>\n   {\n      public:\n         typedef ticket_update_operation operation_type;\n\n         void_result do_evaluate( const ticket_update_operation& op );\n         generic_operation_result do_apply( const ticket_update_operation& op );\n\n         const ticket_object* _ticket = nullptr;\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/ticket_object.hpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/ticket.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n\nusing namespace graphene::db;\n\nusing graphene::protocol::ticket_type;\n\n/// Status of a ticket\nenum ticket_status\n{\n   charging,\n   stable,\n   withdrawing,\n   TICKET_STATUS_COUNT\n};\n\n/**\n *  @brief a ticket for governance voting\n *  @ingroup object\n *  @ingroup protocol\n *\n */\nclass ticket_object : public abstract_object<ticket_object>\n{\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id  = ticket_object_type;\n\n      account_id_type  account;      ///< The account who owns the ticket\n      ticket_type      target_type;  ///< The target type of the ticket\n      asset            amount;       ///< The token type and amount in the ticket\n\n      ticket_type      current_type; ///< The current type of the ticket\n      ticket_status    status;       ///< The status of the ticket\n      share_type       value;        ///< The current value of the ticket\n      time_point_sec   next_auto_update_time; ///< The next time that the ticket will be automatically updated\n\n      /// When the account has ever started a downgrade or withdrawal, the scheduled auto-update time is stored here\n      time_point_sec   next_type_downgrade_time = time_point_sec::maximum();\n\n      // Configurations\n      static constexpr uint32_t lock_forever_update_steps = 4;\n      static constexpr uint32_t seconds_per_lock_forever_update_step = 1 * 86400;\n      static constexpr uint32_t seconds_per_charging_step = 1 * 8640;\n      static constexpr uint32_t seconds_to_cancel_charging = 1 * 8640;\n      static uint32_t seconds_to_downgrade( ticket_type i ) {\n         static constexpr uint32_t _seconds_to_downgrade[] = { 180 * 86400, 180 * 86400, 360 * 86400 };\n         return _seconds_to_downgrade[ static_cast<uint8_t>(i) ];\n      }\n      static uint8_t value_multiplier( ticket_type i ) {\n         static constexpr uint32_t _value_multiplier[] = { 1, 2, 4, 8, 8, 0 };\n         return _value_multiplier[ static_cast<uint8_t>(i) ];\n      }\n\n      /// Initialize member variables for a ticket newly created from account balance\n      void init_new( time_point_sec now, account_id_type new_account,\n                     ticket_type new_target_type, const asset& new_amount );\n\n      /// Initialize member variables for a ticket split from another ticket\n      void init_split( time_point_sec now, const ticket_object& old_ticket,\n                       ticket_type new_target_type, const asset& new_amount );\n\n      /// Set a new target type and update member variables accordingly\n      void update_target_type( time_point_sec now, ticket_type new_target_type );\n\n      /// Adjust amount and update member variables accordingly\n      void adjust_amount( const asset& delta_amount );\n\n      /// Update the ticket when it's time\n      void auto_update();\n\n   private:\n      /// Recalculate value of the ticket\n      void update_value();\n\n};\n\nstruct by_next_update;\nstruct by_account_type;\n\n/**\n* @ingroup object_index\n*/\ntypedef multi_index_container<\n   ticket_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_next_update>,\n         composite_key< ticket_object,\n            member< ticket_object, time_point_sec, &ticket_object::next_auto_update_time>,\n            member< object, object_id_type, &object::id>\n         >\n      >,\n      ordered_unique< tag<by_account_type>,\n         composite_key< ticket_object,\n            member< ticket_object, account_id_type, &ticket_object::account>,\n            member< ticket_object, ticket_type, &ticket_object::current_type>,\n            member< object, object_id_type, &object::id>\n         >\n      >\n   >\n> ticket_multi_index_type;\n\n/**\n* @ingroup object_index\n*/\ntypedef generic_index<ticket_object, ticket_multi_index_type> ticket_index;\n\n} } // graphene::chain\n\nFC_REFLECT_ENUM( graphene::chain::ticket_status,\n                 (charging)(stable)(withdrawing)(TICKET_STATUS_COUNT) )\n\nMAP_OBJECT_ID_TO_TYPE( graphene::chain::ticket_object )\n\nFC_REFLECT_TYPENAME( graphene::chain::ticket_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::ticket_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/transaction_evaluation_state.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n\nnamespace graphene {\nnamespace protocol { class signed_transaction; }\nnamespace chain {\n   class database;\n   using protocol::signed_transaction;\n\n   /**\n    *  Place holder for state tracked while processing a transaction. This class provides helper methods that are\n    *  common to many different operations and also tracks which keys have signed the transaction\n    */\n   class transaction_evaluation_state\n   {\n      public:\n         transaction_evaluation_state( database* db = nullptr )\n         :_db(db){}\n\n\n         database& db()const { assert( _db ); return *_db; }\n         vector<operation_result> operation_results;\n\n         const signed_transaction*        _trx = nullptr;\n         database*                        _db = nullptr;\n         bool                             _is_proposed_trx = false;\n         bool                             skip_fee = false;\n         bool                             skip_fee_schedule_check = false;\n   };\n} } // namespace graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/transaction_history_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/transaction.hpp>\n#include <graphene/db/generic_index.hpp>\n\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace graphene::db;\n   using boost::multi_index_container;\n   using namespace boost::multi_index;\n   /**\n    * The purpose of this object is to enable the detection of duplicate transactions. When a transaction is included\n    * in a block a transaction_history_object is added. At the end of block processing all transaction_history_objects that\n    * have expired can be removed from the index.\n    */\n   class transaction_history_object : public abstract_object<transaction_history_object>\n   {\n      public:\n         static const uint8_t space_id = implementation_ids;\n         static const uint8_t type_id  = impl_transaction_history_object_type;\n\n         signed_transaction  trx;\n         transaction_id_type trx_id;\n\n         time_point_sec get_expiration()const { return trx.expiration; }\n   };\n\n   struct by_expiration;\n   struct by_trx_id;\n   typedef multi_index_container<\n      transaction_history_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         hashed_unique< tag<by_trx_id>, BOOST_MULTI_INDEX_MEMBER(transaction_history_object, transaction_id_type, trx_id),\n                        std::hash<transaction_id_type> >,\n         ordered_non_unique< tag<by_expiration>, const_mem_fun< transaction_history_object, time_point_sec,\n                                                                &transaction_history_object::get_expiration > >\n      >\n   > transaction_multi_index_type;\n\n   typedef generic_index<transaction_history_object, transaction_multi_index_type> transaction_index;\n} }\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::transaction_history_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::transaction_history_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::transaction_history_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/transfer_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace chain {\n\n   class transfer_evaluator : public evaluator<transfer_evaluator>\n   {\n      public:\n         typedef transfer_operation operation_type;\n\n         void_result do_evaluate( const transfer_operation& o );\n         void_result do_apply( const transfer_operation& o );\n   };\n\n   class override_transfer_evaluator : public evaluator<override_transfer_evaluator>\n   {\n      public:\n         typedef override_transfer_operation operation_type;\n\n         void_result do_evaluate( const override_transfer_operation& o );\n         void_result do_apply( const override_transfer_operation& o );\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/types.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace chain { using namespace protocol; } }\n\nGRAPHENE_DEFINE_IDS(chain, implementation_ids, impl_,\n                    (global_property)\n                    (dynamic_global_property)\n                    (reserved0)\n                    (asset_dynamic_data)\n                    (asset_bitasset_data)\n                    (account_balance)\n                    (account_statistics)\n                    (transaction_history)\n                    (block_summary)\n                    (account_transaction_history)\n                    (blinded_balance)\n                    (chain_property)\n                    (witness_schedule)\n                    (budget_record)\n                    (special_authority)\n                    (buyback)\n                    (fba_accumulator)\n                    (collateral_bid))\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/evaluator.hpp>\n\nnamespace graphene { namespace chain {\n\nclass vesting_balance_create_evaluator;\nclass vesting_balance_withdraw_evaluator;\n\nclass vesting_balance_create_evaluator : public evaluator<vesting_balance_create_evaluator>\n{\n    public:\n        typedef vesting_balance_create_operation operation_type;\n\n        void_result do_evaluate( const vesting_balance_create_operation& op );\n        object_id_type do_apply( const vesting_balance_create_operation& op );\n};\n\nclass vesting_balance_withdraw_evaluator : public evaluator<vesting_balance_withdraw_evaluator>\n{\n    public:\n        typedef vesting_balance_withdraw_operation operation_type;\n\n        void_result do_evaluate( const vesting_balance_withdraw_operation& op );\n        void_result do_apply( const vesting_balance_withdraw_operation& op );\n};\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/vesting_balance_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/db/generic_index.hpp>\n#include <graphene/protocol/asset.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n#include <boost/multi_index/identity.hpp>\n\n#include <fc/static_variant.hpp>\n#include <fc/uint128.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace graphene::db;\n   using namespace graphene::protocol;\n\n   struct vesting_policy_context\n   {\n      vesting_policy_context(\n         asset _balance,\n         fc::time_point_sec _now,\n         asset _amount)\n         : balance(_balance), now(_now), amount(_amount) {}\n\n      asset              balance;\n      fc::time_point_sec now;\n      asset              amount;\n   };\n\n   /**\n    * @brief Linear vesting balance with cliff\n    *\n    * This vesting balance type is used to mimic traditional stock vesting contracts where\n    * each day a certain amount vests until it is fully matured.\n    *\n    * @note New funds may not be added to a linear vesting balance.\n    */\n   struct linear_vesting_policy\n   {\n      /// This is the time at which funds begin vesting.\n      fc::time_point_sec begin_timestamp;\n      /// No amount may be withdrawn before this many seconds of the vesting period have elapsed.\n      uint32_t vesting_cliff_seconds = 0;\n      /// Duration of the vesting period, in seconds. Must be greater than 0 and greater than vesting_cliff_seconds.\n      uint32_t vesting_duration_seconds = 0;\n      /// The total amount of asset to vest.\n      share_type begin_balance;\n\n      asset get_allowed_withdraw(const vesting_policy_context& ctx)const;\n      bool is_deposit_allowed(const vesting_policy_context& ctx)const;\n      bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; }\n      bool is_withdraw_allowed(const vesting_policy_context& ctx)const;\n      void on_deposit(const vesting_policy_context& ctx);\n      void on_deposit_vested(const vesting_policy_context&)\n      { FC_THROW( \"May not deposit vested into a linear vesting balance.\" ); }\n      void on_withdraw(const vesting_policy_context& ctx);\n   };\n\n   /**\n    * @brief defines vesting in terms of coin-days accrued which allows for dynamic deposit/withdraw\n    *\n    * The economic effect of this vesting policy is to require a certain amount of \"interest\" to accrue\n    * before the full balance may be withdrawn.  Interest accrues as coindays (balance * length held).  If\n    * some of the balance is withdrawn, the remaining balance must be held longer.\n    */\n   struct cdd_vesting_policy\n   {\n      uint32_t                         vesting_seconds = 0;\n      fc::uint128_t                    coin_seconds_earned;\n      /** while coindays may accrue over time, none may be claimed before first_claim date */\n      fc::time_point_sec               start_claim;\n      fc::time_point_sec               coin_seconds_earned_last_update;\n\n      /**\n       * Compute coin_seconds_earned.  Used to\n       * non-destructively figure out how many coin seconds\n       * are available.\n       */\n      fc::uint128_t compute_coin_seconds_earned(const vesting_policy_context& ctx)const;\n\n      /**\n       * Update coin_seconds_earned and\n       * coin_seconds_earned_last_update fields; called by both\n       * on_deposit() and on_withdraw().\n       */\n      void update_coin_seconds_earned(const vesting_policy_context& ctx);\n\n      asset get_allowed_withdraw(const vesting_policy_context& ctx)const;\n      bool is_deposit_allowed(const vesting_policy_context& ctx)const;\n      bool is_deposit_vested_allowed(const vesting_policy_context& ctx)const;\n      bool is_withdraw_allowed(const vesting_policy_context& ctx)const;\n      void on_deposit(const vesting_policy_context& ctx);\n      void on_deposit_vested(const vesting_policy_context& ctx);\n      void on_withdraw(const vesting_policy_context& ctx);\n   };\n\n     /**\n    * @brief instant vesting policy\n    *\n    * This policy allows to withdraw everything that is on a balance immediately\n    *\n    */\n   struct instant_vesting_policy\n   {\n      asset get_allowed_withdraw(const vesting_policy_context& ctx)const;\n      bool is_deposit_allowed(const vesting_policy_context& ctx)const;\n      bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; }\n      bool is_withdraw_allowed(const vesting_policy_context& ctx)const;\n      void on_deposit(const vesting_policy_context& ctx);\n      void on_deposit_vested(const vesting_policy_context&);\n      void on_withdraw(const vesting_policy_context& ctx);\n   };\n\n   typedef fc::static_variant<\n      linear_vesting_policy,\n      cdd_vesting_policy,\n      instant_vesting_policy\n      > vesting_policy;\n\n   enum class vesting_balance_type {   unspecified,\n                                       cashback,\n                                       worker,\n                                       witness,\n                                       market_fee_sharing };\n   /**\n    * Vesting balance object is a balance that is locked by the blockchain for a period of time.\n    */\n   class vesting_balance_object : public abstract_object<vesting_balance_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id = vesting_balance_object_type;\n\n         /// Account which owns and may withdraw from this vesting balance\n         account_id_type owner;\n         /// Total amount remaining in this vesting balance\n         /// Includes the unvested funds, and the vested funds which have not yet been withdrawn\n         asset balance;\n         /// The vesting policy stores details on when funds vest, and controls when they may be withdrawn\n         vesting_policy policy;\n         /// type of the vesting balance\n         vesting_balance_type balance_type = vesting_balance_type::unspecified;\n\n         vesting_balance_object() {}\n\n         ///@brief Deposit amount into vesting balance, requiring it to vest before withdrawal\n         void deposit(const fc::time_point_sec& now, const asset& amount);\n         bool is_deposit_allowed(const fc::time_point_sec& now, const asset& amount)const;\n\n         /// @brief Deposit amount into vesting balance, making the new funds vest immediately\n         void deposit_vested(const fc::time_point_sec& now, const asset& amount);\n         bool is_deposit_vested_allowed(const fc::time_point_sec& now, const asset& amount)const;\n\n         /**\n          * Used to remove a vesting balance from the VBO. As well as the\n          * balance field, coin_seconds_earned and\n          * coin_seconds_earned_last_update fields are updated.\n          *\n          * The money doesn't \"go\" anywhere; the caller is responsible for\n          * crediting it to the proper account.\n          */\n         void withdraw(const fc::time_point_sec& now, const asset& amount);\n         bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const;\n\n         /**\n          * Get amount of allowed withdrawal.\n          */\n         asset get_allowed_withdraw(const time_point_sec& now)const;\n   };\n   /**\n    * @ingroup object_index\n    */\n   struct by_account;\n   // by_vesting_type index MUST NOT be used for iterating because order is not well-defined.\n   struct by_vesting_type;\n\nnamespace detail {\n\n   /**\n      Calculate a hash for account_id_type and asset_id.\n      Use 48 bit value (see object_id.hpp) for account_id and XOR it with 24 bit for asset_id\n   */\n   inline uint64_t vbo_mfs_hash(const account_id_type& account_id, const asset_id_type& asset_id)\n   {\n      return (asset_id.instance.value << 40) ^ account_id.instance.value;\n   }\n\n   /**\n    * Used as CompatibleHash\n      Calculate a hash vesting_balance_object\n      if vesting_balance_object.balance_type is market_fee_sharing\n         calculate has as vbo_mfs_hash(vesting_balance_object.owner, hash(vbo.balance.asset_id) (see vbo_mfs_hash)\n      otherwise: hash_value(vesting_balance_object.id);\n   */\n   struct vesting_balance_object_hash\n   {\n      uint64_t operator()(const vesting_balance_object& vbo) const\n      {\n         if ( vbo.balance_type == vesting_balance_type::market_fee_sharing )\n         {\n            return vbo_mfs_hash(vbo.owner, vbo.balance.asset_id);\n         }\n         return hash_value(vbo.id);\n      }\n   };\n\n   /**\n    * Used as CompatiblePred\n    * Compares two vesting_balance_objects\n    * if vesting_balance_object.balance_type is a market_fee_sharing\n    *    compare owners' ids and assets' ids\n    * otherwise: vesting_balance_object.id\n   */\n   struct vesting_balance_object_equal\n   {\n      bool operator() (const vesting_balance_object& lhs, const vesting_balance_object& rhs) const\n      {\n         if ( ( lhs.balance_type == vesting_balance_type::market_fee_sharing ) &&\n              ( lhs.balance_type == rhs.balance_type ) &&\n              ( lhs.owner == rhs.owner ) &&\n              ( lhs.balance.asset_id == rhs.balance.asset_id)\n            )\n         {\n               return true;\n         }\n         return ( lhs.id == rhs.id );\n      }\n   };\n} // detail\n\n   typedef multi_index_container<\n      vesting_balance_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id >\n         >,\n         ordered_non_unique< tag<by_account>,\n            member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>\n         >,\n         hashed_unique< tag<by_vesting_type>,\n            identity<vesting_balance_object>,\n            detail::vesting_balance_object_hash,\n            detail::vesting_balance_object_equal\n         >\n      >\n   > vesting_balance_multi_index_type;\n   /**\n    * @ingroup object_index\n    */\n   typedef generic_index<vesting_balance_object, vesting_balance_multi_index_type> vesting_balance_index;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::vesting_balance_object)\n\nFC_REFLECT(graphene::chain::linear_vesting_policy,\n           (begin_timestamp)\n           (vesting_cliff_seconds)\n           (vesting_duration_seconds)\n           (begin_balance)\n          )\n\nFC_REFLECT(graphene::chain::cdd_vesting_policy,\n           (vesting_seconds)\n           (start_claim)\n           (coin_seconds_earned)\n           (coin_seconds_earned_last_update)\n          )\n\nFC_REFLECT_EMPTY( graphene::chain::instant_vesting_policy )\n\nFC_REFLECT_TYPENAME( graphene::chain::vesting_policy )\n\nFC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object),\n                   (owner)\n                   (balance)\n                   (policy)\n                   (balance_type)\n                  )\n\nFC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (unspecified)(cashback)(worker)(witness)(market_fee_sharing) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::linear_vesting_policy )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::cdd_vesting_policy )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::vesting_balance_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/vote_count.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n\n#include <graphene/protocol/authority.hpp>\n\nnamespace graphene { namespace chain {\n\n/**\n * Keep track of vote totals in internal authority object.  See #533.\n */\nstruct vote_counter\n{\n   template< typename Component >\n   void add( Component who, uint64_t votes )\n   {\n      if( votes == 0 )\n         return;\n      assert( votes <= last_votes );\n      last_votes = votes;\n      if( bitshift == -1 )\n         bitshift = std::max(int(boost::multiprecision::detail::find_msb( votes )) - 15, 0);\n      uint64_t scaled_votes = std::max( votes >> bitshift, uint64_t(1) );\n      assert( scaled_votes <= std::numeric_limits<weight_type>::max() );\n      total_votes += scaled_votes;\n      assert( total_votes <= std::numeric_limits<uint32_t>::max() );\n      auth.add_authority( who, weight_type( scaled_votes ) );\n   }\n\n   /**\n    * Write into out_auth, but only if we have at least one member.\n    */\n   void finish( authority& out_auth )\n   {\n      if( total_votes == 0 )\n         return;\n      assert( total_votes <= std::numeric_limits<uint32_t>::max() );\n      uint32_t weight = uint32_t( total_votes );\n      weight = (weight >> 1)+1;\n      auth.weight_threshold = weight;\n      out_auth = auth;\n   }\n\n   bool is_empty()const\n   {\n      return (total_votes == 0);\n   }\n\n   uint64_t last_votes = std::numeric_limits<uint64_t>::max();\n   uint64_t total_votes = 0;\n   int8_t bitshift = -1;\n   authority auth;\n};\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/withdraw_permission_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/evaluator.hpp>\n\nnamespace graphene { namespace chain {\n\nclass withdraw_permission_create_evaluator : public evaluator<withdraw_permission_create_evaluator>\n{\npublic:\n   typedef withdraw_permission_create_operation operation_type;\n\n   void_result do_evaluate( const operation_type& op );\n   object_id_type do_apply( const operation_type& op );\n};\n\nclass withdraw_permission_claim_evaluator : public evaluator<withdraw_permission_claim_evaluator>\n{\npublic:\n   typedef withdraw_permission_claim_operation operation_type;\n\n   void_result do_evaluate( const operation_type& op );\n   void_result do_apply( const operation_type& op );\n};\n\nclass withdraw_permission_update_evaluator : public evaluator<withdraw_permission_update_evaluator>\n{\npublic:\n   typedef withdraw_permission_update_operation operation_type;\n\n   void_result do_evaluate( const operation_type& op );\n   void_result do_apply( const operation_type& op );\n};\n\nclass withdraw_permission_delete_evaluator : public evaluator<withdraw_permission_delete_evaluator>\n{\npublic:\n   typedef withdraw_permission_delete_operation operation_type;\n\n   void_result do_evaluate( const operation_type& op );\n   void_result do_apply( const operation_type& op );\n};\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/withdraw_permission_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/asset.hpp>\n#include <graphene/db/generic_index.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace graphene::protocol;\n\n  /**\n   * @class withdraw_permission_object\n   * @brief Grants another account authority to withdraw a limited amount of funds per interval\n   *\n   * The primary purpose of this object is to enable recurring payments on the blockchain. An account which wishes to\n   * process a recurring payment may use a @ref withdraw_permission_claim_operation to reference an object of this type\n   * and withdraw up to @ref withdrawal_limit from @ref withdraw_from_account. Only @ref authorized_account may do\n   * this. Any number of withdrawals may be made so long as the total amount withdrawn per period does not exceed the\n   * limit for any given period.\n   */\n  class withdraw_permission_object : public graphene::db::abstract_object<withdraw_permission_object>\n  {\n     public:\n        static const uint8_t space_id = protocol_ids;\n        static const uint8_t type_id  = withdraw_permission_object_type;\n\n        /// The account authorizing @ref authorized_account to withdraw from it\n        account_id_type    withdraw_from_account;\n        /// The account authorized to make withdrawals from @ref withdraw_from_account\n        account_id_type    authorized_account;\n        /// The maximum amount which may be withdrawn per period. All withdrawals must be of this asset type\n        asset              withdrawal_limit;\n        /// The duration of a withdrawal period in seconds\n        uint32_t           withdrawal_period_sec = 0;\n       /***\n        * The beginning of the next withdrawal period\n        * WARNING: Due to caching, this value does not always represent the start of the next or current period (because it is only updated after a withdrawal operation such as claim).  For the latest current period, use current_period().\n        */\n        time_point_sec     period_start_time;\n        /// The time at which this withdraw permission expires\n        time_point_sec     expiration;\n\n       /***\n        * Tracks the total amount\n        * WARNING: Due to caching, this value does not always represent the total amount claimed during the current period; it may represent what was claimed during the last claimed period (because it is only updated after a withdrawal operation such as claim).  For the latest current period, use current_period().\n        */\n        share_type         claimed_this_period;\n\n       /***\n        * Determine how much is still available to be claimed during the period that contains a time of interest.  This object and function is mainly intended to be used with the \"current\" time as a parameter.  The current time can be obtained from the time of the current head of the blockchain.\n        */\n        asset              available_this_period( fc::time_point_sec current_time )const\n        {\n           if( current_time >= period_start_time + withdrawal_period_sec )\n              return withdrawal_limit;\n           return asset(\n              ( withdrawal_limit.amount > claimed_this_period )\n              ? withdrawal_limit.amount - claimed_this_period\n              : 0, withdrawal_limit.asset_id );\n        }\n   };\n\n   struct by_from;\n   struct by_authorized;\n   struct by_expiration;\n\n   typedef multi_index_container<\n      withdraw_permission_object,\n      indexed_by<\n         ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n         ordered_unique< tag<by_from>,\n            composite_key< withdraw_permission_object,\n               member<withdraw_permission_object, account_id_type, &withdraw_permission_object::withdraw_from_account>,\n               member< object, object_id_type, &object::id >\n            >\n         >,\n         ordered_unique< tag<by_authorized>,\n            composite_key< withdraw_permission_object,\n               member<withdraw_permission_object, account_id_type, &withdraw_permission_object::authorized_account>,\n               member< object, object_id_type, &object::id >\n            >\n         >,\n         ordered_unique< tag<by_expiration>,\n            composite_key< withdraw_permission_object,\n               member<withdraw_permission_object, time_point_sec, &withdraw_permission_object::expiration>,\n               member< object, object_id_type, &object::id >\n            >\n         >\n      >\n   > withdraw_permission_object_multi_index_type;\n\n   typedef generic_index<withdraw_permission_object, withdraw_permission_object_multi_index_type> withdraw_permission_index;\n\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::withdraw_permission_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::withdraw_permission_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/witness_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/witness_object.hpp>\n\nnamespace graphene { namespace chain {\n\n   class witness_create_evaluator : public evaluator<witness_create_evaluator>\n   {\n      public:\n         typedef witness_create_operation operation_type;\n\n         void_result do_evaluate( const witness_create_operation& o );\n         object_id_type do_apply( const witness_create_operation& o );\n   };\n\n   class witness_update_evaluator : public evaluator<witness_update_evaluator>\n   {\n      public:\n         typedef witness_update_operation operation_type;\n\n         void_result do_evaluate( const witness_update_operation& o );\n         void_result do_apply( const witness_update_operation& o );\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/witness_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/asset.hpp>\n#include <graphene/db/generic_index.hpp>\n\nnamespace graphene { namespace chain {\n   using namespace graphene::db;\n\n   class witness_object : public abstract_object<witness_object>\n   {\n      public:\n         static const uint8_t space_id = protocol_ids;\n         static const uint8_t type_id = witness_object_type;\n\n         account_id_type  witness_account;\n         uint64_t         last_aslot = 0;\n         public_key_type  signing_key;\n         optional< vesting_balance_id_type > pay_vb;\n         vote_id_type     vote_id;\n         uint64_t         total_votes = 0;\n         string           url;\n         int64_t          total_missed = 0;\n         uint32_t         last_confirmed_block_num = 0;\n\n         witness_object() : vote_id(vote_id_type::witness) {}\n   };\n\n   struct by_account;\n   struct by_vote_id;\n   struct by_last_block;\n   using witness_multi_index_type = multi_index_container<\n      witness_object,\n      indexed_by<\n         ordered_unique< tag<by_id>,\n            member<object, object_id_type, &object::id>\n         >,\n         ordered_unique< tag<by_account>,\n            member<witness_object, account_id_type, &witness_object::witness_account>\n         >,\n         ordered_unique< tag<by_vote_id>,\n            member<witness_object, vote_id_type, &witness_object::vote_id>\n         >\n      >\n   >;\n   using witness_index = generic_index<witness_object, witness_multi_index_type>;\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::witness_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::witness_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::witness_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/witness_schedule_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/types.hpp>\n#include <graphene/db/generic_index.hpp>\n\nnamespace graphene { namespace chain {\n\nclass witness_schedule_object : public graphene::db::abstract_object<witness_schedule_object>\n{\n   public:\n      static const uint8_t space_id = implementation_ids;\n      static const uint8_t type_id = impl_witness_schedule_object_type;\n\n      vector< witness_id_type > current_shuffled_witnesses;\n};\n\n} }\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::witness_schedule_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::witness_schedule_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::witness_schedule_object )\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/worker_evaluator.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/evaluator.hpp>\n\nnamespace graphene { namespace chain {\n\n   class worker_create_evaluator : public evaluator<worker_create_evaluator>\n   {\n      public:\n         typedef worker_create_operation operation_type;\n\n         void_result do_evaluate( const operation_type& o );\n         object_id_type do_apply( const operation_type& o );\n   };\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/include/graphene/chain/worker_object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/chain/types.hpp>\n#include <graphene/db/generic_index.hpp>\n#include <graphene/protocol/vote.hpp>\n\nnamespace graphene { namespace chain {\nclass database;\n\n/**\n  * @defgroup worker_types Implementations of the various worker types in the system\n  *\n  * The system has various worker types, which do different things with the money they are paid. These worker types\n  * and their semantics are specified here.\n  *\n  * All worker types exist as a struct containing the data this worker needs to evaluate, as well as a method\n  * pay_worker, which takes a pay amount and a non-const database reference, and applies the worker's specific pay\n  * semantics to the worker_type struct and/or the database. Furthermore, all worker types have an initializer,\n  * which is a struct containing the data needed to create that kind of worker.\n  *\n  * Each initializer type has a method, init, which takes a non-const database reference, a const reference to the\n  * worker object being created, and a non-const reference to the specific *_worker_type object to initialize. The\n  * init method creates any further objects, and initializes the worker_type object as necessary according to the\n  * semantics of that particular worker type.\n  *\n  * To create a new worker type, define a my_new_worker_type struct with a pay_worker method which updates the\n  * my_new_worker_type object and/or the database. Create a my_new_worker_type::initializer struct with an init\n  * method and any data members necessary to create a new worker of this type. Reflect my_new_worker_type and\n  * my_new_worker_type::initializer into FC's type system, and add them to @ref worker_type and @ref\n  * worker_initializer respectively. Make sure the order of types in @ref worker_type and @ref worker_initializer\n  * remains the same.\n  * @{\n  */\n/**\n * @brief A worker who returns all of his pay to the reserve\n *\n * This worker type pays everything he receives back to the network's reserve funds pool.\n */\nstruct refund_worker_type\n{\n   /// Record of how much this worker has burned in his lifetime\n   share_type total_burned;\n\n   void pay_worker(share_type pay, database&);\n};\n\n/**\n * @brief A worker who sends his pay to a vesting balance\n *\n * This worker type takes all of his pay and places it into a vesting balance\n */\nstruct vesting_balance_worker_type\n{\n   /// The balance this worker pays into\n   vesting_balance_id_type balance;\n\n   void pay_worker(share_type pay, database& db);\n};\n\n/**\n * @brief A worker who permanently destroys all of his pay\n *\n * This worker sends all pay he receives to the null account.\n */\nstruct burn_worker_type\n{\n   /// Record of how much this worker has burned in his lifetime\n   share_type total_burned;\n\n   void pay_worker(share_type pay, database&);\n};\n///@}\n\n// The ordering of types in these two static variants MUST be the same.\ntypedef static_variant<\n   refund_worker_type,\n   vesting_balance_worker_type,\n   burn_worker_type\n> worker_type;\n\n\n/**\n * @brief Worker object contains the details of a blockchain worker. See @ref workers for details.\n */\nclass worker_object : public abstract_object<worker_object>\n{\n   public:\n      static const uint8_t space_id = protocol_ids;\n      static const uint8_t type_id =  worker_object_type;\n\n      /// ID of the account which owns this worker\n      account_id_type worker_account;\n      /// Time at which this worker begins receiving pay, if elected\n      time_point_sec work_begin_date;\n      /// Time at which this worker will cease to receive pay. Worker will be deleted at this time\n      time_point_sec work_end_date;\n      /// Amount in CORE this worker will be paid each day\n      share_type daily_pay;\n      /// ID of this worker's pay balance\n      worker_type worker;\n      /// Human-readable name for the worker\n      string name;\n      /// URL to a web page representing this worker\n      string url;\n\n      /// Voting ID which represents approval of this worker\n      vote_id_type vote_for;\n      /// Voting ID which represents disapproval of this worker\n      vote_id_type vote_against;\n\n      uint64_t total_votes_for = 0;\n      uint64_t total_votes_against = 0;\n\n      bool is_active(fc::time_point_sec now)const {\n         return now >= work_begin_date && now <= work_end_date;\n      }\n\n      share_type approving_stake()const {\n         return int64_t( total_votes_for ) - int64_t( total_votes_against );\n      }\n};\n\nstruct by_account;\nstruct by_vote_for;\nstruct by_vote_against;\nstruct by_end_date;\ntypedef multi_index_container<\n   worker_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_non_unique< tag<by_account>, member< worker_object, account_id_type, &worker_object::worker_account > >,\n      ordered_unique< tag<by_vote_for>, member< worker_object, vote_id_type, &worker_object::vote_for > >,\n      ordered_unique< tag<by_vote_against>, member< worker_object, vote_id_type, &worker_object::vote_against > >,\n      ordered_non_unique< tag<by_end_date>, member< worker_object, time_point_sec, &worker_object::work_end_date> >\n   >\n> worker_object_multi_index_type;\n\nusing worker_index = generic_index<worker_object, worker_object_multi_index_type>;\n\n} } // graphene::chain\n\nMAP_OBJECT_ID_TO_TYPE(graphene::chain::worker_object)\n\nFC_REFLECT_TYPENAME( graphene::chain::refund_worker_type )\nFC_REFLECT_TYPENAME( graphene::chain::vesting_balance_worker_type )\nFC_REFLECT_TYPENAME( graphene::chain::burn_worker_type )\nFC_REFLECT_TYPENAME( graphene::chain::worker_type )\nFC_REFLECT_TYPENAME( graphene::chain::worker_object )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::worker_object )\n"
  },
  {
    "path": "libraries/chain/is_authorized_asset.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n\nnamespace graphene { namespace chain {\n\nnamespace detail {\n\nbool _is_authorized_asset(\n   const database& d,\n   const account_object& acct,\n   const asset_object& asset_obj)\n{\n   // committee-account is always allowed to transact after BSIP 86\n   if( HARDFORK_BSIP_86_PASSED( d.head_block_time() ) )\n   {\n      static const object_id_type committee_account_id( GRAPHENE_COMMITTEE_ACCOUNT );\n      if( acct.id == committee_account_id )\n         return true;\n   }\n\n   if( acct.allowed_assets.valid() )\n   {\n      if( acct.allowed_assets->find( asset_obj.id ) == acct.allowed_assets->end() )\n         return false;\n      // must still pass other checks even if it is in allowed_assets\n   }\n\n   for( const auto id : acct.blacklisting_accounts )\n   {\n      if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() )\n         return false;\n   }\n\n   if( asset_obj.options.whitelist_authorities.size() == 0 )\n      return true;\n\n   for( const auto id : acct.whitelisting_accounts )\n   {\n      if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() )\n         return true;\n   }\n\n   return false;\n}\n\n} // detail\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/liquidity_pool_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/liquidity_pool_object.hpp>\n\n#include <graphene/chain/liquidity_pool_evaluator.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n#include <graphene/protocol/liquidity_pool.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result liquidity_pool_create_evaluator::do_evaluate(const liquidity_pool_create_operation& op)\n{ try {\n   const database& d = db();\n   const auto block_time = d.head_block_time();\n\n   FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), \"Not allowed until the LP hardfork\" );\n\n   op.asset_a(d); // Make sure it exists\n   op.asset_b(d); // Make sure it exists\n   _share_asset = &op.share_asset(d);\n\n   FC_ASSERT( _share_asset->issuer == op.account,\n              \"Only the asset owner can set an asset as the share asset of a liquidity pool\" );\n\n   FC_ASSERT( !_share_asset->is_market_issued(),\n              \"Can not specify a market-issued asset as the share asset of a liquidity pool\" );\n\n   FC_ASSERT( !_share_asset->is_liquidity_pool_share_asset(),\n              \"The share asset is already bound to another liquidity pool\" );\n\n   FC_ASSERT( _share_asset->dynamic_data(d).current_supply == 0,\n              \"Current supply of the share asset needs to be zero\" );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\ngeneric_operation_result liquidity_pool_create_evaluator::do_apply(const liquidity_pool_create_operation& op)\n{ try {\n   database& d = db();\n   generic_operation_result result;\n\n   const auto& new_liquidity_pool_object = d.create<liquidity_pool_object>([&op](liquidity_pool_object& obj){\n      obj.asset_a = op.asset_a;\n      obj.asset_b = op.asset_b;\n      obj.share_asset = op.share_asset;\n      obj.taker_fee_percent = op.taker_fee_percent;\n      obj.withdrawal_fee_percent = op.withdrawal_fee_percent;\n   });\n   result.new_objects.insert( new_liquidity_pool_object.id );\n\n   result.updated_objects.insert( _share_asset->id );\n   d.modify( *_share_asset, [&new_liquidity_pool_object](asset_object& ao) {\n      ao.for_liquidity_pool = new_liquidity_pool_object.id;\n   });\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result liquidity_pool_delete_evaluator::do_evaluate(const liquidity_pool_delete_operation& op)\n{ try {\n   const database& d = db();\n\n   _pool = &op.pool(d);\n\n   FC_ASSERT( _pool->balance_a == 0 && _pool->balance_b == 0, \"Can not delete a non-empty pool\" );\n\n   _share_asset = &_pool->share_asset(d);\n\n   FC_ASSERT( _share_asset->issuer == op.account, \"The account is not the owner of the liquidity pool\" );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\ngeneric_operation_result liquidity_pool_delete_evaluator::do_apply(const liquidity_pool_delete_operation& op)\n{ try {\n   database& d = db();\n   generic_operation_result result;\n\n   result.updated_objects.insert( _share_asset->id );\n   d.modify( *_share_asset, [](asset_object& ao) {\n      ao.for_liquidity_pool.reset();\n   });\n\n   result.removed_objects.insert( _pool->id );\n   d.remove( *_pool );\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result liquidity_pool_deposit_evaluator::do_evaluate(const liquidity_pool_deposit_operation& op)\n{ try {\n   const database& d = db();\n\n   _pool = &op.pool(d);\n\n   FC_ASSERT( op.amount_a.asset_id == _pool->asset_a, \"Asset type A mismatch\" );\n   FC_ASSERT( op.amount_b.asset_id == _pool->asset_b, \"Asset type B mismatch\" );\n\n   FC_ASSERT( (_pool->balance_a == 0) == (_pool->balance_b == 0), \"Internal error\" );\n\n   const asset_object& share_asset_obj = _pool->share_asset(d);\n\n   FC_ASSERT( share_asset_obj.can_create_new_supply(), \"Can not create new supply for the share asset\" );\n\n   if( _pool->balance_a == 0 ) // which implies that _pool->balance_b == 0\n   {\n      FC_ASSERT( share_asset_obj.issuer == op.account, \"The initial deposit can only be done by the pool owner\" );\n   }\n\n   _share_asset_dyn_data = &share_asset_obj.dynamic_data(d);\n\n   FC_ASSERT( (_pool->balance_a == 0) == (_share_asset_dyn_data->current_supply == 0), \"Internal error\" );\n\n   FC_ASSERT( _share_asset_dyn_data->current_supply < share_asset_obj.options.max_supply,\n              \"Can not create new supply for the share asset\" );\n\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, share_asset_obj ),\n              \"The account is unauthorized by the share asset\" );\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_a(d) ),\n              \"The account is unauthorized by asset A\" );\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_b(d) ),\n              \"The account is unauthorized by asset B\" );\n\n   if( _pool->balance_a == 0 )\n   {\n      share_type share_amount = std::max( op.amount_a.amount.value, op.amount_b.amount.value );\n      FC_ASSERT( share_amount <= share_asset_obj.options.max_supply,\n                 \"For initial deposit, each amount of the two assets in the pool should not be greater than \"\n                 \"the maximum supply of the share asset\" );\n      _pool_receives_a = op.amount_a;\n      _pool_receives_b = op.amount_b;\n      _account_receives = asset( share_amount, _pool->share_asset );\n   }\n   else\n   {\n      share_type max_new_supply = share_asset_obj.options.max_supply - _share_asset_dyn_data->current_supply;\n      fc::uint128_t max128( max_new_supply.value );\n      fc::uint128_t supply128( _share_asset_dyn_data->current_supply.value );\n      fc::uint128_t new_supply_if_a = supply128 * op.amount_a.amount.value / _pool->balance_a.value;\n      fc::uint128_t new_supply_if_b = supply128 * op.amount_b.amount.value / _pool->balance_b.value;\n      fc::uint128_t new_supply = std::min( { new_supply_if_a, new_supply_if_b, max128 } );\n\n      FC_ASSERT( new_supply > 0, \"Aborting due to zero outcome\" );\n\n      fc::uint128_t a128 = ( new_supply * _pool->balance_a.value + supply128 - 1 ) / supply128; // round up\n      FC_ASSERT( a128 <= fc::uint128_t( op.amount_a.amount.value ), \"Internal error\" );\n      _pool_receives_a = asset( static_cast<int64_t>( a128 ), _pool->asset_a );\n\n      fc::uint128_t b128 = ( new_supply * _pool->balance_b.value + supply128 - 1 ) / supply128; // round up\n      FC_ASSERT( b128 <= fc::uint128_t( op.amount_b.amount.value ), \"Internal error\" );\n      _pool_receives_b = asset( static_cast<int64_t>( b128 ), _pool->asset_b );\n\n      _account_receives = asset( static_cast<int64_t>( new_supply ), _pool->share_asset );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\ngeneric_exchange_operation_result liquidity_pool_deposit_evaluator::do_apply(\n      const liquidity_pool_deposit_operation& op)\n{ try {\n   database& d = db();\n   generic_exchange_operation_result result;\n\n   d.adjust_balance( op.account, -_pool_receives_a );\n   d.adjust_balance( op.account, -_pool_receives_b );\n   d.adjust_balance( op.account, _account_receives );\n\n   d.modify( *_pool, [this]( liquidity_pool_object& lpo ){\n      lpo.balance_a += _pool_receives_a.amount;\n      lpo.balance_b += _pool_receives_b.amount;\n      lpo.update_virtual_value();\n   });\n\n   d.modify( *_share_asset_dyn_data, [this]( asset_dynamic_data_object& data ){\n      data.current_supply += _account_receives.amount;\n   });\n\n   FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, \"Internal error\" );\n   FC_ASSERT( _share_asset_dyn_data->current_supply > 0, \"Internal error\" );\n\n   result.paid.emplace_back( _pool_receives_a );\n   result.paid.emplace_back( _pool_receives_b );\n   result.received.emplace_back( _account_receives );\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result liquidity_pool_withdraw_evaluator::do_evaluate(const liquidity_pool_withdraw_operation& op)\n{ try {\n   const database& d = db();\n\n   _pool = &op.pool(d);\n\n   FC_ASSERT( op.share_amount.asset_id == _pool->share_asset, \"Share asset type mismatch\" );\n\n   FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, \"The pool has not been initialized\" );\n\n   const asset_object& share_asset_obj = _pool->share_asset(d);\n\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, share_asset_obj ),\n              \"The account is unauthorized by the share asset\" );\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_a(d) ),\n              \"The account is unauthorized by asset A\" );\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, _pool->asset_b(d) ),\n              \"The account is unauthorized by asset B\" );\n\n   _share_asset_dyn_data = &share_asset_obj.dynamic_data(d);\n\n   FC_ASSERT( _share_asset_dyn_data->current_supply >= op.share_amount.amount,\n              \"Can not withdraw an amount that is more than the current supply\" );\n\n   if( _share_asset_dyn_data->current_supply == op.share_amount.amount )\n   {\n      _pool_pays_a = asset( _pool->balance_a, _pool->asset_a );\n      _pool_pays_b = asset( _pool->balance_b, _pool->asset_b );\n   }\n   else\n   {\n      fc::uint128_t share128( op.share_amount.amount.value );\n      fc::uint128_t a128 = share128 * _pool->balance_a.value / _share_asset_dyn_data->current_supply.value;\n      FC_ASSERT( a128 < fc::uint128_t( _pool->balance_a.value ), \"Internal error\" );\n      fc::uint128_t fee_a = a128 * _pool->withdrawal_fee_percent / GRAPHENE_100_PERCENT;\n      FC_ASSERT( fee_a <= a128, \"Withdrawal fee percent of the pool is too high\" );\n      a128 -= fee_a;\n      fc::uint128_t b128 = share128 * _pool->balance_b.value / _share_asset_dyn_data->current_supply.value;\n      FC_ASSERT( b128 < fc::uint128_t( _pool->balance_b.value ), \"Internal error\" );\n      fc::uint128_t fee_b = b128 * _pool->withdrawal_fee_percent / GRAPHENE_100_PERCENT;\n      FC_ASSERT( fee_b <= b128, \"Withdrawal fee percent of the pool is too high\" );\n      b128 -= fee_b;\n      FC_ASSERT( a128 > 0 || b128 > 0, \"Aborting due to zero outcome\" );\n      _pool_pays_a = asset( static_cast<int64_t>( a128 ), _pool->asset_a );\n      _pool_pays_b = asset( static_cast<int64_t>( b128 ), _pool->asset_b );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\ngeneric_exchange_operation_result liquidity_pool_withdraw_evaluator::do_apply(\n      const liquidity_pool_withdraw_operation& op)\n{ try {\n   database& d = db();\n   generic_exchange_operation_result result;\n\n   d.adjust_balance( op.account, -op.share_amount );\n\n   if( _pool_pays_a.amount > 0 )\n      d.adjust_balance( op.account, _pool_pays_a );\n   if( _pool_pays_b.amount > 0 )\n      d.adjust_balance( op.account, _pool_pays_b );\n\n   d.modify( *_share_asset_dyn_data, [&op]( asset_dynamic_data_object& data ){\n      data.current_supply -= op.share_amount.amount;\n   });\n\n   d.modify( *_pool, [this]( liquidity_pool_object& lpo ){\n      lpo.balance_a -= _pool_pays_a.amount;\n      lpo.balance_b -= _pool_pays_b.amount;\n      lpo.update_virtual_value();\n   });\n\n   FC_ASSERT( (_pool->balance_a == 0) == (_pool->balance_b == 0), \"Internal error\" );\n   FC_ASSERT( (_pool->balance_a == 0) == (_share_asset_dyn_data->current_supply == 0), \"Internal error\" );\n\n   result.paid.emplace_back( op.share_amount );\n   result.received.emplace_back( _pool_pays_a );\n   result.received.emplace_back( _pool_pays_b );\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result liquidity_pool_exchange_evaluator::do_evaluate(const liquidity_pool_exchange_operation& op)\n{ try {\n   const database& d = db();\n\n   _pool = &op.pool(d);\n\n   FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, \"The pool has not been initialized\" );\n\n   FC_ASSERT(    ( op.amount_to_sell.asset_id == _pool->asset_a && op.min_to_receive.asset_id == _pool->asset_b )\n              || ( op.amount_to_sell.asset_id == _pool->asset_b && op.min_to_receive.asset_id == _pool->asset_a ),\n              \"Asset type mismatch\" );\n\n   const asset_object& asset_obj_a = _pool->asset_a(d);\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, asset_obj_a ),\n              \"The account is unauthorized by asset A\" );\n\n   const asset_object& asset_obj_b = _pool->asset_b(d);\n   FC_ASSERT( is_authorized_asset( d, *fee_paying_account, asset_obj_b ),\n              \"The account is unauthorized by asset B\" );\n\n   _pool_receives_asset = ( op.amount_to_sell.asset_id == _pool->asset_a ? &asset_obj_a : &asset_obj_b );\n\n   _maker_market_fee = d.calculate_market_fee( *_pool_receives_asset, op.amount_to_sell, true );\n   FC_ASSERT( _maker_market_fee < op.amount_to_sell,\n              \"Aborting since the maker market fee of the selling asset is too high\" );\n   _pool_receives = op.amount_to_sell - _maker_market_fee;\n\n   fc::uint128_t delta;\n   if( op.amount_to_sell.asset_id == _pool->asset_a )\n   {\n      share_type new_balance_a = _pool->balance_a + _pool_receives.amount;\n      // round up\n      fc::uint128_t new_balance_b = ( _pool->virtual_value + new_balance_a.value - 1 ) / new_balance_a.value;\n      FC_ASSERT( new_balance_b <= _pool->balance_b, \"Internal error\" );\n      delta = fc::uint128_t( _pool->balance_b.value ) - new_balance_b;\n      _pool_pays_asset = &asset_obj_b;\n   }\n   else\n   {\n      share_type new_balance_b = _pool->balance_b + _pool_receives.amount;\n      // round up\n      fc::uint128_t new_balance_a = ( _pool->virtual_value + new_balance_b.value - 1 ) / new_balance_b.value;\n      FC_ASSERT( new_balance_a <= _pool->balance_a, \"Internal error\" );\n      delta = fc::uint128_t( _pool->balance_a.value ) - new_balance_a;\n      _pool_pays_asset = &asset_obj_a;\n   }\n\n   fc::uint128_t pool_taker_fee = delta * _pool->taker_fee_percent / GRAPHENE_100_PERCENT;\n   FC_ASSERT( pool_taker_fee <= delta, \"Taker fee percent of the pool is too high\" );\n\n   _pool_pays = asset( static_cast<int64_t>( delta - pool_taker_fee ), op.min_to_receive.asset_id );\n\n   _taker_market_fee = d.calculate_market_fee( *_pool_pays_asset, _pool_pays, false );\n   FC_ASSERT( _taker_market_fee <= _pool_pays, \"Market fee should not be greater than the amount to receive\" );\n   _account_receives = _pool_pays - _taker_market_fee;\n\n   FC_ASSERT( _account_receives.amount >= op.min_to_receive.amount, \"Unable to exchange at expected price\" );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\ngeneric_exchange_operation_result liquidity_pool_exchange_evaluator::do_apply(\n      const liquidity_pool_exchange_operation& op)\n{ try {\n   database& d = db();\n   generic_exchange_operation_result result;\n\n   d.adjust_balance( op.account, -op.amount_to_sell );\n   d.adjust_balance( op.account, _account_receives );\n\n   // TODO whose registrar and referrer should receive the shared maker market fee?\n   d.pay_market_fees( &_pool->share_asset(d).issuer(d), *_pool_receives_asset, op.amount_to_sell, true,\n                      _maker_market_fee );\n   d.pay_market_fees( fee_paying_account, *_pool_pays_asset, _pool_pays, false, _taker_market_fee );\n\n   const auto old_virtual_value = _pool->virtual_value;\n   if( op.amount_to_sell.asset_id == _pool->asset_a )\n   {\n      d.modify( *_pool, [&op,this]( liquidity_pool_object& lpo ){\n         lpo.balance_a += _pool_receives.amount;\n         lpo.balance_b -= _pool_pays.amount;\n         lpo.update_virtual_value();\n      });\n   }\n   else\n   {\n      d.modify( *_pool, [&op,this]( liquidity_pool_object& lpo ){\n         lpo.balance_b += _pool_receives.amount;\n         lpo.balance_a -= _pool_pays.amount;\n         lpo.update_virtual_value();\n      });\n   }\n\n   FC_ASSERT( _pool->balance_a > 0 && _pool->balance_b > 0, \"Internal error\" );\n   FC_ASSERT( _pool->virtual_value >= old_virtual_value, \"Internal error\" );\n\n   result.paid.emplace_back( op.amount_to_sell );\n   result.received.emplace_back( _account_receives );\n   result.fees.emplace_back( _maker_market_fee );\n   result.fees.emplace_back( _taker_market_fee );\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/market_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include <graphene/chain/market_evaluator.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n#include <graphene/protocol/market.hpp>\n\nnamespace graphene { namespace chain {\nvoid_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op)\n{ try {\n   const database& d = db();\n\n   FC_ASSERT( op.expiration >= d.head_block_time() );\n\n   _seller        = this->fee_paying_account;\n   _sell_asset    = &op.amount_to_sell.asset_id(d);\n   _receive_asset = &op.min_to_receive.asset_id(d);\n\n   if( _sell_asset->options.whitelist_markets.size() )\n   {\n      GRAPHENE_ASSERT( _sell_asset->options.whitelist_markets.find(_receive_asset->id)\n                          != _sell_asset->options.whitelist_markets.end(),\n                       limit_order_create_market_not_whitelisted,\n                       \"This market has not been whitelisted by the selling asset\", );\n   }\n   if( _sell_asset->options.blacklist_markets.size() )\n   {\n      GRAPHENE_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id)\n                          == _sell_asset->options.blacklist_markets.end(),\n                       limit_order_create_market_blacklisted,\n                       \"This market has been blacklisted by the selling asset\", );\n   }\n\n   GRAPHENE_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ),\n                    limit_order_create_selling_asset_unauthorized,\n                    \"The account is not allowed to transact the selling asset\", );\n\n   GRAPHENE_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ),\n                    limit_order_create_receiving_asset_unauthorized,\n                    \"The account is not allowed to transact the receiving asset\", );\n\n   GRAPHENE_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell,\n                    limit_order_create_insufficient_balance,\n                    \"insufficient balance\",\n                    (\"balance\",d.get_balance(*_seller,*_sell_asset))(\"amount_to_sell\",op.amount_to_sell) );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid limit_order_create_evaluator::convert_fee()\n{\n   if( db().head_block_time() <= HARDFORK_CORE_604_TIME )\n      generic_evaluator::convert_fee();\n   else\n      if( !trx_state->skip_fee )\n      {\n         if( fee_asset->get_id() != asset_id_type() )\n         {\n            db().modify(*fee_asset_dyn_data, [this](asset_dynamic_data_object& d) {\n               d.fee_pool -= core_fee_paid;\n            });\n         }\n      }\n}\n\nvoid limit_order_create_evaluator::pay_fee()\n{\n   if( db().head_block_time() <= HARDFORK_445_TIME )\n      generic_evaluator::pay_fee();\n   else\n   {\n      _deferred_fee = core_fee_paid;\n      if( db().head_block_time() > HARDFORK_CORE_604_TIME && fee_asset->get_id() != asset_id_type() )\n         _deferred_paid_fee = fee_from_account;\n   }\n}\n\nobject_id_type limit_order_create_evaluator::do_apply(const limit_order_create_operation& op)\n{ try {\n   if( op.amount_to_sell.asset_id == asset_id_type() )\n   {\n      db().modify( _seller->statistics(db()), [&op](account_statistics_object& bal) {\n         bal.total_core_in_orders += op.amount_to_sell.amount;\n      });\n   }\n\n   db().adjust_balance(op.seller, -op.amount_to_sell);\n\n   const auto& new_order_object = db().create<limit_order_object>([&](limit_order_object& obj){\n       obj.seller   = _seller->id;\n       obj.for_sale = op.amount_to_sell.amount;\n       obj.sell_price = op.get_price();\n       obj.expiration = op.expiration;\n       obj.deferred_fee = _deferred_fee;\n       obj.deferred_paid_fee = _deferred_paid_fee;\n   });\n   limit_order_id_type order_id = new_order_object.id; // save this because we may remove the object by filling it\n   bool filled;\n   if( db().get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_625_TIME )\n      filled = db().apply_order_before_hardfork_625( new_order_object );\n   else\n      filled = db().apply_order( new_order_object );\n\n   GRAPHENE_ASSERT( !op.fill_or_kill || filled,\n                    limit_order_create_kill_unfilled,\n                    \"Killing limit order ${op} due to unable to fill\",\n                    (\"op\",op) );\n\n   return order_id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result limit_order_cancel_evaluator::do_evaluate(const limit_order_cancel_operation& o)\n{ try {\n   database& d = db();\n\n   _order = d.find( o.order );\n\n   GRAPHENE_ASSERT( _order != nullptr,\n                    limit_order_cancel_nonexist_order,\n                    \"Limit order ${oid} does not exist\",\n                    (\"oid\", o.order) );\n\n   GRAPHENE_ASSERT( _order->seller == o.fee_paying_account,\n                    limit_order_cancel_owner_mismatch,\n                    \"Limit order ${oid} is owned by someone else\",\n                    (\"oid\", o.order) );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nasset limit_order_cancel_evaluator::do_apply(const limit_order_cancel_operation& o)\n{ try {\n   database& d = db();\n\n   auto base_asset = _order->sell_price.base.asset_id;\n   auto quote_asset = _order->sell_price.quote.asset_id;\n   auto refunded = _order->amount_for_sale();\n\n   d.cancel_limit_order(*_order, false /* don't create a virtual op*/);\n\n   if( d.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_606_TIME )\n   {\n      // Possible optimization: order can be called by canceling a limit order iff the canceled order was at the top of the book.\n      // Do I need to check calls in both assets?\n      d.check_call_orders(base_asset(d));\n      d.check_call_orders(quote_asset(d));\n   }\n\n   return refunded;\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result call_order_update_evaluator::do_evaluate(const call_order_update_operation& o)\n{ try {\n   database& d = db();\n\n   auto next_maintenance_time = d.get_dynamic_global_properties().next_maintenance_time;\n\n   _paying_account = &o.funding_account(d);\n   _debt_asset     = &o.delta_debt.asset_id(d);\n   FC_ASSERT( _debt_asset->is_market_issued(), \"Unable to cover ${sym} as it is not a collateralized asset.\",\n              (\"sym\", _debt_asset->symbol) );\n\n   FC_ASSERT( o.delta_debt.amount <= 0 || _debt_asset->can_create_new_supply(), \"Can not create new supply\" );\n\n   _dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d);\n\n   /***\n    * There are instances of assets exceeding max_supply before hf 1465, therefore this code must remain.\n    */\n   if (next_maintenance_time > HARDFORK_CORE_1465_TIME)\n   {\n      FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply,\n            \"Borrowing this quantity would exceed MAX_SUPPLY\" );\n   }\n   \n   FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0,\n         \"This transaction would bring current supply below zero.\");\n\n   _bitasset_data  = &_debt_asset->bitasset_data(d);\n\n   /// if there is a settlement for this asset, then no further margin positions may be taken and\n   /// all existing margin positions should have been closed va database::globally_settle_asset\n   FC_ASSERT( !_bitasset_data->has_settlement(), \"Cannot update debt position when the asset has been globally settled\" );\n\n   FC_ASSERT( o.delta_collateral.asset_id == _bitasset_data->options.short_backing_asset,\n              \"Collateral asset type should be same as backing asset of debt asset\" );\n\n   if( _bitasset_data->is_prediction_market )\n      FC_ASSERT( o.delta_collateral.amount == o.delta_debt.amount,\n                 \"Debt amount and collateral amount should be same when updating debt position in a prediction market\" );\n   else if( _bitasset_data->current_feed.settlement_price.is_null() )\n      FC_THROW_EXCEPTION(insufficient_feeds, \"Cannot borrow asset with no price feed.\");\n\n   // Note: there was code here checking whether the account has enough balance to increase delta collateral,\n   //       which is now removed since the check is implicitly done later by `adjust_balance()` in `do_apply()`.\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\nobject_id_type call_order_update_evaluator::do_apply(const call_order_update_operation& o)\n{ try {\n   database& d = db();\n\n   if( o.delta_debt.amount != 0 )\n   {\n      d.adjust_balance( o.funding_account, o.delta_debt );\n\n      // Deduct the debt paid from the total supply of the debt asset.\n      d.modify(*_dynamic_data_obj, [&](asset_dynamic_data_object& dynamic_asset) {\n         dynamic_asset.current_supply += o.delta_debt.amount;\n      });\n   }\n\n   if( o.delta_collateral.amount != 0 )\n   {\n      d.adjust_balance( o.funding_account, -o.delta_collateral  );\n\n      // Adjust the total core in orders accodingly\n      if( o.delta_collateral.asset_id == asset_id_type() )\n      {\n         d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) {\n               stats.total_core_in_orders += o.delta_collateral.amount;\n         });\n      }\n   }\n\n   const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;\n   bool before_core_hardfork_1270 = ( next_maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue\n\n   auto& call_idx = d.get_index_type<call_order_index>().indices().get<by_account>();\n   auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) );\n   const call_order_object* call_obj = nullptr;\n   call_order_id_type call_order_id;\n\n   optional<price> old_collateralization;\n   optional<share_type> old_debt;\n\n   if( itr == call_idx.end() ) // creating new debt position\n   {\n      FC_ASSERT( o.delta_collateral.amount > 0, \"Delta collateral amount of new debt position should be positive\" );\n      FC_ASSERT( o.delta_debt.amount > 0, \"Delta debt amount of new debt position should be positive\" );\n\n      call_obj = &d.create<call_order_object>( [&o,this,before_core_hardfork_1270]( call_order_object& call ){\n         call.borrower = o.funding_account;\n         call.collateral = o.delta_collateral.amount;\n         call.debt = o.delta_debt.amount;\n         if( before_core_hardfork_1270 ) // before core-1270 hard fork, calculate call_price here and cache it\n            call.call_price = price::call_price( o.delta_debt, o.delta_collateral,\n                                                 _bitasset_data->current_feed.maintenance_collateral_ratio );\n         else // after core-1270 hard fork, set call_price to 1\n            call.call_price = price( asset( 1, o.delta_collateral.asset_id ), asset( 1, o.delta_debt.asset_id ) );\n         call.target_collateral_ratio = o.extensions.value.target_collateral_ratio;\n      });\n      call_order_id = call_obj->id;\n   }\n   else // updating existing debt position\n   {\n      call_obj = &*itr;\n      auto new_collateral = call_obj->collateral + o.delta_collateral.amount;\n      auto new_debt = call_obj->debt + o.delta_debt.amount;\n      call_order_id = call_obj->id;\n\n      if( new_debt == 0 )\n      {\n         FC_ASSERT( new_collateral == 0, \"Should claim all collateral when closing debt position\" );\n         d.remove( *call_obj );\n         return call_order_id;\n      }\n\n      FC_ASSERT( new_collateral > 0 && new_debt > 0,\n                 \"Both collateral and debt should be positive after updated a debt position if not to close it\" );\n\n      old_collateralization = call_obj->collateralization();\n      old_debt = call_obj->debt;\n\n      d.modify( *call_obj, [&o,new_debt,new_collateral,this,before_core_hardfork_1270]( call_order_object& call ){\n         call.collateral = new_collateral;\n         call.debt       = new_debt;\n         if( before_core_hardfork_1270 ) // don't update call_price after core-1270 hard fork\n         {\n            call.call_price  =  price::call_price( call.get_debt(), call.get_collateral(),\n                                                   _bitasset_data->current_feed.maintenance_collateral_ratio );\n         }\n         call.target_collateral_ratio = o.extensions.value.target_collateral_ratio;\n      });\n   }\n\n   // then we must check for margin calls and other issues\n   if( !_bitasset_data->is_prediction_market )\n   {\n      // check to see if the order needs to be margin called now, but don't allow black swans and require there to be\n      // limit orders available that could be used to fill the order.\n      // Note: due to https://github.com/bitshares/bitshares-core/issues/649, before core-343 hard fork,\n      //       the first call order may be unable to be updated if the second one is undercollateralized.\n      if( d.check_call_orders( *_debt_asset, false, false, _bitasset_data ) ) // don't allow black swan, not for new limit order\n      {\n         call_obj = d.find(call_order_id);\n         // before hard fork core-583: if we filled at least one call order, we are OK if we totally filled.\n         // after hard fork core-583: we want to allow increasing collateral\n         //   Note: increasing collateral won't get the call order itself matched (instantly margin called)\n         //   if there is at least a call order get matched but didn't cause a black swan event,\n         //   current order must have got matched. in this case, it's OK if it's totally filled.\n         GRAPHENE_ASSERT(\n            !call_obj,\n            call_order_update_unfilled_margin_call,\n            \"Updating call order would trigger a margin call that cannot be fully filled\"\n            );\n      }\n      else\n      {\n         call_obj = d.find(call_order_id);\n         // we know no black swan event has occurred\n         FC_ASSERT( call_obj, \"no margin call was executed and yet the call object was deleted\" );\n         // this HF must remain as-is, as the assert inside the \"if\" was triggered during push_proposal()\n         if( d.head_block_time() <= HARDFORK_CORE_583_TIME )\n         {\n            // We didn't fill any call orders.  This may be because we\n            // aren't in margin call territory, or it may be because there\n            // were no matching orders.  In the latter case, we throw.\n            GRAPHENE_ASSERT(\n               // we know core-583 hard fork is before core-1270 hard fork, it's ok to use call_price here\n               ~call_obj->call_price < _bitasset_data->current_feed.settlement_price,\n               call_order_update_unfilled_margin_call,\n               \"Updating call order would trigger a margin call that cannot be fully filled\",\n               // we know core-583 hard fork is before core-1270 hard fork, it's ok to use call_price here\n               (\"a\", ~call_obj->call_price )(\"b\", _bitasset_data->current_feed.settlement_price)\n               );\n         }\n         else // after hard fork core-583, always allow call order to be updated if collateral ratio\n              // is increased and debt is not increased\n         {\n            // We didn't fill any call orders.  This may be because we\n            // aren't in margin call territory, or it may be because there\n            // were no matching orders. In the latter case,\n            // if collateral ratio is not increased or debt is increased, we throw.\n            // be here, we know no margin call was executed,\n            // so call_obj's collateral ratio should be set only by op\n            // ------\n            // Before BSIP77, CR of the new/updated position is required to be above MCR;\n            // after BSIP77, CR of the new/updated position is required to be above max(ICR,MCR).\n            // The `current_initial_collateralization` variable has been initialized according to the logic,\n            // so we directly use it here.\n            bool check = ( !before_core_hardfork_1270\n                            && call_obj->collateralization() > _bitasset_data->current_initial_collateralization )\n                       || ( before_core_hardfork_1270\n                            && ~call_obj->call_price < _bitasset_data->current_feed.settlement_price )\n                       || ( old_collateralization.valid() && call_obj->debt <= *old_debt\n                                                          && call_obj->collateralization() > *old_collateralization );\n            FC_ASSERT( check,\n               \"Can only increase collateral ratio without increasing debt when the debt position's \"\n               \"collateral ratio is lower than required initial collateral ratio (ICR), \"\n               \"if not to trigger a margin call that be fully filled immediately\",\n               (\"old_debt\", old_debt)\n               (\"new_debt\", call_obj->debt)\n               (\"old_collateralization\", old_collateralization)\n               (\"new_collateralization\", call_obj->collateralization() )\n               );\n         }\n      }\n   }\n\n   return call_order_id;\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result bid_collateral_evaluator::do_evaluate(const bid_collateral_operation& o)\n{ try {\n   database& d = db();\n\n   FC_ASSERT( d.head_block_time() > HARDFORK_CORE_216_TIME, \"Not yet!\" );\n\n   _paying_account = &o.bidder(d);\n   _debt_asset     = &o.debt_covered.asset_id(d);\n   FC_ASSERT( _debt_asset->is_market_issued(), \"Unable to cover ${sym} as it is not a collateralized asset.\",\n              (\"sym\", _debt_asset->symbol) );\n\n   _bitasset_data  = &_debt_asset->bitasset_data(d);\n\n   FC_ASSERT( _bitasset_data->has_settlement() );\n\n   FC_ASSERT( o.additional_collateral.asset_id == _bitasset_data->options.short_backing_asset );\n\n   FC_ASSERT( !_bitasset_data->is_prediction_market, \"Cannot bid on a prediction market!\" );\n\n   const collateral_bid_index& bids = d.get_index_type<collateral_bid_index>();\n   const auto& index = bids.indices().get<by_account>();\n   const auto& bid = index.find( boost::make_tuple( o.debt_covered.asset_id, o.bidder ) );\n   if( bid != index.end() )\n      _bid = &(*bid);\n   else\n       FC_ASSERT( o.debt_covered.amount > 0, \"Can't find bid to cancel?!\");\n\n   if( o.additional_collateral.amount > 0 )\n   {\n      if( _bid && d.head_block_time() >= HARDFORK_CORE_1692_TIME ) // TODO: see if HF check can be removed after HF\n      {\n         asset delta = o.additional_collateral - _bid->get_additional_collateral();\n         FC_ASSERT( d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)) >= delta,\n                    \"Cannot increase bid from ${oc} to ${nc} collateral when payer only has ${b}\",\n                    (\"oc\", _bid->get_additional_collateral().amount)(\"nc\", o.additional_collateral.amount)\n                    (\"b\", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );\n      } else\n         FC_ASSERT( d.get_balance( *_paying_account,\n                                   _bitasset_data->options.short_backing_asset(d) ) >= o.additional_collateral,\n                    \"Cannot bid ${c} collateral when payer only has ${b}\", (\"c\", o.additional_collateral.amount)\n                    (\"b\", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\nvoid_result bid_collateral_evaluator::do_apply(const bid_collateral_operation& o)\n{ try {\n   database& d = db();\n\n   if( _bid )\n      d.cancel_bid( *_bid, false );\n\n   if( o.debt_covered.amount == 0 ) return void_result();\n\n   d.adjust_balance( o.bidder, -o.additional_collateral  );\n\n   _bid = &d.create<collateral_bid_object>([&]( collateral_bid_object& bid ) {\n      bid.bidder = o.bidder;\n      bid.inv_swan_price = o.additional_collateral / o.debt_covered;\n   });\n\n   // Note: CORE asset in collateral_bid_object is not counted in account_stats.total_core_in_orders\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/market_object.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/market_object.hpp>\n\n#include <boost/multiprecision/cpp_int.hpp>\n\n#include <functional>\n\n#include <fc/io/raw.hpp>\n\nusing namespace graphene::chain;\n\n/*\ntarget_CR = max( target_CR, MCR )\n\ntarget_CR = new_collateral / ( new_debt / feed_price )\n          = ( collateral - max_amount_to_sell ) * feed_price\n            / ( debt - amount_to_get )\n          = ( collateral - max_amount_to_sell ) * feed_price\n            / ( debt - round_down(max_amount_to_sell * match_price ) )\n          = ( collateral - max_amount_to_sell ) * feed_price\n            / ( debt - (max_amount_to_sell * match_price - x) )\n\nNote: x is the fraction, 0 <= x < 1\n\n=>\n\nmax_amount_to_sell = ( (debt + x) * target_CR - collateral * feed_price )\n                     / (target_CR * match_price - feed_price)\n                   = ( (debt + x) * tCR / DENOM - collateral * fp_debt_amt / fp_coll_amt )\n                     / ( (tCR / DENOM) * (mp_debt_amt / mp_coll_amt) - fp_debt_amt / fp_coll_amt )\n                   = ( (debt + x) * tCR * fp_coll_amt * mp_coll_amt - collateral * fp_debt_amt * DENOM * mp_coll_amt)\n                     / ( tCR * mp_debt_amt * fp_coll_amt - fp_debt_amt * DENOM * mp_coll_amt )\n\nmax_debt_to_cover = max_amount_to_sell * match_price\n                  = max_amount_to_sell * mp_debt_amt / mp_coll_amt\n                  = ( (debt + x) * tCR * fp_coll_amt * mp_debt_amt - collateral * fp_debt_amt * DENOM * mp_debt_amt)\n                    / (tCR * mp_debt_amt * fp_coll_amt - fp_debt_amt * DENOM * mp_coll_amt)\n*/\nshare_type call_order_object::get_max_debt_to_cover( price match_price,\n                                                     price feed_price,\n                                                     const uint16_t maintenance_collateral_ratio,\n                                                     const optional<price>& maintenance_collateralization )const\n{ try {\n   // be defensive here, make sure feed_price is in collateral / debt format\n   if( feed_price.base.asset_id != call_price.base.asset_id )\n      feed_price = ~feed_price;\n\n   FC_ASSERT( feed_price.base.asset_id == call_price.base.asset_id\n              && feed_price.quote.asset_id == call_price.quote.asset_id );\n\n   bool after_core_hardfork_1270 = maintenance_collateralization.valid();\n\n   // be defensive here, make sure maintenance_collateralization is in collateral / debt format\n   if( after_core_hardfork_1270 )\n   {\n      FC_ASSERT( maintenance_collateralization->base.asset_id == call_price.base.asset_id\n                 && maintenance_collateralization->quote.asset_id == call_price.quote.asset_id );\n   }\n\n   // According to the feed protection rule (https://github.com/cryptonomex/graphene/issues/436),\n   // a call order should only be called when its collateral ratio is not higher than required maintenance collateral ratio.\n   // Although this should be guaranteed by the caller of this function, we still check here to be defensive.\n   // Theoretically this check can be skipped for better performance.\n   //\n   // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().\n   if( ( !after_core_hardfork_1270 && call_price > feed_price )\n       || ( after_core_hardfork_1270 && collateralization() > *maintenance_collateralization ) )\n      return 0;\n\n   if( !target_collateral_ratio.valid() ) // target cr is not set\n      return debt;\n\n   uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio ); // use mcr if target cr is too small\n\n   price target_collateralization = ( after_core_hardfork_1270 ?\n                                      feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) :\n                                      price() );\n\n   // be defensive here, make sure match_price is in collateral / debt format\n   if( match_price.base.asset_id != call_price.base.asset_id )\n      match_price = ~match_price;\n\n   FC_ASSERT( match_price.base.asset_id == call_price.base.asset_id\n              && match_price.quote.asset_id == call_price.quote.asset_id );\n\n   typedef boost::multiprecision::int256_t i256;\n   i256 mp_debt_amt = match_price.quote.amount.value;\n   i256 mp_coll_amt = match_price.base.amount.value;\n   i256 fp_debt_amt = feed_price.quote.amount.value;\n   i256 fp_coll_amt = feed_price.base.amount.value;\n\n   // firstly we calculate without the fraction (x), the result could be a bit too small\n   i256 numerator = fp_coll_amt * mp_debt_amt * debt.value * tcr\n                  - fp_debt_amt * mp_debt_amt * collateral.value * GRAPHENE_COLLATERAL_RATIO_DENOM;\n   if( numerator < 0 ) // feed protected, actually should not be true here, just check to be safe\n      return 0;\n\n   i256 denominator = fp_coll_amt * mp_debt_amt * tcr - fp_debt_amt * mp_coll_amt * GRAPHENE_COLLATERAL_RATIO_DENOM;\n   if( denominator <= 0 ) // black swan\n      return debt;\n\n   // note: if add 1 here, will result in 1.5x imperfection rate;\n   //       however, due to rounding, the result could still be a bit too big, thus imperfect.\n   i256 to_cover_i256 = ( numerator / denominator );\n   if( to_cover_i256 >= debt.value ) // avoid possible overflow\n      return debt;\n   share_type to_cover_amt = static_cast< int64_t >( to_cover_i256 );\n\n   // stabilize\n   // note: rounding up-down results in 3x imperfection rate in comparison to down-down-up\n   asset to_pay = asset( to_cover_amt, debt_type() ) * match_price;\n   asset to_cover = to_pay * match_price;\n   to_pay = to_cover.multiply_and_round_up( match_price );\n\n   if( to_cover.amount >= debt || to_pay.amount >= collateral ) // to be safe\n      return debt;\n   FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );\n\n   // Check whether the collateral ratio after filled is high enough\n   // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().\n   std::function<bool()> result_is_good = after_core_hardfork_1270 ?\n      std::function<bool()>( [this,&to_cover,&to_pay,target_collateralization]() -> bool\n      {\n         price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover );\n         return ( new_collateralization > target_collateralization );\n      }) :\n      std::function<bool()>( [this,&to_cover,&to_pay,tcr,feed_price]() -> bool\n      {\n         price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );\n         return ( new_call_price > feed_price );\n      });\n\n   // if the result is good, we return.\n   if( result_is_good() )\n      return to_cover.amount;\n\n   // be here, to_cover is too small due to rounding. deal with the fraction\n   numerator += fp_coll_amt * mp_debt_amt * tcr; // plus the fraction\n   to_cover_i256 = ( numerator / denominator ) + 1;\n   if( to_cover_i256 >= debt.value ) // avoid possible overflow\n      to_cover_i256 = debt.value;\n   to_cover_amt = static_cast< int64_t >( to_cover_i256 );\n\n   asset max_to_pay = ( ( to_cover_amt == debt.value ) ? get_collateral()\n                        : asset( to_cover_amt, debt_type() ).multiply_and_round_up( match_price ) );\n   if( max_to_pay.amount > collateral )\n      max_to_pay.amount = collateral;\n\n   asset max_to_cover = ( ( max_to_pay.amount == collateral ) ? get_debt() : ( max_to_pay * match_price ) );\n   if( max_to_cover.amount >= debt ) // to be safe\n   {\n      max_to_pay.amount = collateral;\n      max_to_cover.amount = debt;\n   }\n\n   if( max_to_pay <= to_pay || max_to_cover <= to_cover ) // strange data. should skip binary search and go on, but doesn't help much\n      return debt;\n   FC_ASSERT( max_to_pay > to_pay && max_to_cover > to_cover );\n\n   asset min_to_pay = to_pay;\n   asset min_to_cover = to_cover;\n\n   // try with binary search to find a good value\n   // note: actually binary search can not always provide perfect result here,\n   //       due to rounding, collateral ratio is not always increasing while to_pay or to_cover is increasing\n   bool max_is_ok = false;\n   while( true )\n   {\n      // get the mean\n      if( match_price.base.amount < match_price.quote.amount ) // step of collateral is smaller\n      {\n         to_pay.amount = ( min_to_pay.amount + max_to_pay.amount + 1 ) / 2; // should not overflow. round up here\n         if( to_pay.amount == max_to_pay.amount )\n            to_cover.amount = max_to_cover.amount;\n         else\n         {\n            to_cover = to_pay * match_price;\n            if( to_cover.amount >= max_to_cover.amount ) // can be true when max_is_ok is false\n            {\n               to_pay.amount = max_to_pay.amount;\n               to_cover.amount = max_to_cover.amount;\n            }\n            else\n            {\n               to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization, no change or become smaller\n               FC_ASSERT( to_pay.amount < max_to_pay.amount );\n            }\n         }\n      }\n      else // step of debt is smaller or equal\n      {\n         to_cover.amount = ( min_to_cover.amount + max_to_cover.amount ) / 2; // should not overflow. round down here\n         if( to_cover.amount == max_to_cover.amount )\n            to_pay.amount = max_to_pay.amount;\n         else\n         {\n            to_pay = to_cover.multiply_and_round_up( match_price );\n            if( to_pay.amount >= max_to_pay.amount ) // can be true when max_is_ok is false\n            {\n               to_pay.amount = max_to_pay.amount;\n               to_cover.amount = max_to_cover.amount;\n            }\n            else\n            {\n               to_cover = to_pay * match_price; // stabilization, to_cover should have increased\n               if( to_cover.amount >= max_to_cover.amount ) // to be safe\n               {\n                  to_pay.amount = max_to_pay.amount;\n                  to_cover.amount = max_to_cover.amount;\n               }\n            }\n         }\n      }\n\n      // check again to see if we've moved away from the minimums, if not, use the maximums directly\n      if( to_pay.amount <= min_to_pay.amount || to_cover.amount <= min_to_cover.amount\n            || to_pay.amount > max_to_pay.amount || to_cover.amount > max_to_cover.amount )\n      {\n         to_pay.amount = max_to_pay.amount;\n         to_cover.amount = max_to_cover.amount;\n      }\n\n      // check the mean\n      if( to_pay.amount == max_to_pay.amount && ( max_is_ok || to_pay.amount == collateral ) )\n         return to_cover.amount;\n      FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );\n\n      // Check whether the result is good\n      if( result_is_good() ) // good\n      {\n         if( to_pay.amount == max_to_pay.amount )\n            return to_cover.amount;\n         max_to_pay.amount = to_pay.amount;\n         max_to_cover.amount = to_cover.amount;\n         max_is_ok = true;\n      }\n      else // not good\n      {\n         if( to_pay.amount == max_to_pay.amount )\n            break;\n         min_to_pay.amount = to_pay.amount;\n         min_to_cover.amount = to_cover.amount;\n      }\n   }\n\n   // be here, max_to_cover is too small due to rounding. search forward\n   for( uint64_t d1 = 0, d2 = 1, d3 = 1; ; d1 = d2, d2 = d3, d3 = d1 + d2 ) // 1,1,2,3,5,8,...\n   {\n      if( match_price.base.amount > match_price.quote.amount ) // step of debt is smaller\n      {\n         to_pay.amount += d2;\n         if( to_pay.amount >= collateral )\n            return debt;\n         to_cover = to_pay * match_price;\n         if( to_cover.amount >= debt )\n            return debt;\n         to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization\n         if( to_pay.amount >= collateral )\n            return debt;\n      }\n      else // step of collateral is smaller or equal\n      {\n         to_cover.amount += d2;\n         if( to_cover.amount >= debt )\n            return debt;\n         to_pay = to_cover.multiply_and_round_up( match_price );\n         if( to_pay.amount >= collateral )\n            return debt;\n         to_cover = to_pay * match_price; // stabilization\n         if( to_cover.amount >= debt )\n            return debt;\n      }\n\n      // defensive check\n      FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );\n\n      // Check whether the result is good\n      if( result_is_good() ) // good\n         return to_cover.amount;\n   }\n\n} FC_CAPTURE_AND_RETHROW( (*this)(feed_price)(match_price)(maintenance_collateral_ratio) ) }\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::limit_order_object,\n                    (graphene::db::object),\n                    (expiration)(seller)(for_sale)(sell_price)(deferred_fee)(deferred_paid_fee)\n                  )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::call_order_object, (graphene::db::object),\n                    (borrower)(collateral)(debt)(call_price)(target_collateral_ratio) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::force_settlement_object,\n                    (graphene::db::object),\n                    (owner)(balance)(settlement_date)\n                  )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::collateral_bid_object, (graphene::db::object),\n                    (bidder)(inv_swan_price) )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::limit_order_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::call_order_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::force_settlement_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::collateral_bid_object )\n"
  },
  {
    "path": "libraries/chain/proposal_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/proposal_evaluator.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n\nnamespace graphene { namespace chain {\n\nnamespace detail {\n   void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options);\n\n   void check_asset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const asset_options& options);\n   void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const bitasset_options& options);\n   void check_asset_update_extensions_hf_bsip_48_75( const fc::time_point_sec& block_time,\n                                                     const asset_update_operation::ext& extensions );\n\n   void check_asset_publish_feed_extensions_hf_bsip77( const fc::time_point_sec& block_time,\n                                                       const asset_publish_feed_operation::ext& extensions );\n   void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time, const bitasset_options& options);\n\n   void check_bitasset_options_hf_bsip74(const fc::time_point_sec& block_time,\n                                         const bitasset_options& options); // HF_REMOVABLE\n\n   void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options);\n\n   void check_bitasset_options_hf_bsip87(const fc::time_point_sec& block_time,\n                                         const bitasset_options& options); // HF_REMOVABLE\n\n   void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time,\n                                                        const asset_claim_fees_operation& op); // HF_REMOVABLE\n}\n\nstruct proposal_operation_hardfork_visitor\n{\n   typedef void result_type;\n   const database& db;\n   const fc::time_point_sec block_time;\n   const fc::time_point_sec next_maintenance_time;\n\n   proposal_operation_hardfork_visitor( const database& _db, const fc::time_point_sec bt )\n   : db( _db ), block_time(bt), next_maintenance_time( db.get_dynamic_global_properties().next_maintenance_time ) {}\n\n   template<typename T>\n   void operator()(const T &v) const {}\n\n   void operator()(const graphene::chain::asset_create_operation &v) const {\n      detail::check_asset_options_hf_1774(block_time, v.common_options);\n      detail::check_asset_options_hf_bsip_48_75(block_time, v.common_options);\n      detail::check_asset_options_hf_bsip81(block_time, v.common_options);\n      if( v.bitasset_opts.valid() ) {\n         detail::check_bitasset_options_hf_bsip_48_75( block_time, *v.bitasset_opts );\n         detail::check_bitasset_options_hf_bsip74( block_time, *v.bitasset_opts ); // HF_REMOVABLE\n         detail::check_bitasset_options_hf_bsip77( block_time, *v.bitasset_opts ); // HF_REMOVABLE\n         detail::check_bitasset_options_hf_bsip87( block_time, *v.bitasset_opts ); // HF_REMOVABLE\n      }\n\n      // TODO move as many validations as possible to validate() if not triggered before hardfork\n      if( HARDFORK_BSIP_48_75_PASSED( block_time ) )\n      {\n         v.common_options.validate_flags( v.bitasset_opts.valid() );\n      }\n   }\n\n   void operator()(const graphene::chain::asset_update_operation &v) const {\n      detail::check_asset_options_hf_1774(block_time, v.new_options);\n      detail::check_asset_options_hf_bsip_48_75(block_time, v.new_options);\n      detail::check_asset_options_hf_bsip81(block_time, v.new_options);\n\n      detail::check_asset_update_extensions_hf_bsip_48_75( block_time, v.extensions.value );\n\n      // TODO move as many validations as possible to validate() if not triggered before hardfork\n      if( HARDFORK_BSIP_48_75_PASSED( block_time ) )\n      {\n         v.new_options.validate_flags( true );\n      }\n\n   }\n\n   void operator()(const graphene::chain::asset_update_bitasset_operation &v) const {\n      detail::check_bitasset_options_hf_bsip_48_75( block_time, v.new_options );\n      detail::check_bitasset_options_hf_bsip74( block_time, v.new_options ); // HF_REMOVABLE\n      detail::check_bitasset_options_hf_bsip77( block_time, v.new_options ); // HF_REMOVABLE\n      detail::check_bitasset_options_hf_bsip87( block_time, v.new_options ); // HF_REMOVABLE\n   }\n\n   void operator()(const graphene::chain::asset_claim_fees_operation &v) const {\n      detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE\n   }\n\n   void operator()(const graphene::chain::asset_publish_feed_operation &v) const {\n\n      detail::check_asset_publish_feed_extensions_hf_bsip77( block_time, v.extensions.value );\n\n   }\n\n   void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const {\n      if (block_time < HARDFORK_CORE_1468_TIME) {\n         FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), \n               \"Unable to set HTLC options before hardfork 1468\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<htlc_create_operation>());\n         FC_ASSERT(!op.new_parameters.current_fees->exists<htlc_redeem_operation>());\n         FC_ASSERT(!op.new_parameters.current_fees->exists<htlc_extend_operation>());\n      }\n      if (!HARDFORK_BSIP_40_PASSED(block_time)) {\n         FC_ASSERT(!op.new_parameters.extensions.value.custom_authority_options.valid(),\n                   \"Unable to set Custom Authority Options before hardfork BSIP 40\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<custom_authority_create_operation>(),\n                   \"Unable to define fees for custom authority operations prior to hardfork BSIP 40\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<custom_authority_update_operation>(),\n                   \"Unable to define fees for custom authority operations prior to hardfork BSIP 40\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<custom_authority_delete_operation>(),\n                   \"Unable to define fees for custom authority operations prior to hardfork BSIP 40\");\n      }\n      if (!HARDFORK_BSIP_85_PASSED(block_time)) {\n         FC_ASSERT(!op.new_parameters.extensions.value.maker_fee_discount_percent.valid(),\n                   \"Unable to set maker_fee_discount_percent before hardfork BSIP 85\");\n      }\n      if (!HARDFORK_BSIP_86_PASSED(block_time)) {\n         FC_ASSERT(!op.new_parameters.extensions.value.market_fee_network_percent.valid(),\n                   \"Unable to set market_fee_network_percent before hardfork BSIP 86\");\n      }\n      if (!HARDFORK_CORE_2103_PASSED(block_time)) {\n         FC_ASSERT(!op.new_parameters.current_fees->exists<ticket_create_operation>(),\n                   \"Unable to define fees for ticket operations prior to hardfork 2103\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<ticket_update_operation>(),\n                   \"Unable to define fees for ticket operations prior to hardfork 2103\");\n      }\n      if (!HARDFORK_LIQUIDITY_POOL_PASSED(block_time)) {\n         FC_ASSERT(!op.new_parameters.current_fees->exists<liquidity_pool_create_operation>(),\n                   \"Unable to define fees for liquidity pool operations prior to the LP hardfork\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<liquidity_pool_delete_operation>(),\n                   \"Unable to define fees for liquidity pool operations prior to the LP hardfork\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<liquidity_pool_deposit_operation>(),\n                   \"Unable to define fees for liquidity pool operations prior to the LP hardfork\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<liquidity_pool_withdraw_operation>(),\n                   \"Unable to define fees for liquidity pool operations prior to the LP hardfork\");\n         FC_ASSERT(!op.new_parameters.current_fees->exists<liquidity_pool_exchange_operation>(),\n                   \"Unable to define fees for liquidity pool operations prior to the LP hardfork\");\n      }\n   }\n   void operator()(const graphene::chain::htlc_create_operation &op) const {\n      FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, \"Not allowed until hardfork 1468\" );\n      if (block_time < HARDFORK_CORE_BSIP64_TIME)\n      {\n         // memo field added at harfork BSIP64\n         // NOTE: both of these checks can be removed after hardfork time\n         FC_ASSERT( !op.extensions.value.memo.valid(), \n               \"Memo unavailable until after HARDFORK BSIP64\");\n         // HASH160 added at hardfork BSIP64\n         FC_ASSERT( !op.preimage_hash.is_type<fc::hash160>(),\n               \"HASH160 unavailable until after HARDFORK BSIP64\" );   \n      }\n   }\n   void operator()(const graphene::chain::htlc_redeem_operation &op) const {\n      FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, \"Not allowed until hardfork 1468\" );\n   }\n   void operator()(const graphene::chain::htlc_extend_operation &op) const {\n      FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, \"Not allowed until hardfork 1468\" );\n   }\n   void operator()(const graphene::chain::custom_authority_create_operation&) const {\n      FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), \"Not allowed until hardfork BSIP 40\" );\n   }\n   void operator()(const graphene::chain::custom_authority_update_operation&) const {\n      FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), \"Not allowed until hardfork BSIP 40\" );\n   }\n   void operator()(const graphene::chain::custom_authority_delete_operation&) const {\n      FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), \"Not allowed until hardfork BSIP 40\" );\n   }\n   void operator()(const graphene::chain::ticket_create_operation &op) const {\n      FC_ASSERT( HARDFORK_CORE_2103_PASSED(block_time), \"Not allowed until hardfork 2103\" );\n   }\n   void operator()(const graphene::chain::ticket_update_operation &op) const {\n      FC_ASSERT( HARDFORK_CORE_2103_PASSED(block_time), \"Not allowed until hardfork 2103\" );\n   }\n   void operator()(const graphene::chain::liquidity_pool_create_operation &op) const {\n      FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), \"Not allowed until the LP hardfork\" );\n   }\n   void operator()(const graphene::chain::liquidity_pool_delete_operation &op) const {\n      FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), \"Not allowed until the LP hardfork\" );\n   }\n   void operator()(const graphene::chain::liquidity_pool_deposit_operation &op) const {\n      FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), \"Not allowed until the LP hardfork\" );\n   }\n   void operator()(const graphene::chain::liquidity_pool_withdraw_operation &op) const {\n      FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), \"Not allowed until the LP hardfork\" );\n   }\n   void operator()(const graphene::chain::liquidity_pool_exchange_operation &op) const {\n      FC_ASSERT( HARDFORK_LIQUIDITY_POOL_PASSED(block_time), \"Not allowed until the LP hardfork\" );\n   }\n\n   // loop and self visit in proposals\n   void operator()(const graphene::chain::proposal_create_operation &v) const {\n      bool already_contains_proposal_update = false;\n\n      for (const op_wrapper &op : v.proposed_ops)\n      {\n         op.op.visit(*this);\n         // Do not allow more than 1 proposal_update in a proposal\n         if ( op.op.is_type<proposal_update_operation>() )\n         {\n            FC_ASSERT( !already_contains_proposal_update, \n                  \"At most one proposal update can be nested in a proposal!\" );\n            already_contains_proposal_update = true;\n         }\n      }\n   }\n};\n\nstruct hardfork_visitor_214 // non-recursive proposal visitor\n{\n   typedef void result_type;\n\n   template<typename T>\n   void operator()(const T &v) const {}\n\n   void operator()(const proposal_update_operation &v) const {\n      FC_ASSERT(false, \"Not allowed until hardfork 214\");\n   }\n};\n\nvoid hardfork_visitor_1479::operator()(const proposal_update_operation &v)\n{\n   if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance )\n      max_update_instance = v.proposal.instance.value;\n   nested_update_count++;\n}\n\nvoid hardfork_visitor_1479::operator()(const proposal_delete_operation &v)\n{\n   if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance )\n      max_update_instance = v.proposal.instance.value;\n   nested_update_count++;\n}\n\n// loop and self visit in proposals\nvoid hardfork_visitor_1479::operator()(const graphene::chain::proposal_create_operation &v)\n{\n   for (const op_wrapper &op : v.proposed_ops)\n      op.op.visit(*this);\n}\n\nvoid_result proposal_create_evaluator::do_evaluate( const proposal_create_operation& o )\n{ try {\n   const database& d = db();\n\n   // Calling the proposal hardfork visitor\n   const fc::time_point_sec block_time = d.head_block_time();\n   proposal_operation_hardfork_visitor vtor( d, block_time );\n   vtor( o );\n   if( block_time < HARDFORK_CORE_214_TIME )\n   {\n      // cannot be removed after hf, unfortunately\n      hardfork_visitor_214 hf214;\n      for( const op_wrapper &op : o.proposed_ops )\n         op.op.visit( hf214 );\n   }\n   vtor_1479( o );\n\n   const auto& global_parameters = d.get_global_properties().parameters;\n\n   FC_ASSERT( o.expiration_time > block_time, \"Proposal has already expired on creation.\" );\n   FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime,\n              \"Proposal expiration time is too far in the future.\" );\n   FC_ASSERT( !o.review_period_seconds || \n         fc::seconds( *o.review_period_seconds ) < ( o.expiration_time - block_time ),\n         \"Proposal review period must be less than its overall lifetime.\" );\n\n   // Find all authorities required by the proposed operations\n   flat_set<account_id_type> tmp_required_active_auths;\n   vector<authority> other;\n   for( auto& op : o.proposed_ops )\n   {\n      operation_get_required_authorities( op.op, tmp_required_active_auths, _required_owner_auths, other,\n                                          MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( block_time ) );\n   }\n   // All accounts which must provide both owner and active authority should be omitted from the \n   // active authority set; owner authority approval implies active authority approval.\n   std::set_difference( tmp_required_active_auths.begin(), tmp_required_active_auths.end(),\n                        _required_owner_auths.begin(), _required_owner_auths.end(),\n                        std::inserter( _required_active_auths, _required_active_auths.begin() ) );\n\n   // TODO: what about other???\n   FC_ASSERT ( other.empty(),\n               \"Proposals containing operations requiring non-account authorities are not yet implemented.\" );\n\n   // If we're dealing with the committee authority, make sure this transaction has a sufficient review period.\n   if( _required_active_auths.count( GRAPHENE_COMMITTEE_ACCOUNT ) ||\n       _required_owner_auths.count( GRAPHENE_COMMITTEE_ACCOUNT ) )\n   {\n      GRAPHENE_ASSERT( o.review_period_seconds.valid(),\n                       proposal_create_review_period_required,\n                       \"Review period not given, but at least ${min} required\",\n                       (\"min\", global_parameters.committee_proposal_review_period) );\n      GRAPHENE_ASSERT( *o.review_period_seconds >= global_parameters.committee_proposal_review_period,\n                       proposal_create_review_period_insufficient,\n                       \"Review period of ${t} specified, but at least ${min} required\",\n                       (\"t\", *o.review_period_seconds)\n                       (\"min\", global_parameters.committee_proposal_review_period) );\n   }\n\n   for( const op_wrapper& op : o.proposed_ops )\n      _proposed_trx.operations.push_back( op.op );\n\n   _proposed_trx.validate();\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nobject_id_type proposal_create_evaluator::do_apply( const proposal_create_operation& o )\n{ try {\n   database& d = db();\n   auto chain_time = d.head_block_time();\n\n   const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time](proposal_object& proposal) {\n      _proposed_trx.expiration = o.expiration_time;\n      proposal.proposed_transaction = _proposed_trx;\n      proposal.expiration_time = o.expiration_time;\n      proposal.proposer = o.fee_paying_account;\n      if( o.review_period_seconds )\n         proposal.review_period_time = o.expiration_time - *o.review_period_seconds;\n\n      //Populate the required approval sets\n      proposal.required_owner_approvals.insert( _required_owner_auths.begin(), _required_owner_auths.end() );\n      proposal.required_active_approvals.insert( _required_active_auths.begin(), _required_active_auths.end() );\n\n      if( chain_time > HARDFORK_CORE_1479_TIME )\n         FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance,\n                    \"Cannot update/delete a proposal with a future id!\" );\n      else if( vtor_1479.nested_update_count > 0 && proposal.id.instance() <= vtor_1479.max_update_instance )\n      {\n         // Note: This happened on mainnet, proposal 1.10.17503\n         // prevent approval\n         transfer_operation top;\n         top.from = GRAPHENE_NULL_ACCOUNT;\n         top.to = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;\n         top.amount = asset( GRAPHENE_MAX_SHARE_SUPPLY );\n         proposal.proposed_transaction.operations.emplace_back( top );\n      }\n   });\n\n   return proposal.id;\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result proposal_update_evaluator::do_evaluate( const proposal_update_operation& o )\n{ try {\n   database& d = db();\n\n   _proposal = &o.proposal(d);\n\n   if( _proposal->review_period_time && d.head_block_time() >= *_proposal->review_period_time )\n      FC_ASSERT( o.active_approvals_to_add.empty() && o.owner_approvals_to_add.empty(),\n                 \"This proposal is in its review period. No new approvals may be added.\" );\n\n   for( account_id_type id : o.active_approvals_to_remove )\n   {\n      FC_ASSERT( _proposal->available_active_approvals.find(id) != _proposal->available_active_approvals.end(),\n                 \"\", (\"id\", id)(\"available\", _proposal->available_active_approvals) );\n   }\n   for( account_id_type id : o.owner_approvals_to_remove )\n   {\n      FC_ASSERT( _proposal->available_owner_approvals.find(id) != _proposal->available_owner_approvals.end(),\n                 \"\", (\"id\", id)(\"available\", _proposal->available_owner_approvals) );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result proposal_update_evaluator::do_apply(const proposal_update_operation& o)\n{ try {\n   database& d = db();\n\n   // Potential optimization: if _executed_proposal is true, we can skip the modify step and make push_proposal \n   // skip signature checks. This isn't done now because I just wrote all the proposals code, and I'm not yet \n   // 100% sure the required approvals are sufficient to authorize the transaction.\n   d.modify(*_proposal, [&o](proposal_object& p) {\n      p.available_active_approvals.insert(o.active_approvals_to_add.begin(), o.active_approvals_to_add.end());\n      p.available_owner_approvals.insert(o.owner_approvals_to_add.begin(), o.owner_approvals_to_add.end());\n      for( account_id_type id : o.active_approvals_to_remove )\n         p.available_active_approvals.erase(id);\n      for( account_id_type id : o.owner_approvals_to_remove )\n         p.available_owner_approvals.erase(id);\n      for( const auto& id : o.key_approvals_to_add )\n         p.available_key_approvals.insert(id);\n      for( const auto& id : o.key_approvals_to_remove )\n         p.available_key_approvals.erase(id);\n   });\n\n   // If the proposal has a review period, don't bother attempting to authorize/execute it.\n   // Proposals with a review period may never be executed except at their expiration.\n   if( _proposal->review_period_time )\n      return void_result();\n\n   if( _proposal->is_authorized_to_execute(d) )\n   {\n      // All required approvals are satisfied. Execute!\n      _executed_proposal = true;\n      try {\n         _processed_transaction = d.push_proposal(*_proposal);\n      } catch(fc::exception& e) {\n         d.modify(*_proposal, [&e](proposal_object& p) {\n            p.fail_reason = e.to_string(fc::log_level(fc::log_level::all));\n         });\n         wlog(\"Proposed transaction ${id} failed to apply once approved with exception:\\n----\\n${reason}\\n----\\nWill try again when it expires.\",\n              (\"id\", o.proposal)(\"reason\", e.to_detail_string()));\n         _proposal_failed = true;\n      }\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result proposal_delete_evaluator::do_evaluate(const proposal_delete_operation& o)\n{ try {\n   database& d = db();\n\n   _proposal = &o.proposal(d);\n\n   auto required_approvals = o.using_owner_authority? &_proposal->required_owner_approvals\n                                                    : &_proposal->required_active_approvals;\n   FC_ASSERT( required_approvals->find(o.fee_paying_account) != required_approvals->end(),\n              \"Provided authority is not authoritative for this proposal.\",\n              (\"provided\", o.fee_paying_account)(\"required\", *required_approvals));\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid_result proposal_delete_evaluator::do_apply(const proposal_delete_operation& o)\n{ try {\n   db().remove(*_proposal);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/proposal_object.cpp",
    "content": "/*\n * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/transaction_evaluation_state.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/restriction_predicate.hpp>\n\nnamespace graphene { namespace chain {\n\nbool proposal_object::is_authorized_to_execute( database& db ) const\n{\n   transaction_evaluation_state dry_run_eval( &db );\n\n   try {\n      bool allow_non_immediate_owner = ( db.head_block_time() >= HARDFORK_CORE_584_TIME );\n      verify_authority( proposed_transaction.operations,\n                        available_key_approvals,\n                        [&db]( account_id_type id ){ return &id( db ).active; },\n                        [&db]( account_id_type id ){ return &id( db ).owner;  },\n                        [&db]( account_id_type id, const operation& op, rejected_predicate_map* rejects ){\n                           return db.get_viable_custom_authorities(id, op, rejects); },\n                        allow_non_immediate_owner,\n                        MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ),\n                        db.get_global_properties().parameters.max_authority_depth,\n                        true, /* allow committee */\n                        available_active_approvals,\n                        available_owner_approvals );\n   } \n   catch ( const fc::exception& e )\n   {\n      return false;\n   }\n   return true;\n}\n\nvoid required_approval_index::object_inserted( const object& obj )\n{\n    assert( dynamic_cast<const proposal_object*>(&obj) );\n    const proposal_object& p = static_cast<const proposal_object&>(obj);\n\n    for( const auto& a : p.required_active_approvals )\n       _account_to_proposals[a].insert( p.id );\n    for( const auto& a : p.required_owner_approvals )\n       _account_to_proposals[a].insert( p.id );\n    for( const auto& a : p.available_active_approvals )\n       _account_to_proposals[a].insert( p.id );\n    for( const auto& a : p.available_owner_approvals )\n       _account_to_proposals[a].insert( p.id );\n}\n\nvoid required_approval_index::remove( account_id_type a, proposal_id_type p )\n{\n    auto itr = _account_to_proposals.find(a);\n    if( itr != _account_to_proposals.end() )\n    {\n        itr->second.erase( p );\n        if( itr->second.empty() )\n            _account_to_proposals.erase( itr->first );\n    }\n}\n\nvoid required_approval_index::object_removed( const object& obj )\n{\n    assert( dynamic_cast<const proposal_object*>(&obj) );\n    const proposal_object& p = static_cast<const proposal_object&>(obj);\n\n    for( const auto& a : p.required_active_approvals )\n       remove( a, p.id );\n    for( const auto& a : p.required_owner_approvals )\n       remove( a, p.id );\n    for( const auto& a : p.available_active_approvals )\n       remove( a, p.id );\n    for( const auto& a : p.available_owner_approvals )\n       remove( a, p.id );\n}\n\nvoid required_approval_index::insert_or_remove_delta( proposal_id_type p,\n                                                      const flat_set<account_id_type>& before,\n                                                      const flat_set<account_id_type>& after )\n{\n    auto b = before.begin();\n    auto a = after.begin();\n    while( b != before.end() || a != after.end() )\n    {\n       if( a == after.end() || (b != before.end() && *b < *a) )\n       {\n           remove( *b, p );\n           ++b;\n       }\n       else if( b == before.end() || (a != after.end() && *a < *b) )\n       {\n           _account_to_proposals[*a].insert( p );\n           ++a;\n       }\n       else // *a == *b\n       {\n           ++a;\n           ++b;\n       }\n    }\n}\n\nvoid required_approval_index::about_to_modify( const object& before )\n{\n    const proposal_object& p = static_cast<const proposal_object&>(before);\n    available_active_before_modify = p.available_active_approvals;\n    available_owner_before_modify  = p.available_owner_approvals;\n}\n\nvoid required_approval_index::object_modified( const object& after )\n{\n    const proposal_object& p = static_cast<const proposal_object&>(after);\n    insert_or_remove_delta( p.id, available_active_before_modify, p.available_active_approvals );\n    insert_or_remove_delta( p.id, available_owner_before_modify,  p.available_owner_approvals );\n}\n\n} } // graphene::chain\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::proposal_object, (graphene::chain::object),\n                    (expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals)\n                    (available_active_approvals)(required_owner_approvals)(available_owner_approvals)\n                    (available_key_approvals)(proposer)(fail_reason) )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::proposal_object )\n"
  },
  {
    "path": "libraries/chain/small_objects.cpp",
    "content": "/*\n * Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/block_summary_object.hpp>\n#include <graphene/chain/budget_record_object.hpp>\n#include <graphene/chain/buyback_object.hpp>\n#include <graphene/chain/chain_property_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/global_property_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/liquidity_pool_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/special_authority_object.hpp>\n#include <graphene/chain/transaction_history_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/witness_schedule_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n\n#include <fc/io/raw.hpp>\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::balance_object, (graphene::db::object),\n                    (owner)(balance)(vesting_policy)(last_claim_date) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::block_summary_object, (graphene::db::object), (block_id) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME(\n   graphene::chain::budget_record, BOOST_PP_SEQ_NIL,\n   (time_since_last_budget)\n   (from_initial_reserve)\n   (from_accumulated_fees)\n   (from_unused_witness_budget)\n   (requested_witness_budget)\n   (total_budget)\n   (witness_budget)\n   (worker_budget)\n   (leftover_worker_funds)\n   (supply_delta)\n)\n\nFC_REFLECT_DERIVED_NO_TYPENAME(\n   graphene::chain::budget_record_object,\n   (graphene::db::object),\n   (time)\n   (record)\n)\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::buyback_object, (graphene::db::object), (asset_to_buy) )\n\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::immutable_chain_parameters, BOOST_PP_SEQ_NIL,\n   (min_committee_member_count)\n   (min_witness_count)\n   (num_special_accounts)\n   (num_special_assets)\n)\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::chain_property_object, (graphene::db::object),\n                    (chain_id)\n                    (immutable_parameters)\n                  )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::committee_member_object, (graphene::db::object),\n                    (committee_member_account)(vote_id)(total_votes)(url) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::blinded_balance_object, (graphene::db::object),\n                                (commitment)(asset_id)(owner) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::fba_accumulator_object, (graphene::db::object),\n                                (accumulated_fba_fees)(designated_asset) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::dynamic_global_property_object, (graphene::db::object),\n                    (head_block_number)\n                    (head_block_id)\n                    (time)\n                    (current_witness)\n                    (next_maintenance_time)\n                    (last_budget_time)\n                    (witness_budget)\n                    (total_pob)\n                    (total_inactive)\n                    (accounts_registered_this_interval)\n                    (recently_missed_count)\n                    (current_aslot)\n                    (recent_slots_filled)\n                    (dynamic_flags)\n                    (last_irreversible_block_num)\n                  )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::global_property_object, (graphene::db::object),\n                    (parameters)\n                    (pending_parameters)\n                    (next_available_vote_id)\n                    (active_committee_members)\n                    (active_witnesses)\n                  )\n\nFC_REFLECT( graphene::chain::htlc_object::transfer_info,\n   (from) (to) (amount) (asset_id) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object::condition_info::hash_lock_info, BOOST_PP_SEQ_NIL,\n   (preimage_hash) (preimage_size) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object::condition_info::time_lock_info, BOOST_PP_SEQ_NIL,\n   (expiration) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object::condition_info, BOOST_PP_SEQ_NIL,\n   (hash_lock)(time_lock) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object, (graphene::db::object),\n               (transfer) (conditions) (memo) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::operation_history_object, (graphene::chain::object),\n                    (op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::account_transaction_history_object, (graphene::chain::object),\n                    (account)(operation_id)(sequence)(next) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME(\n   graphene::chain::special_authority_object,\n   (graphene::db::object),\n   (account)\n)\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::transaction_history_object, (graphene::db::object), (trx)(trx_id) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::withdraw_permission_object, (graphene::db::object),\n                    (withdraw_from_account)\n                    (authorized_account)\n                    (withdrawal_limit)\n                    (withdrawal_period_sec)\n                    (period_start_time)\n                    (expiration)\n                    (claimed_this_period)\n                 )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::witness_object, (graphene::db::object),\n                    (witness_account)\n                    (last_aslot)\n                    (signing_key)\n                    (pay_vb)\n                    (vote_id)\n                    (total_votes)\n                    (url)\n                    (total_missed)\n                    (last_confirmed_block_num)\n                  )\n\nFC_REFLECT_DERIVED_NO_TYPENAME(\n   graphene::chain::witness_schedule_object,\n   (graphene::db::object),\n   (current_shuffled_witnesses)\n)\n\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::refund_worker_type, BOOST_PP_SEQ_NIL, (total_burned) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::vesting_balance_worker_type, BOOST_PP_SEQ_NIL, (balance) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::burn_worker_type, BOOST_PP_SEQ_NIL, (total_burned) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::worker_object, (graphene::db::object),\n                    (worker_account)\n                    (work_begin_date)\n                    (work_end_date)\n                    (daily_pay)\n                    (worker)\n                    (vote_for)\n                    (vote_against)\n                    (total_votes_for)\n                    (total_votes_against)\n                    (name)\n                    (url)\n                  )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::custom_authority_object, (graphene::db::object),\n                               (account)(enabled)(valid_from)(valid_to)(operation_type)\n                               (auth)(restrictions)(restriction_counter) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::liquidity_pool_object, (graphene::db::object),\n                    (asset_a)\n                    (asset_b)\n                    (balance_a)\n                    (balance_b)\n                    (share_asset)\n                    (taker_fee_percent)\n                    (withdrawal_fee_percent)\n                    (virtual_value)\n                  )\n\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::balance_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::block_summary_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::budget_record )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::budget_record_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::buyback_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::immutable_chain_parameters )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::chain_property_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::committee_member_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::blinded_balance_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::fba_accumulator_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::dynamic_global_property_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::global_property_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::htlc_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::operation_history_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::account_transaction_history_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::special_authority_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::transaction_history_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::withdraw_permission_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::witness_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::witness_schedule_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::worker_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::custom_authority_object )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::liquidity_pool_object )\n"
  },
  {
    "path": "libraries/chain/special_authority_evaluation.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/special_authority_evaluation.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace chain {\n\nstruct special_authority_evaluate_visitor\n{\n   typedef void result_type;\n\n   special_authority_evaluate_visitor( const database& d ) : db(d) {}\n\n   void operator()( const no_special_authority& a ) {}\n\n   void operator()( const top_holders_special_authority& a )\n   {\n      db.get(a.asset);     // require asset to exist\n   }\n\n   const database& db;\n};\n\nvoid evaluate_special_authority( const database& db, const special_authority& a )\n{\n   special_authority_evaluate_visitor vtor( db );\n   a.visit( vtor );\n}\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/ticket_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/ticket_object.hpp>\n\n#include <graphene/chain/ticket_evaluator.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/ticket.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result ticket_create_evaluator::do_evaluate(const ticket_create_operation& op)\n{ try {\n   const database& d = db();\n   const auto block_time = d.head_block_time();\n\n   FC_ASSERT( HARDFORK_CORE_2103_PASSED(block_time), \"Not allowed until hardfork 2103\" );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nobject_id_type ticket_create_evaluator::do_apply(const ticket_create_operation& op)\n{ try {\n   database& d = db();\n   const auto block_time = d.head_block_time();\n\n   d.adjust_balance( op.account, -op.amount );\n\n   const auto& new_ticket_object = d.create<ticket_object>([&op,block_time](ticket_object& obj){\n      obj.init_new( block_time, op.account, op.target_type, op.amount );\n   });\n\n   // Note: amount.asset_id is checked in validate(), so no check here\n   d.modify( d.get_account_stats_by_owner( op.account ), [&op,&new_ticket_object](account_statistics_object& aso) {\n      aso.total_core_pol += op.amount.amount;\n      aso.total_pol_value += new_ticket_object.value;\n   });\n\n   return new_ticket_object.id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result ticket_update_evaluator::do_evaluate(const ticket_update_operation& op)\n{ try {\n   database& d = db();\n\n   _ticket = &op.ticket(d);\n\n   FC_ASSERT( _ticket->account == op.account, \"Ticket is not owned by the account\" );\n\n   FC_ASSERT( _ticket->current_type != lock_forever, \"Can not to update a ticket that is locked forever\" );\n\n   FC_ASSERT( static_cast<uint64_t>(_ticket->target_type) != op.target_type, \"Target type does not change\" );\n\n   if( op.amount_for_new_target.valid() )\n   {\n      // Note: amount.asset_id is checked in validate(), so no check here\n      FC_ASSERT( *op.amount_for_new_target <= _ticket->amount, \"Insufficient amount in ticket to be updated\" );\n   }\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\ngeneric_operation_result ticket_update_evaluator::do_apply(const ticket_update_operation& op)\n{ try {\n   database& d = db();\n   const auto block_time = d.head_block_time();\n\n   generic_operation_result result;\n\n   share_type old_value = _ticket->value;\n   share_type delta_value;\n\n   // To partially update the ticket, aka splitting\n   if ( op.amount_for_new_target.valid() && *op.amount_for_new_target < _ticket->amount )\n   {\n      const auto& new_ticket_object = d.create<ticket_object>([&op,this,block_time](ticket_object& obj){\n         obj.init_split( block_time, *_ticket, op.target_type, *op.amount_for_new_target );\n      });\n\n      result.new_objects.insert( new_ticket_object.id );\n\n      d.modify( *_ticket, [&op](ticket_object& obj){\n         obj.adjust_amount( -(*op.amount_for_new_target) );\n      });\n      delta_value = new_ticket_object.value + _ticket->value - old_value;\n   }\n   else // To update the whole ticket\n   {\n      d.modify( *_ticket, [&op,block_time](ticket_object& obj){\n         obj.update_target_type( block_time, op.target_type );\n      });\n      delta_value = _ticket->value - old_value;\n   }\n   result.updated_objects.insert( _ticket->id );\n\n   if( delta_value != 0 )\n   {\n      const auto& stat = d.get_account_stats_by_owner( op.account );\n      d.modify( stat, [delta_value](account_statistics_object& aso) {\n         aso.total_pol_value += delta_value;\n      });\n   }\n\n   // Do auto-update now.\n   // Note: calling process_tickets() here won't affect other tickets,\n   //       since head_block_time is not updated after last call,\n   //       even when called via a proposal this time or last time\n   generic_operation_result process_result = d.process_tickets();\n   result.removed_objects.insert( process_result.removed_objects.begin(), process_result.removed_objects.end() );\n   result.updated_objects.insert( process_result.updated_objects.begin(), process_result.updated_objects.end() );\n   for( const auto id : result.new_objects )\n      result.updated_objects.erase( id );\n   for( const auto id : result.removed_objects )\n      result.updated_objects.erase( id );\n\n   return result;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/ticket_object.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/ticket_object.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/io/raw.hpp>\n#include <iostream>\n#include <ctime>\n\nusing namespace graphene::chain;\n\nvoid ticket_object::init_new( time_point_sec now, account_id_type new_account,\n                              ticket_type new_target_type, const asset& new_amount )\n{\n   account = new_account;\n   target_type = new_target_type;\n   amount = new_amount;\n\n   current_type = liquid;\n   status = charging;\n   next_auto_update_time = now + seconds_per_charging_step;\n   next_type_downgrade_time = time_point_sec::maximum();\n\n   update_value();\n}\n//新起锁仓\nvoid ticket_object::init_split( time_point_sec now, const ticket_object& old_ticket,\n                                ticket_type new_target_type, const asset& new_amount )\n{\n   account = old_ticket.account;\n   target_type = old_ticket.target_type;\n   amount = new_amount;\n\n   current_type = old_ticket.current_type;\n   status = old_ticket.status;\n   next_auto_update_time = time_point_sec::maximum();;\n   next_type_downgrade_time = old_ticket.next_type_downgrade_time;\n\n   update_target_type( now, new_target_type );\n}\n//永远无法升级到下一阶段\nvoid ticket_object::update_target_type( time_point_sec now, ticket_type new_target_type )\n{\n   //const database& _db = db();\n   //const auto block_time = _db.head_block_time();\n\n   if( current_type < new_target_type )\n   {\n      if( status != charging )\n      {\n         status = charging;\n         next_auto_update_time = time_point_sec::maximum();;\n      }\n      // else do nothing here\n   }\n   else // current_type >= new_target_type\n   {\n      status = withdrawing;\n      if( next_type_downgrade_time == time_point_sec::maximum() ) // no downgrade was started ago\n      {\n         if( current_type == new_target_type ) // was charging, to cancel\n            next_type_downgrade_time = now + seconds_to_cancel_charging;\n         else // was stable or charging, to downgrade\n         {\n            current_type = static_cast<ticket_type>( static_cast<uint8_t>(current_type) - 1 );\n            if( HARDFORK_CORE_2103F_PASSED(now) )\n               next_type_downgrade_time = now + seconds_to_downgrade(current_type);\n            else\n               next_type_downgrade_time = now + seconds_to_downgrade(current_type);\n\n         }\n      }\n      // else a downgrade was started ago, keep the old value of `next_type_downgrade_time`\n      // Note: it's possible that `next_type_downgrade_time` is in the past,\n      //       in this case, it will be processed in database::process_tickets().\n      next_auto_update_time = next_type_downgrade_time;\n   }\n   target_type = new_target_type;\n\n   update_value();\n}\n\nvoid ticket_object::adjust_amount( const asset& delta_amount )\n{\n   amount += delta_amount;\n   update_value();\n}\n\nvoid ticket_object::auto_update()\n{\n   //const database& _db = db();\n   //const auto block_time = _db.head_block_time();\n   time_point_sec now = time_point_sec( time_point::now() ) ;  \n\n   if( status == charging )\n   {\n      current_type = static_cast<ticket_type>( static_cast<uint8_t>(current_type) + 1 );\n      next_type_downgrade_time = time_point_sec::maximum();\n      if( current_type < target_type )\n      {\n         next_auto_update_time = time_point_sec::maximum();\n      }\n      else // reached target, stop\n      {\n         if( current_type != lock_forever )\n         {\n            status = stable;\n            next_auto_update_time = time_point_sec::maximum();\n         }\n         else // lock forever\n         {\n            status = withdrawing;\n            next_auto_update_time += seconds_per_lock_forever_update_step;\n            if( HARDFORK_CORE_2103F_PASSED(now) )\n            {\n               value = amount.amount * 1;\n            }\n            else\n            {\n               value = amount.amount * value_multiplier(current_type);\n            }\n         }\n      }\n   }\n   else // status == withdrawing\n   {\n      // Note: current_type != liquid, guaranteed by the caller\n      if( current_type == lock_forever )\n      {\n         share_type delta_value = 0;\n         if( HARDFORK_CORE_2103F_PASSED(now) )\n         {\n            delta_value = amount.amount * 1 / lock_forever_update_steps;\n         }\n         else\n         {         \n            delta_value = amount.amount * value_multiplier(current_type) / lock_forever_update_steps;\n         }\n         \n         if( delta_value <= 0 )\n            delta_value = 1;\n         if( value <= delta_value )\n         {\n            value = 0;\n            status = stable;\n            next_auto_update_time = time_point_sec::maximum();\n         }\n         else\n         {\n            value -= delta_value;\n            next_auto_update_time += seconds_per_lock_forever_update_step;\n         }\n      }\n      else if( current_type == target_type ) // finished downgrading\n      {\n         status = stable;\n         next_type_downgrade_time = time_point_sec::maximum();\n         next_auto_update_time = time_point_sec::maximum();\n      }\n      else // to downgrade\n      {\n         current_type = static_cast<ticket_type>( static_cast<uint8_t>(current_type) - 1 );\n         if( HARDFORK_CORE_2103F_PASSED(now) )\n         {\n            next_type_downgrade_time = next_type_downgrade_time + ( seconds_to_downgrade(current_type) * 10 );\n         }\n         else\n         {\n            next_type_downgrade_time += seconds_to_downgrade(current_type);\n         }\n         next_auto_update_time = time_point_sec::maximum();;\n      }\n   }\n\n   update_value();\n}\n\nvoid ticket_object::update_value()\n{\n   //const database& _db = db();\n   //const auto block_time = _db.head_block_time();\n   time_point_sec now = time_point_sec( time_point::now() ) ;\n\n   if( current_type != lock_forever )\n   {\n      if( HARDFORK_CORE_2103F_PASSED(now) )\n         value = amount.amount * 1;\n      else\n         value = amount.amount * value_multiplier(current_type);\n   }\n   // else lock forever and to be updated, do nothing here\n}\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::ticket_object, (graphene::db::object),\n                    (account)\n                    (target_type)\n                    (amount)\n                    (current_type)\n                    (status)\n                    (value)\n                    (next_auto_update_time)\n                    (next_type_downgrade_time)\n                  )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::ticket_object )\n"
  },
  {
    "path": "libraries/chain/transfer_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/transfer_evaluator.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\nnamespace graphene { namespace chain {\nvoid_result transfer_evaluator::do_evaluate( const transfer_operation& op )\n{ try {\n   \n   const database& d = db();\n\n   const account_object& from_account    = op.from(d);\n   const account_object& to_account      = op.to(d);\n   const asset_object&   asset_type      = op.amount.asset_id(d);\n\n   try {\n\n      GRAPHENE_ASSERT(\n         is_authorized_asset( d, from_account, asset_type ),\n         transfer_from_account_not_whitelisted,\n         \"'from' account ${from} is not whitelisted for asset ${asset}\",\n         (\"from\",op.from)\n         (\"asset\",op.amount.asset_id)\n         );\n      GRAPHENE_ASSERT(\n         is_authorized_asset( d, to_account, asset_type ),\n         transfer_to_account_not_whitelisted,\n         \"'to' account ${to} is not whitelisted for asset ${asset}\",\n         (\"to\",op.to)\n         (\"asset\",op.amount.asset_id)\n         );\n\n      if( asset_type.is_transfer_restricted() )\n      {\n         GRAPHENE_ASSERT(\n            from_account.id == asset_type.issuer || to_account.id == asset_type.issuer,\n            transfer_restricted_transfer_asset,\n            \"Asset ${asset} has transfer_restricted flag enabled\",\n            (\"asset\", op.amount.asset_id)\n          );\n      }\n\n      bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= op.amount.amount;\n      FC_ASSERT( insufficient_balance,\n                 \"Insufficient Balance: ${balance}, unable to transfer '${total_transfer}' from account '${a}' to '${t}'\", \n                 (\"a\",from_account.name)(\"t\",to_account.name)(\"total_transfer\",d.to_pretty_string(op.amount))(\"balance\",d.to_pretty_string(d.get_balance(from_account, asset_type))) );\n\n      return void_result();\n   } FC_RETHROW_EXCEPTIONS( error, \"Unable to transfer ${a} from ${f} to ${t}\", (\"a\",d.to_pretty_string(op.amount))(\"f\",op.from(d).name)(\"t\",op.to(d).name) );\n\n}  FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result transfer_evaluator::do_apply( const transfer_operation& o )\n{ try {\n   db().adjust_balance( o.from, -o.amount );\n   db().adjust_balance( o.to, o.amount );\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\n\nvoid_result override_transfer_evaluator::do_evaluate( const override_transfer_operation& op )\n{ try {\n   const database& d = db();\n\n   const asset_object&   asset_type      = op.amount.asset_id(d);\n   GRAPHENE_ASSERT(\n      asset_type.can_override(),\n      override_transfer_not_permitted,\n      \"override_transfer not permitted for asset ${asset}\",\n      (\"asset\", op.amount.asset_id)\n      );\n   FC_ASSERT( asset_type.issuer == op.issuer );\n\n   const account_object& from_account    = op.from(d);\n   const account_object& to_account      = op.to(d);\n\n   FC_ASSERT( is_authorized_asset( d, to_account, asset_type ) );\n   FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) );\n\n   FC_ASSERT( d.get_balance( from_account, asset_type ).amount >= op.amount.amount,\n              \"\", (\"total_transfer\",op.amount)(\"balance\",d.get_balance(from_account, asset_type).amount) );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result override_transfer_evaluator::do_apply( const override_transfer_operation& o )\n{ try {\n   db().adjust_balance( o.from, -o.amount );\n   db().adjust_balance( o.to, o.amount );\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/vesting_balance_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/vesting_balance_evaluator.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance_create_operation& op )\n{ try {\n   const database& d = db();\n\n   const account_object& creator_account = op.creator( d );\n   /* const account_object& owner_account = */ op.owner( d );\n\n   // TODO: Check asset authorizations and withdrawals\n\n   FC_ASSERT( op.amount.amount > 0 );\n   FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount );\n   FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nstruct init_policy_visitor\n{\n   typedef void result_type;\n\n   init_policy_visitor( vesting_policy& po,\n                        const share_type& begin_balance,\n                        const fc::time_point_sec& n ):p(po),init_balance(begin_balance),now(n){}\n\n   vesting_policy&    p;\n   share_type         init_balance;\n   fc::time_point_sec now;\n\n   void operator()( const linear_vesting_policy_initializer& i )const\n   {\n      linear_vesting_policy policy;\n      policy.begin_timestamp = i.begin_timestamp;\n      policy.vesting_cliff_seconds = i.vesting_cliff_seconds;\n      policy.vesting_duration_seconds = i.vesting_duration_seconds;\n      policy.begin_balance = init_balance;\n      p = policy;\n   }\n\n   void operator()( const cdd_vesting_policy_initializer& i )const\n   {\n      cdd_vesting_policy policy;\n      policy.vesting_seconds = i.vesting_seconds;\n      policy.start_claim = i.start_claim;\n      policy.coin_seconds_earned = 0;\n      policy.coin_seconds_earned_last_update = now;\n      p = policy;\n   }\n\n   void operator()( const instant_vesting_policy_initializer& i )const\n   {\n      p = instant_vesting_policy{};\n   }\n\n};\n\nobject_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op )\n{ try {\n   database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   FC_ASSERT( d.get_balance( op.creator, op.amount.asset_id ) >= op.amount );\n   d.adjust_balance( op.creator, -op.amount );\n\n   const vesting_balance_object& vbo = d.create< vesting_balance_object >( [&]( vesting_balance_object& obj )\n   {\n      //WARNING: The logic to create a vesting balance object is replicated in vesting_balance_worker_type::initializer::init.\n      // If making changes to this logic, check if those changes should also be made there as well.\n      obj.owner = op.owner;\n      obj.balance = op.amount;\n      op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) );\n   } );\n\n\n   return vbo.id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balance_withdraw_operation& op )\n{ try {\n   const database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   const vesting_balance_object& vbo = op.vesting_balance( d );\n   FC_ASSERT( op.owner == vbo.owner, \"\", (\"op.owner\", op.owner)(\"vbo.owner\", vbo.owner) );\n   FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), \"\", (\"now\", now)(\"op\", op)(\"vbo\", vbo) );\n   assert( op.amount <= vbo.balance );      // is_withdraw_allowed should fail before this check is reached\n\n   /* const account_object& owner_account = */ op.owner( d );\n   // TODO: Check asset authorizations and withdrawals\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_withdraw_operation& op )\n{ try {\n   database& d = db();\n   const time_point_sec now = d.head_block_time();\n\n   const vesting_balance_object& vbo = op.vesting_balance( d );\n\n   // Allow zero balance objects to stick around, (1) to comply\n   // with the chain's \"objects live forever\" design principle, (2)\n   // if it's cashback or worker, it'll be filled up again.\n\n   d.modify( vbo, [&]( vesting_balance_object& vbo )\n   {\n      vbo.withdraw( now, op.amount );\n   } );\n\n   d.adjust_balance( op.owner, op.amount );\n\n   // TODO: Check asset authorizations and withdrawals\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/vesting_balance_object.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/vesting_balance_object.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace chain {\n\ninline bool sum_below_max_shares(const asset& a, const asset& b)\n{\n   assert(GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY);\n   return (a.amount              <= GRAPHENE_MAX_SHARE_SUPPLY)\n       && (            b.amount  <= GRAPHENE_MAX_SHARE_SUPPLY)\n       && ((a.amount + b.amount) <= GRAPHENE_MAX_SHARE_SUPPLY);\n}\n\nasset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const\n{\n    share_type allowed_withdraw = 0;\n\n    if( ctx.now > begin_timestamp )\n    {\n        const auto elapsed_seconds = (ctx.now - begin_timestamp).to_seconds();\n        assert( elapsed_seconds > 0 );\n\n        if( elapsed_seconds >= vesting_cliff_seconds )\n        {\n            share_type total_vested = 0;\n            if( elapsed_seconds < vesting_duration_seconds )\n            {\n                total_vested = static_cast<uint64_t>(fc::uint128_t( begin_balance.value ) * elapsed_seconds\n                                                     / vesting_duration_seconds);\n            }\n            else\n            {\n                total_vested = begin_balance;\n            }\n            assert( total_vested >= 0 );\n\n            const share_type withdrawn_already = begin_balance - ctx.balance.amount;\n            assert( withdrawn_already >= 0 );\n\n            allowed_withdraw = total_vested - withdrawn_already;\n            assert( allowed_withdraw >= 0 );\n        }\n    }\n\n    return asset( allowed_withdraw, ctx.balance.asset_id );\n}\n\nvoid linear_vesting_policy::on_deposit(const vesting_policy_context& ctx)\n{\n}\n\nbool linear_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const\n{\n   return (ctx.amount.asset_id == ctx.balance.asset_id)\n      && sum_below_max_shares(ctx.amount, ctx.balance);\n}\n\nvoid linear_vesting_policy::on_withdraw(const vesting_policy_context& ctx)\n{\n}\n\nbool linear_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const\n{\n   return (ctx.amount.asset_id == ctx.balance.asset_id)\n          && (ctx.amount <= get_allowed_withdraw(ctx));\n}\n\nfc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned(const vesting_policy_context& ctx)const\n{\n   assert(ctx.now >= coin_seconds_earned_last_update);\n   int64_t delta_seconds = (ctx.now - coin_seconds_earned_last_update).to_seconds();\n   assert(delta_seconds >= 0);\n\n   fc::uint128_t delta_coin_seconds = ctx.balance.amount.value;\n   delta_coin_seconds *= delta_seconds;\n\n   fc::uint128_t coin_seconds_earned_cap = ctx.balance.amount.value;\n   coin_seconds_earned_cap *= std::max(vesting_seconds, 1u);\n\n   return std::min(coin_seconds_earned + delta_coin_seconds, coin_seconds_earned_cap);\n}\n\nvoid cdd_vesting_policy::update_coin_seconds_earned(const vesting_policy_context& ctx)\n{\n   coin_seconds_earned = compute_coin_seconds_earned(ctx);\n   coin_seconds_earned_last_update = ctx.now;\n}\n\nasset cdd_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx)const\n{\n   if(ctx.now <= start_claim)\n      return asset(0, ctx.balance.asset_id);\n   fc::uint128_t cs_earned = compute_coin_seconds_earned(ctx);\n   fc::uint128_t withdraw_available = cs_earned / std::max(vesting_seconds, 1u);\n   assert(withdraw_available <= static_cast<fc::uint128_t>(ctx.balance.amount.value));\n   return asset(static_cast<uint64_t>(withdraw_available), ctx.balance.asset_id);\n}\n\nvoid cdd_vesting_policy::on_deposit(const vesting_policy_context& ctx)\n{\n   update_coin_seconds_earned(ctx);\n}\n\nvoid cdd_vesting_policy::on_deposit_vested(const vesting_policy_context& ctx)\n{\n   on_deposit(ctx);\n   coin_seconds_earned += ctx.amount.amount.value * std::max(vesting_seconds, 1u);\n}\n\nvoid cdd_vesting_policy::on_withdraw(const vesting_policy_context& ctx)\n{\n   update_coin_seconds_earned(ctx);\n   fc::uint128_t coin_seconds_needed = ctx.amount.amount.value;\n   coin_seconds_needed *= std::max(vesting_seconds, 1u);\n   // is_withdraw_allowed should forbid any withdrawal that\n   // would trigger this assert\n   assert(coin_seconds_needed <= coin_seconds_earned);\n\n   coin_seconds_earned -= coin_seconds_needed;\n}\n\nbool cdd_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const\n{\n   return (ctx.amount.asset_id == ctx.balance.asset_id)\n         && sum_below_max_shares(ctx.amount, ctx.balance);\n}\n\nbool cdd_vesting_policy::is_deposit_vested_allowed(const vesting_policy_context& ctx) const\n{\n   return is_deposit_allowed(ctx);\n}\n\nbool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const\n{\n   return (ctx.amount <= get_allowed_withdraw(ctx));\n}\n\nasset instant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const\n{\n   return ctx.balance;\n}\n\nvoid instant_vesting_policy::on_deposit(const vesting_policy_context& ctx)\n{\n}\n\nvoid instant_vesting_policy::on_deposit_vested(const vesting_policy_context&)\n{\n\n}\n\nbool instant_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const\n{\n   return (ctx.amount.asset_id == ctx.balance.asset_id)\n      && sum_below_max_shares(ctx.amount, ctx.balance);\n}\n\nvoid instant_vesting_policy::on_withdraw(const vesting_policy_context& ctx)\n{\n}\n\nbool instant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const\n{\n   return (ctx.amount.asset_id == ctx.balance.asset_id)\n          && (ctx.amount <= get_allowed_withdraw(ctx));\n}\n\n#define VESTING_VISITOR(NAME, MAYBE_CONST)                    \\\nstruct NAME ## _visitor                                       \\\n{                                                             \\\n   typedef decltype(                                          \\\n      std::declval<linear_vesting_policy>().NAME(             \\\n         std::declval<vesting_policy_context>())              \\\n     ) result_type;                                           \\\n                                                              \\\n   NAME ## _visitor(                                          \\\n      const asset& balance,                                   \\\n      const time_point_sec& now,                              \\\n      const asset& amount                                     \\\n     )                                                        \\\n   : ctx(balance, now, amount) {}                             \\\n                                                              \\\n   template< typename Policy >                                \\\n   result_type                                                \\\n   operator()(MAYBE_CONST Policy& policy) MAYBE_CONST         \\\n   {                                                          \\\n      return policy.NAME(ctx);                                \\\n   }                                                          \\\n                                                              \\\n   vesting_policy_context ctx;                                \\\n}\n\nVESTING_VISITOR(on_deposit,);\nVESTING_VISITOR(on_deposit_vested,);\nVESTING_VISITOR(on_withdraw,);\nVESTING_VISITOR(is_deposit_allowed, const);\nVESTING_VISITOR(is_deposit_vested_allowed, const);\nVESTING_VISITOR(is_withdraw_allowed, const);\nVESTING_VISITOR(get_allowed_withdraw, const);\n\nbool vesting_balance_object::is_deposit_allowed(const time_point_sec& now, const asset& amount)const\n{\n   return policy.visit(is_deposit_allowed_visitor(balance, now, amount));\n}\n\nbool vesting_balance_object::is_withdraw_allowed(const time_point_sec& now, const asset& amount)const\n{\n   bool result = policy.visit(is_withdraw_allowed_visitor(balance, now, amount));\n   // if some policy allows you to withdraw more than your balance,\n   //    there's a programming bug in the policy algorithm\n   assert((amount <= balance) || (!result));\n   return result;\n}\n\nvoid vesting_balance_object::deposit(const time_point_sec& now, const asset& amount)\n{\n   on_deposit_visitor vtor(balance, now, amount);\n   policy.visit(vtor);\n   balance += amount;\n}\n\nvoid vesting_balance_object::deposit_vested(const time_point_sec& now, const asset& amount)\n{\n   on_deposit_vested_visitor vtor(balance, now, amount);\n   policy.visit(vtor);\n   balance += amount;\n}\n\nbool vesting_balance_object::is_deposit_vested_allowed(const time_point_sec& now, const asset& amount) const\n{\n   return policy.visit(is_deposit_vested_allowed_visitor(balance, now, amount));\n}\n\nvoid vesting_balance_object::withdraw(const time_point_sec& now, const asset& amount)\n{\n   assert(amount <= balance);\n   on_withdraw_visitor vtor(balance, now, amount);\n   policy.visit(vtor);\n   balance -= amount;\n}\n\nasset vesting_balance_object::get_allowed_withdraw(const time_point_sec& now)const\n{\n   asset amount = asset();\n   return policy.visit(get_allowed_withdraw_visitor(balance, now, amount));\n}\n\n} } // graphene::chain\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::linear_vesting_policy )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::cdd_vesting_policy )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::vesting_balance_object )\n"
  },
  {
    "path": "libraries/chain/withdraw_permission_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/withdraw_permission_evaluator.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result withdraw_permission_create_evaluator::do_evaluate(const operation_type& op)\n{ try {\n   database& d = db();\n   FC_ASSERT(d.find_object(op.withdraw_from_account));\n   FC_ASSERT(d.find_object(op.authorized_account));\n   FC_ASSERT(d.find_object(op.withdrawal_limit.asset_id));\n   FC_ASSERT(op.period_start_time > d.head_block_time());\n   FC_ASSERT(op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec > d.head_block_time());\n   FC_ASSERT(op.withdrawal_period_sec >= d.get_global_properties().parameters.block_interval);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nobject_id_type withdraw_permission_create_evaluator::do_apply(const operation_type& op)\n{ try {\n   return db().create<withdraw_permission_object>([&op](withdraw_permission_object& p) {\n      p.withdraw_from_account = op.withdraw_from_account;\n      p.authorized_account = op.authorized_account;\n      p.withdrawal_limit = op.withdrawal_limit;\n      p.withdrawal_period_sec = op.withdrawal_period_sec;\n      p.expiration = op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec;\n      p.period_start_time = op.period_start_time;\n   }).id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_permission_claim_evaluator::operation_type& op)\n{ try {\n   const database& d = db();\n   time_point_sec head_block_time = d.head_block_time();\n\n   const withdraw_permission_object& permit = op.withdraw_permission(d);\n   FC_ASSERT(permit.expiration > head_block_time);\n   FC_ASSERT(permit.authorized_account == op.withdraw_to_account);\n   FC_ASSERT(permit.withdraw_from_account == op.withdraw_from_account);\n   FC_ASSERT(permit.period_start_time <= head_block_time);\n   FC_ASSERT(op.amount_to_withdraw <= permit.available_this_period( head_block_time ) );\n   FC_ASSERT(d.get_balance(op.withdraw_from_account, op.amount_to_withdraw.asset_id) >= op.amount_to_withdraw);\n\n   const asset_object& _asset = op.amount_to_withdraw.asset_id(d);\n   if( _asset.is_transfer_restricted() )\n   {\n      FC_ASSERT( _asset.issuer == permit.authorized_account || _asset.issuer == permit.withdraw_from_account,\n                 \"Asset ${a} '${sym}' has transfer_restricted flag enabled\",\n                 (\"a\", _asset.id)(\"sym\", _asset.symbol) );\n   }\n\n   const account_object& to    = permit.authorized_account(d);\n   FC_ASSERT( is_authorized_asset( d, to, _asset ),\n              \"Account ${acct} '${name}' is unauthorized to transact asset ${a} '${sym}' due to whitelist / blacklist\",\n              (\"acct\", to.id)(\"name\", to.name)(\"a\", _asset.id)(\"sym\", _asset.symbol) );\n\n   const account_object& from  = op.withdraw_from_account(d);\n   bool from_is_authorized = ( is_authorized_asset( d, from, _asset ) );\n   FC_ASSERT( from_is_authorized,\n         \"Account ${acct} '${name}' is unauthorized to withdraw asset ${a} '${sym}' due to whitelist / blacklist\",\n         (\"acct\", from.id)(\"name\", from.name)(\"a\", _asset.id)(\"sym\", _asset.symbol) );\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result withdraw_permission_claim_evaluator::do_apply(const withdraw_permission_claim_evaluator::operation_type& op)\n{ try {\n   database& d = db();\n\n   const withdraw_permission_object& permit = d.get(op.withdraw_permission);\n   d.modify(permit, [&](withdraw_permission_object& p) {\n      auto periods = (d.head_block_time() - p.period_start_time).to_seconds() / p.withdrawal_period_sec;\n      p.period_start_time += periods * p.withdrawal_period_sec;\n      if( periods == 0 )\n         p.claimed_this_period += op.amount_to_withdraw.amount;\n      else\n         p.claimed_this_period = op.amount_to_withdraw.amount;\n   });\n\n   d.adjust_balance(op.withdraw_from_account, -op.amount_to_withdraw);\n   d.adjust_balance(op.withdraw_to_account, op.amount_to_withdraw);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result withdraw_permission_update_evaluator::do_evaluate(const withdraw_permission_update_evaluator::operation_type& op)\n{ try {\n   database& d = db();\n\n   const withdraw_permission_object& permit = op.permission_to_update(d);\n   FC_ASSERT(permit.authorized_account == op.authorized_account);\n   FC_ASSERT(permit.withdraw_from_account == op.withdraw_from_account);\n   FC_ASSERT(d.find_object(op.withdrawal_limit.asset_id));\n   FC_ASSERT(op.period_start_time >= d.head_block_time());\n   FC_ASSERT(op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec > d.head_block_time());\n   FC_ASSERT(op.withdrawal_period_sec >= d.get_global_properties().parameters.block_interval);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result withdraw_permission_update_evaluator::do_apply(const withdraw_permission_update_evaluator::operation_type& op)\n{ try {\n   database& d = db();\n\n   d.modify(op.permission_to_update(d), [&op](withdraw_permission_object& p) {\n      p.period_start_time = op.period_start_time;\n      p.expiration = op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec;\n      p.withdrawal_limit = op.withdrawal_limit;\n      p.withdrawal_period_sec = op.withdrawal_period_sec;\n   });\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result withdraw_permission_delete_evaluator::do_evaluate(const withdraw_permission_delete_evaluator::operation_type& op)\n{ try {\n   database& d = db();\n\n   const withdraw_permission_object& permit = op.withdrawal_permission(d);\n   FC_ASSERT(permit.authorized_account == op.authorized_account);\n   FC_ASSERT(permit.withdraw_from_account == op.withdraw_from_account);\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result withdraw_permission_delete_evaluator::do_apply(const withdraw_permission_delete_evaluator::operation_type& op)\n{ try {\n   db().remove(db().get(op.withdrawal_permission));\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/witness_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/witness_evaluator.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/protocol/vote.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result witness_create_evaluator::do_evaluate( const witness_create_operation& op )\n{ try {\n   FC_ASSERT(db().get(op.witness_account).is_lifetime_member());\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nobject_id_type witness_create_evaluator::do_apply( const witness_create_operation& op )\n{ try {\n   database& _db = db();\n   vote_id_type vote_id;\n   _db.modify( _db.get_global_properties(), [&vote_id](global_property_object& p) {\n      vote_id = vote_id_type(vote_id_type::witness, p.next_available_vote_id++);\n   });\n\n   const auto& new_witness_object = _db.create<witness_object>( [&op,&vote_id]( witness_object& obj ){\n         obj.witness_account  = op.witness_account;\n         obj.signing_key      = op.block_signing_key;\n         obj.vote_id          = vote_id;\n         obj.url              = op.url;\n   });\n   return new_witness_object.id;\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result witness_update_evaluator::do_evaluate( const witness_update_operation& op )\n{ try {\n   FC_ASSERT(db().get(op.witness).witness_account == op.witness_account);\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\nvoid_result witness_update_evaluator::do_apply( const witness_update_operation& op )\n{ try {\n   database& _db = db();\n   _db.modify(\n      _db.get(op.witness),\n      [&op]( witness_object& wit )\n      {\n         if( op.new_url.valid() )\n            wit.url = *op.new_url;\n         if( op.new_signing_key.valid() )\n            wit.signing_key = *op.new_signing_key;\n      });\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (op) ) }\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/chain/worker_evaluator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/worker_evaluator.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n\n#include <graphene/protocol/vote.hpp>\n\nnamespace graphene { namespace chain {\n\nvoid_result worker_create_evaluator::do_evaluate(const worker_create_evaluator::operation_type& o)\n{ try {\n   database& d = db();\n\n   FC_ASSERT(d.get(o.owner).is_lifetime_member());\n   FC_ASSERT(o.work_begin_date >= d.head_block_time());\n\n   return void_result();\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\n\nstruct worker_init_visitor\n{\n   typedef void result_type;\n\n   worker_object& worker;\n   database&      db;\n\n   worker_init_visitor( worker_object& w, database& d ):worker(w),db(d){}\n\n   result_type operator()( const vesting_balance_worker_initializer& i )const\n   {\n      vesting_balance_worker_type w;\n       w.balance = db.create<vesting_balance_object>([&](vesting_balance_object& b) {\n         b.owner = worker.worker_account;\n         b.balance = asset(0);\n         b.balance_type = vesting_balance_type::worker;\n\n         cdd_vesting_policy policy;\n         policy.vesting_seconds = fc::days(i.pay_vesting_period_days).to_seconds();\n         policy.coin_seconds_earned = 0;\n         policy.coin_seconds_earned_last_update = db.head_block_time();\n         b.policy = policy;\n      }).id;\n      worker.worker = w;\n   }\n\n   template<typename T>\n   result_type operator()( const T& )const\n   {\n      // DO NOTHING FOR OTHER WORKERS\n   }\n};\n\n\n\n\n\nobject_id_type worker_create_evaluator::do_apply(const worker_create_evaluator::operation_type& o)\n{ try {\n   database& d = db();\n   vote_id_type for_id, against_id;\n   d.modify(d.get_global_properties(), [&for_id, &against_id](global_property_object& p) {\n      for_id = vote_id_type(vote_id_type::worker, p.next_available_vote_id++);\n      against_id = vote_id_type(vote_id_type::worker, p.next_available_vote_id++);\n   });\n\n   return d.create<worker_object>([&](worker_object& w) {\n      w.worker_account = o.owner;\n      w.daily_pay = o.daily_pay;\n      w.work_begin_date = o.work_begin_date;\n      w.work_end_date = o.work_end_date;\n      w.name = o.name;\n      w.url = o.url;\n      w.vote_for = for_id;\n      w.vote_against = against_id;\n\n      w.worker.set_which(o.initializer.which());\n      o.initializer.visit( worker_init_visitor( w, d ) );\n   }).id;\n} FC_CAPTURE_AND_RETHROW( (o) ) }\n\nvoid refund_worker_type::pay_worker(share_type pay, database& db)\n{\n   total_burned += pay;\n   db.modify( db.get_core_dynamic_data(), [pay](asset_dynamic_data_object& d) {\n      d.current_supply -= pay;\n   });\n}\n\nvoid vesting_balance_worker_type::pay_worker(share_type pay, database& db)\n{\n   db.modify(balance(db), [&](vesting_balance_object& b) {\n      b.deposit(db.head_block_time(), asset(pay));\n   });\n}\n\n\nvoid burn_worker_type::pay_worker(share_type pay, database& db)\n{\n   total_burned += pay;\n   db.adjust_balance( GRAPHENE_NULL_ACCOUNT, pay );\n}\n\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/db/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/db/*.hpp\")\nadd_library( graphene_db undo_database.cpp index.cpp object_database.cpp ${HEADERS} )\ntarget_link_libraries( graphene_db graphene_protocol fc )\ntarget_include_directories( graphene_db PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\ninstall( TARGETS\n   graphene_db\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\ninstall( FILES ${HEADERS} DESTINATION \"include/graphene/db\" )\n"
  },
  {
    "path": "libraries/db/include/graphene/db/generic_index.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/db/index.hpp>\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n\nnamespace graphene { namespace db {\n\n   using boost::multi_index_container;\n   using namespace boost::multi_index;\n\n   struct by_id;\n   /**\n    *  Almost all objects can be tracked and managed via a boost::multi_index container that uses\n    *  an unordered_unique key on the object ID.  This template class adapts the generic index interface\n    *  to work with arbitrary boost multi_index containers on the same type.\n    */\n   template<typename ObjectType, typename MultiIndexType>\n   class generic_index : public index\n   {\n      public:\n         typedef MultiIndexType index_type;\n         typedef ObjectType     object_type;\n\n         virtual const object& insert( object&& obj )override\n         {\n            assert( nullptr != dynamic_cast<ObjectType*>(&obj) );\n            auto insert_result = _indices.insert( std::move( static_cast<ObjectType&>(obj) ) );\n            FC_ASSERT( insert_result.second, \"Could not insert object, most likely a uniqueness constraint was violated\" );\n            return *insert_result.first;\n         }\n\n         virtual const object&  create(const std::function<void(object&)>& constructor )override\n         {\n            ObjectType item;\n            item.id = get_next_id();\n            constructor( item );\n            auto insert_result = _indices.insert( std::move(item) );\n            FC_ASSERT(insert_result.second, \"Could not create object! Most likely a uniqueness constraint is violated.\");\n            use_next_id();\n            return *insert_result.first;\n         }\n\n         virtual void modify( const object& obj, const std::function<void(object&)>& m )override\n         {\n            assert(nullptr != dynamic_cast<const ObjectType*>(&obj));\n            std::exception_ptr exc;\n            auto ok = _indices.modify(_indices.iterator_to(static_cast<const ObjectType&>(obj)),\n                                       [&m, &exc](ObjectType& o) mutable {\n                                          try {\n                                             m(o);\n                                          } catch (fc::exception& e) {\n                                             exc = std::current_exception();\n                                             elog(\"Exception while modifying object: ${e} -- object may be corrupted\",\n                                                  (\"e\", e));\n                                          } catch (...) {\n                                             exc = std::current_exception();\n                                             elog(\"Unknown exception while modifying object\");\n                                          }\n                                       }\n                      );\n            if (exc)\n                std::rethrow_exception(exc);\n            FC_ASSERT(ok, \"Could not modify object, most likely an index constraint was violated\");\n         }\n\n         virtual void remove( const object& obj )override\n         {\n            _indices.erase( _indices.iterator_to( static_cast<const ObjectType&>(obj) ) );\n         }\n\n         virtual const object* find( object_id_type id )const override\n         {\n            static_assert(std::is_same<typename MultiIndexType::key_type, object_id_type>::value,\n                          \"First index of MultiIndexType MUST be object_id_type!\");\n            auto itr = _indices.find( id );\n            if( itr == _indices.end() ) return nullptr;\n            return &*itr;\n         }\n\n         virtual void inspect_all_objects(std::function<void (const object&)> inspector)const override\n         {\n            try {\n               for( const auto& ptr : _indices )\n                  inspector(ptr);\n            } FC_CAPTURE_AND_RETHROW()\n         }\n\n         const index_type& indices()const { return _indices; }\n\n      private:\n         index_type  _indices;\n   };\n\n   /**\n    * @brief An index type for objects which may be deleted\n    *\n    * This is the preferred index type for objects which need only be referenced by ID, but may be deleted.\n    */\n   template< class T >\n   struct sparse_index : public generic_index<T, boost::multi_index_container<\n      T,\n      indexed_by<\n         ordered_unique<\n            tag<by_id>,\n            member<object, object_id_type, &object::id>\n         >\n      >\n   >>{};\n\n} }\n"
  },
  {
    "path": "libraries/db/include/graphene/db/index.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/db/object.hpp>\n\n#include <fc/interprocess/file_mapping.hpp>\n#include <fc/io/raw.hpp>\n#include <fc/io/json.hpp>\n#include <fc/crypto/sha256.hpp>\n\n#include <fstream>\n#include <stack>\n\nnamespace graphene { namespace db {\n   class object_database;\n   using fc::path;\n\n   /**\n    * @class index_observer\n    * @brief used to get callbacks when objects change\n    */\n   class index_observer\n   {\n      public:\n         virtual ~index_observer(){}\n         /** called just after the object is added */\n         virtual void on_add( const object& obj ){}\n         /** called just before obj is removed */\n         virtual void on_remove( const object& obj ){}\n         /** called just after obj is modified with new value*/\n         virtual void on_modify( const object& obj ){}\n   };\n\n   /**\n    *  @class index\n    *  @brief abstract base class for accessing objects indexed in various ways.\n    *\n    *  All indexes assume that there exists an object ID space that will grow\n    *  forever in a seqential manner.  These IDs are used to identify the\n    *  index, type, and instance of the object.\n    *\n    *  Items in an index can only be modified via a call to modify and\n    *  all references to objects outside of that callback are const references.\n    *\n    *  Most implementations will probably be some form of boost::multi_index_container\n    *  which means that they can covnert a reference to an object to an iterator.  When\n    *  at all possible save a pointer/reference to your objects rather than constantly\n    *  looking them up by ID.\n    */\n   class index\n   {\n      public:\n         virtual ~index(){}\n\n         virtual uint8_t object_space_id()const = 0;\n         virtual uint8_t object_type_id()const = 0;\n\n         virtual object_id_type get_next_id()const = 0;\n         virtual void           use_next_id() = 0;\n         virtual void           set_next_id( object_id_type id ) = 0;\n\n         virtual const object&  load( const std::vector<char>& data ) = 0;\n         /**\n          *  Polymorphically insert by moving an object into the index.\n          *  this should throw if the object is already in the database.\n          */\n         virtual const object& insert( object&& obj ) = 0;\n\n         /**\n          * Builds a new object and assigns it the next available ID and then\n          * initializes it with constructor and lastly inserts it into the index.\n          */\n         virtual const object&  create( const std::function<void(object&)>& constructor ) = 0;\n\n         /**\n          *  Opens the index loading objects from a file\n          */\n         virtual void open( const fc::path& db ) = 0;\n         virtual void save( const fc::path& db ) = 0;\n\n\n\n         /** @return the object with id or nullptr if not found */\n         virtual const object*      find( object_id_type id )const = 0;\n\n         /**\n          * This version will automatically check for nullptr and throw an exception if the\n          * object ID could not be found.\n          */\n         const object&              get( object_id_type id )const\n         {\n            auto maybe_found = find( id );\n            FC_ASSERT( maybe_found != nullptr, \"Unable to find Object ${id}\", (\"id\",id) );\n            return *maybe_found;\n         }\n\n         virtual void               modify( const object& obj, const std::function<void(object&)>& ) = 0;\n         virtual void               remove( const object& obj ) = 0;\n\n         /**\n          *   When forming your lambda to modify obj, it is natural to have Object& be the signature, but\n          *   that is not compatible with the type erasue required by the virtual method.  This method\n          *   provides a helper to wrap the lambda in a form compatible with the virtual modify call.\n          *   @note Lambda should have the signature:  void(Object&)\n          */\n         template<typename Object, typename Lambda>\n         void modify( const Object& obj, const Lambda& l ) {\n            modify( static_cast<const object&>(obj), std::function<void(object&)>( [&]( object& o ){ l( static_cast<Object&>(o) ); } ) );\n         }\n\n         virtual void               inspect_all_objects(std::function<void(const object&)> inspector)const = 0;\n         virtual void               add_observer( const shared_ptr<index_observer>& ) = 0;\n\n         virtual void               object_from_variant( const fc::variant& var, object& obj, uint32_t max_depth )const = 0;\n         virtual void               object_default( object& obj )const = 0;\n   };\n\n   class secondary_index\n   {\n      public:\n         virtual ~secondary_index(){};\n         virtual void object_inserted( const object& obj ){};\n         virtual void object_removed( const object& obj ){};\n         virtual void about_to_modify( const object& before ){};\n         virtual void object_modified( const object& after  ){};\n   };\n\n   /**\n    *   Defines the common implementation\n    */\n   class base_primary_index\n   {\n      public:\n         base_primary_index( object_database& db ):_db(db){}\n\n         /** called just before obj is modified */\n         void save_undo( const object& obj );\n\n         /** called just after the object is added */\n         void on_add( const object& obj );\n\n         /** called just before obj is removed */\n         void on_remove( const object& obj );\n\n         /** called just after obj is modified */\n         void on_modify( const object& obj );\n\n         template<typename T, typename... Args>\n         T* add_secondary_index(Args... args)\n         {\n            _sindex.emplace_back( new T(args...) );\n            return static_cast<T*>(_sindex.back().get());\n         }\n\n         template<typename T>\n         const T& get_secondary_index()const\n         {\n            for( const auto& item : _sindex )\n            {\n               const T* result = dynamic_cast<const T*>(item.get());\n               if( result != nullptr ) return *result;\n            }\n            FC_THROW_EXCEPTION( fc::assert_exception, \"invalid index type\" );\n         }\n\n      protected:\n         vector< shared_ptr<index_observer> >   _observers;\n         vector< unique_ptr<secondary_index> >  _sindex;\n\n      private:\n         object_database& _db;\n   };\n\n   /** @class direct_index\n    *  @brief A secondary index that tracks objects in vectors indexed by object\n    *  id. It is meant for fully (or almost fully) populated indexes only (will\n    *  fail when loading an object_database with large gaps).\n    *\n    *  WARNING! If any of the methods called on insertion, removal or\n    *  modification throws, subsequent behaviour is undefined! Such exceptions\n    *  indicate that this index type is not appropriate for the use-case.\n    */\n   template<typename Object, uint8_t chunkbits>\n   class direct_index : public secondary_index\n   {\n      static_assert( chunkbits < 64, \"Do you really want arrays with more than 2^63 elements???\" );\n\n      // private\n         static const size_t MAX_HOLE = 100;\n         static const size_t _mask = ((1 << chunkbits) - 1);\n         uint64_t next = 0;\n         vector< vector< const Object* > > content;\n         std::stack< object_id_type > ids_being_modified;\n\n      public:\n         direct_index() {\n            FC_ASSERT( (1ULL << chunkbits) > MAX_HOLE, \"Small chunkbits is inefficient.\" );\n         }\n\n         virtual ~direct_index(){}\n\n         virtual void object_inserted( const object& obj )\n         {\n            uint64_t instance = obj.id.instance();\n            if( instance == next )\n            {\n               if( !(next & _mask) )\n               {\n                  content.resize((next >> chunkbits) + 1);\n                  content[next >> chunkbits].resize( 1 << chunkbits, nullptr );\n               }\n               next++;\n            }\n            else if( instance < next )\n               FC_ASSERT( !content[instance >> chunkbits][instance & _mask], \"Overwriting insert at {id}!\", (\"id\",obj.id) );\n            else // instance > next, allow small \"holes\"\n            {\n               FC_ASSERT( instance <= next + MAX_HOLE, \"Out-of-order insert: {id} > {next}!\", (\"id\",obj.id)(\"next\",next) );\n               if( !(next & _mask) || (next & (~_mask)) != (instance & (~_mask)) )\n               {\n                  content.resize((instance >> chunkbits) + 1);\n                  content[instance >> chunkbits].resize( 1 << chunkbits, nullptr );\n               }\n               while( next <= instance )\n               {\n                  content[next >> chunkbits][next & _mask] = nullptr;\n                  next++;\n               }\n            }\n            FC_ASSERT( nullptr != dynamic_cast<const Object*>(&obj), \"Wrong object type!\" );\n            content[instance >> chunkbits][instance & _mask] = static_cast<const Object*>( &obj );\n         }\n\n         virtual void object_removed( const object& obj )\n         {\n            FC_ASSERT( nullptr != dynamic_cast<const Object*>(&obj), \"Wrong object type!\" );\n            uint64_t instance = obj.id.instance();\n            FC_ASSERT( instance < next, \"Removing out-of-range object: {id} > {next}!\", (\"id\",obj.id)(\"next\",next) );\n            FC_ASSERT( content[instance >> chunkbits][instance & _mask], \"Removing non-existent object {id}!\", (\"id\",obj.id) );\n            content[instance >> chunkbits][instance & _mask] = nullptr;\n         }\n\n         virtual void about_to_modify( const object& before )\n         {\n            ids_being_modified.emplace( before.id );\n         }\n\n         virtual void object_modified( const object& after  )\n         {\n            FC_ASSERT( ids_being_modified.top() == after.id, \"Modification of ID is not supported!\");\n            ids_being_modified.pop();\n         }\n\n         template< typename object_id >\n         const Object* find( const object_id& id )const\n         {\n            static_assert( object_id::space_id == Object::space_id, \"Space ID mismatch!\" );\n            static_assert( object_id::type_id == Object::type_id, \"Type_ID mismatch!\" );\n            if( id.instance >= next ) return nullptr;\n            return content[id.instance.value >> chunkbits][id.instance.value & _mask];\n         };\n\n         template< typename object_id >\n         const Object& get( const object_id& id )const\n         {\n            const Object* ptr = find( id );\n            FC_ASSERT( ptr != nullptr, \"Object not found!\" );\n            return *ptr;\n         };\n\n         const Object* find( const object_id_type& id )const\n         {\n            FC_ASSERT( id.space() == Object::space_id, \"Space ID mismatch!\" );\n            FC_ASSERT( id.type() == Object::type_id, \"Type_ID mismatch!\" );\n            if( id.instance() >= next ) return nullptr;\n            return content[id.instance() >> chunkbits][id.instance() & ((1 << chunkbits) - 1)];\n         };\n   };\n\n   /**\n    * @class primary_index\n    * @brief  Wraps a derived index to intercept calls to create, modify, and remove so that\n    *  callbacks may be fired and undo state saved.\n    *\n    *  @see http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern\n    */\n   template<typename DerivedIndex, uint8_t DirectBits = 0>\n   class primary_index  : public DerivedIndex, public base_primary_index\n   {\n      public:\n         typedef typename DerivedIndex::object_type object_type;\n\n         primary_index( object_database& db )\n         :base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0)\n         {\n            if( DirectBits > 0 )\n               _direct_by_id = add_secondary_index< direct_index< object_type, DirectBits > >();\n         }\n\n         virtual uint8_t object_space_id()const override\n         { return object_type::space_id; }\n\n         virtual uint8_t object_type_id()const override\n         { return object_type::type_id; }\n\n         virtual object_id_type get_next_id()const override              { return _next_id;    }\n         virtual void           use_next_id()override                    { ++_next_id.number;  }\n         virtual void           set_next_id( object_id_type id )override { _next_id = id;      }\n\n         /** @return the object with id or nullptr if not found */\n         virtual const object*  find( object_id_type id )const override\n         {\n            if( DirectBits > 0 )\n               return _direct_by_id->find( id );\n            return DerivedIndex::find( id );\n         }\n\n         fc::sha256 get_object_version()const\n         {\n            std::string desc = \"1.0\";//get_type_description<object_type>();\n            return fc::sha256::hash(desc);\n         }\n\n         virtual void open( const path& db )override\n         { \n            if( !fc::exists( db ) ) return;\n            fc::file_mapping fm( db.generic_string().c_str(), fc::read_only );\n            fc::mapped_region mr( fm, fc::read_only, 0, fc::file_size(db) );\n            fc::datastream<const char*> ds( (const char*)mr.get_address(), mr.get_size() );\n            fc::sha256 open_ver;\n\n            fc::raw::unpack(ds, _next_id);\n            fc::raw::unpack(ds, open_ver);\n            FC_ASSERT( open_ver == get_object_version(), \"Incompatible Version, the serialization of objects in this index has changed\" );\n            vector<char> tmp;\n            while( ds.remaining() > 0 )\n            {\n               fc::raw::unpack( ds, tmp );\n               load( tmp );\n            }\n         }\n\n         virtual void save( const path& db ) override \n         {\n            std::ofstream out( db.generic_string(), \n                               std::ofstream::binary | std::ofstream::out | std::ofstream::trunc );\n            FC_ASSERT( out );\n            auto ver  = get_object_version();\n            fc::raw::pack( out, _next_id );\n            fc::raw::pack( out, ver );\n            this->inspect_all_objects( [&]( const object& o ) {\n                auto vec = fc::raw::pack( static_cast<const object_type&>(o) );\n                auto packed_vec = fc::raw::pack( vec );\n                out.write( packed_vec.data(), packed_vec.size() );\n            });\n         }\n\n         virtual const object&  load( const std::vector<char>& data )override\n         {\n            const auto& result = DerivedIndex::insert( fc::raw::unpack<object_type>( data ) );\n            for( const auto& item : _sindex )\n               item->object_inserted( result );\n            return result;\n         }\n\n\n         virtual const object&  create(const std::function<void(object&)>& constructor )override\n         {\n            const auto& result = DerivedIndex::create( constructor );\n            for( const auto& item : _sindex )\n               item->object_inserted( result );\n            on_add( result );\n            return result;\n         }\n\n         virtual const object& insert( object&& obj ) override\n         {\n            const auto& result = DerivedIndex::insert( std::move( obj ) );\n            for( const auto& item : _sindex )\n               item->object_inserted( result );\n            on_add( result );\n            return result;\n         }\n\n         virtual void  remove( const object& obj ) override\n         {\n            for( const auto& item : _sindex )\n               item->object_removed( obj );\n            on_remove(obj);\n            DerivedIndex::remove(obj);\n         }\n\n         virtual void modify( const object& obj, const std::function<void(object&)>& m )override\n         {\n            save_undo( obj );\n            for( const auto& item : _sindex )\n               item->about_to_modify( obj );\n            DerivedIndex::modify( obj, m );\n            for( const auto& item : _sindex )\n               item->object_modified( obj );\n            on_modify( obj );\n         }\n\n         virtual void add_observer( const shared_ptr<index_observer>& o ) override\n         {\n            _observers.emplace_back( o );\n         }\n\n         virtual void object_from_variant( const fc::variant& var, object& obj, uint32_t max_depth )const override\n         {\n            object_id_type id = obj.id;\n            object_type* result = dynamic_cast<object_type*>( &obj );\n            FC_ASSERT( result != nullptr );\n            fc::from_variant( var, *result, max_depth );\n            obj.id = id;\n         }\n\n         virtual void object_default( object& obj )const override\n         {\n            object_id_type id = obj.id;\n            object_type* result = dynamic_cast<object_type*>( &obj );\n            FC_ASSERT( result != nullptr );\n            (*result) = object_type();\n            obj.id = id;\n         }\n\n      private:\n         object_id_type                                 _next_id;\n         const direct_index< object_type, DirectBits >* _direct_by_id = nullptr;\n   };\n\n} } // graphene::db\n"
  },
  {
    "path": "libraries/db/include/graphene/db/object.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <boost/multiprecision/integer.hpp>\n#include <graphene/protocol/object_id.hpp>\n#include <fc/io/raw.hpp>\n#include <fc/crypto/city.hpp>\n\n#define MAX_NESTING (200)\n\nnamespace graphene { namespace db {\n\n   /**\n    *  @brief base for all database objects\n    *\n    *  The object is the fundamental building block of the database and\n    *  is the level upon which undo/redo operations are performed.  Objects\n    *  are used to track data and their relationships and provide an effecient\n    *  means to find and update information.\n    *\n    *  Objects are assigned a unique and sequential object ID by the database within\n    *  the id_space defined in the object.\n    *\n    *  All objects must be serializable via FC_REFLECT() and their content must be\n    *  faithfully restored.   Additionally all objects must be copy-constructable and\n    *  assignable in a relatively efficient manner.  In general this means that objects\n    *  should only refer to other objects by ID and avoid expensive operations when\n    *  they are copied, especially if they are modified frequently.\n    *\n    *  Additionally all objects may be annotated by plugins which wish to maintain\n    *  additional information to an object.  There can be at most one annotation\n    *  per id_space for each object.   An example of an annotation would be tracking\n    *  extra data not required by validation such as the name and description of\n    *  a user asset.  By carefully organizing how information is organized and\n    *  tracked systems can minimize the workload to only that which is necessary\n    *  to perform their function.\n    *\n    *  @note Do not use multiple inheritance with object because the code assumes\n    *  a static_cast will work between object and derived types.\n    */\n   class object\n   {\n      public:\n         object(){}\n         virtual ~object(){}\n\n         static const uint8_t space_id = 0;\n         static const uint8_t type_id  = 0;\n\n\n         // serialized\n         object_id_type          id;\n\n         /// these methods are implemented for derived classes by inheriting abstract_object<DerivedClass>\n         virtual unique_ptr<object> clone()const = 0;\n         virtual void               move_from( object& obj ) = 0;\n         virtual variant            to_variant()const  = 0;\n         virtual vector<char>       pack()const = 0;\n   };\n\n   /**\n    * @class abstract_object\n    * @brief   Use the Curiously Recurring Template Pattern to automatically add the ability to\n    *  clone, serialize, and move objects polymorphically.\n    *\n    *  http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern\n    */\n   template<typename DerivedClass>\n   class abstract_object : public object\n   {\n      public:\n         virtual unique_ptr<object> clone()const\n         {\n            return unique_ptr<object>(new DerivedClass( *static_cast<const DerivedClass*>(this) ));\n         }\n\n         virtual void    move_from( object& obj )\n         {\n            static_cast<DerivedClass&>(*this) = std::move( static_cast<DerivedClass&>(obj) );\n         }\n         virtual variant to_variant()const { return variant( static_cast<const DerivedClass&>(*this), MAX_NESTING ); }\n         virtual vector<char> pack()const  { return fc::raw::pack( static_cast<const DerivedClass&>(*this) ); }\n   };\n\n   typedef flat_map<uint8_t, object_id_type> annotation_map;\n\n   /**\n    *  @class annotated_object\n    *  @brief An object that is easily extended by providing pointers to other objects, one for each space.\n    */\n   template<typename DerivedClass>\n   class annotated_object : public abstract_object<DerivedClass>\n   {\n      public:\n         /** return object_id_type() if no anotation is found for id_space */\n         object_id_type          get_annotation( uint8_t annotation_id_space )const\n         {\n            auto itr = annotations.find(annotation_id_space);\n            if( itr != annotations.end() ) return itr->second;\n            return object_id_type();\n         }\n         void                    set_annotation( object_id_type id )\n         {\n            annotations[id.space()] = id;\n         }\n\n         /**\n          *  Annotations should be accessed via get_annotation and set_annotation so\n          *  that they can be maintained in sorted order.\n          */\n         annotation_map annotations;\n   };\n\n} } // graphene::db\n\n// Without this, pack(object_id) tries to match the template for\n// pack(boost::multiprecision::uint128_t). No idea why. :-(\nnamespace boost { namespace multiprecision { namespace detail {\ntemplate<typename To>\nstruct is_restricted_conversion<graphene::db::object,To> : public mpl::true_ {};\n}}}\n\nFC_REFLECT_TYPENAME( graphene::db::annotation_map )\nFC_REFLECT( graphene::db::object, (id) )\nFC_REFLECT_DERIVED_TEMPLATE( (typename Derived), graphene::db::annotated_object<Derived>, (graphene::db::object), (annotations) )\n"
  },
  {
    "path": "libraries/db/include/graphene/db/object_database.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/db/object.hpp>\n#include <graphene/db/index.hpp>\n#include <graphene/db/undo_database.hpp>\n\n#include <fc/log/logger.hpp>\n\n#include <map>\n\nnamespace graphene { namespace db {\n\n   /**\n    *   @class object_database\n    *   @brief maintains a set of indexed objects that can be modified with multi-level rollback support\n    */\n   class object_database\n   {\n      public:\n         object_database();\n         ~object_database();\n\n         void reset_indexes() { _index.clear(); _index.resize(255); }\n\n         void open(const fc::path& data_dir );\n\n         /**\n          * Saves the complete state of the object_database to disk, this could take a while\n          */\n         void flush();\n         void wipe(const fc::path& data_dir); // remove from disk\n         void close();\n\n         template<typename T, typename F>\n         const T& create( F&& constructor )\n         {\n            auto& idx = get_mutable_index<T>();\n            return static_cast<const T&>( idx.create( [&](object& o)\n            {\n               assert( dynamic_cast<T*>(&o) );\n               constructor( static_cast<T&>(o) );\n            } ));\n         }\n\n         ///These methods are used to retrieve indexes on the object_database. All public index accessors are const-access only.\n         /// @{\n         template<typename IndexType>\n         const IndexType& get_index_type()const {\n            static_assert( std::is_base_of<index,IndexType>::value, \"Type must be an index type\" );\n            return static_cast<const IndexType&>( get_index( IndexType::object_type::space_id, IndexType::object_type::type_id ) );\n         }\n         template<typename T>\n         const index&  get_index()const { return get_index(T::space_id,T::type_id); }\n         const index&  get_index(uint8_t space_id, uint8_t type_id)const;\n         const index&  get_index(object_id_type id)const { return get_index(id.space(),id.type()); }\n         /// @}\n\n         const object& get_object( object_id_type id )const;\n         const object* find_object( object_id_type id )const;\n\n         /// These methods are mutators of the object_database. You must use these methods to make changes to the object_database,\n         /// in order to maintain proper undo history.\n         ///@{\n\n         const object& insert( object&& obj ) { return get_mutable_index(obj.id).insert( std::move(obj) ); }\n         void          remove( const object& obj ) { get_mutable_index(obj.id).remove( obj ); }\n         template<typename T, typename Lambda>\n         void modify( const T& obj, const Lambda& m ) {\n            get_mutable_index(obj.id).modify(obj,m);\n         }\n\n         ///@}\n\n         template<typename T>\n         static const T& cast( const object& obj )\n         {\n            assert( nullptr != dynamic_cast<const T*>(&obj) );\n            return static_cast<const T&>(obj);\n         }\n         template<typename T>\n         static T& cast( object& obj )\n         {\n            assert( nullptr != dynamic_cast<T*>(&obj) );\n            return static_cast<T&>(obj);\n         }\n\n         template<typename T>\n         const T& get( object_id_type id )const\n         {\n            const object& obj = get_object( id );\n            assert( nullptr != dynamic_cast<const T*>(&obj) );\n            return static_cast<const T&>(obj);\n         }\n         template<typename T>\n         const T* find( object_id_type id )const\n         {\n            const object* obj = find_object( id );\n            assert(  !obj || nullptr != dynamic_cast<const T*>(obj) );\n            return static_cast<const T*>(obj);\n         }\n\n         template<uint8_t SpaceID, uint8_t TypeID>\n         auto find( object_id<SpaceID,TypeID> id )const -> const object_downcast_t<decltype(id)>* {\n             return find<object_downcast_t<decltype(id)>>(id);\n         }\n\n         template<uint8_t SpaceID, uint8_t TypeID>\n         auto get( object_id<SpaceID,TypeID> id )const -> const object_downcast_t<decltype(id)>& {\n             return get<object_downcast_t<decltype(id)>>(id);\n         }\n\n         template<typename IndexType>\n         IndexType* add_index()\n         {\n            typedef typename IndexType::object_type ObjectType;\n            if( _index[ObjectType::space_id].size() <= ObjectType::type_id  )\n                _index[ObjectType::space_id].resize( 255 );\n            assert(!_index[ObjectType::space_id][ObjectType::type_id]);\n            unique_ptr<index> indexptr( new IndexType(*this) );\n            _index[ObjectType::space_id][ObjectType::type_id] = std::move(indexptr);\n            return static_cast<IndexType*>(_index[ObjectType::space_id][ObjectType::type_id].get());\n         }\n\n         template<typename IndexType, typename SecondaryIndexType, typename... Args>\n         SecondaryIndexType* add_secondary_index( Args... args )\n         {\n            return get_mutable_index_type<IndexType>().template add_secondary_index<SecondaryIndexType, Args...>(args...);\n         }\n\n         void pop_undo();\n\n         fc::path get_data_dir()const { return _data_dir; }\n\n         /** public for testing purposes only... should be private in practice. */\n         undo_database                          _undo_db;\n     protected:\n         template<typename IndexType>\n         IndexType&    get_mutable_index_type() {\n            static_assert( std::is_base_of<index,IndexType>::value, \"Type must be an index type\" );\n            return static_cast<IndexType&>( get_mutable_index( IndexType::object_type::space_id, IndexType::object_type::type_id ) );\n         }\n         template<typename T>\n         index& get_mutable_index()                   { return get_mutable_index(T::space_id,T::type_id); }\n         index& get_mutable_index(object_id_type id)  { return get_mutable_index(id.space(),id.type());   }\n         index& get_mutable_index(uint8_t space_id, uint8_t type_id);\n\n     private:\n\n         friend class base_primary_index;\n         friend class undo_database;\n         void save_undo( const object& obj );\n         void save_undo_add( const object& obj );\n         void save_undo_remove( const object& obj );\n\n         fc::path                                                  _data_dir;\n         vector< vector< unique_ptr<index> > >                     _index;\n   };\n\n} } // graphene::db\n\n\n"
  },
  {
    "path": "libraries/db/include/graphene/db/simple_index.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/db/index.hpp>\n\nnamespace graphene { namespace db {\n\n   /**\n    *  @class simple_index\n    *  @brief A simple index uses a vector<unique_ptr<T>> to store data\n    *\n    *  This index is preferred in situations where the data will never be\n    *  removed from main memory and when access by ID is the only kind\n    *  of access that is necessary.\n    */\n   template<typename T>\n   class simple_index : public index\n   {\n      public:\n         typedef T object_type;\n\n         virtual const object&  create( const std::function<void(object&)>& constructor ) override\n         {\n             auto id = get_next_id();\n             auto instance = id.instance();\n             if( instance >= _objects.size() ) _objects.resize( instance + 1 );\n             _objects[instance].reset(new T);\n             _objects[instance]->id = id;\n             constructor( *_objects[instance] );\n             _objects[instance]->id = id; // just in case it changed\n             use_next_id();\n             return *_objects[instance];\n         }\n\n         virtual void modify( const object& obj, const std::function<void(object&)>& modify_callback ) override\n         {\n            assert( obj.id.instance() < _objects.size() );\n            modify_callback( *_objects[obj.id.instance()] );\n         }\n\n         virtual const object& insert( object&& obj )override\n         {\n            auto instance = obj.id.instance();\n            assert( nullptr != dynamic_cast<T*>(&obj) );\n            if( _objects.size() <= instance ) _objects.resize( instance+1 );\n            assert( !_objects[instance] );\n            _objects[instance].reset( new T( std::move( static_cast<T&>(obj) ) ) );\n            return *_objects[instance];\n         }\n\n         virtual void remove( const object& obj ) override\n         {\n            assert( nullptr != dynamic_cast<const T*>(&obj) );\n            const auto instance = obj.id.instance();\n            _objects[instance].reset();\n            while( (_objects.size() > 0) && (_objects.back() == nullptr) )\n               _objects.pop_back();\n         }\n\n         virtual const object* find( object_id_type id )const override\n         {\n            assert( id.space() == T::space_id );\n            assert( id.type() == T::type_id );\n\n            const auto instance = id.instance();\n            if( instance >= _objects.size() ) return nullptr;\n            return _objects[instance].get();\n         }\n\n         virtual void inspect_all_objects(std::function<void (const object&)> inspector)const override\n         {\n            try {\n               for( const auto& ptr : _objects )\n               {\n                  if( ptr.get() )\n                     inspector(*ptr);\n               }\n            } FC_CAPTURE_AND_RETHROW()\n         }\n\n         class const_iterator\n         {\n            public:\n               const_iterator( const vector<unique_ptr<object>>& objects ):_objects(objects) {}\n               const_iterator(\n                  const vector<unique_ptr<object>>& objects,\n                  const vector<unique_ptr<object>>::const_iterator& a ):_itr(a),_objects(objects){}\n               friend bool operator==( const const_iterator& a, const const_iterator& b ) { return a._itr == b._itr; }\n               friend bool operator!=( const const_iterator& a, const const_iterator& b ) { return a._itr != b._itr; }\n               const T& operator*()const { return static_cast<const T&>(*_itr->get()); }\n               const_iterator operator++(int)     // postfix\n               {\n                  const_iterator result( *this );\n                  ++(*this);\n                  return result;\n               }\n               const_iterator& operator++()       // prefix\n               {\n                  ++_itr;\n                  while( (_itr != _objects.end()) && ( (*_itr) == nullptr ) )\n                     ++_itr;\n                  return *this;\n               }\n               typedef std::forward_iterator_tag iterator_category;\n               typedef vector<unique_ptr<object> >::value_type value_type;\n               typedef vector<unique_ptr<object> >::difference_type difference_type;\n               typedef vector<unique_ptr<object> >::pointer pointer;\n               typedef vector<unique_ptr<object> >::reference reference;\n            private:\n               vector<unique_ptr<object>>::const_iterator _itr;\n               const vector<unique_ptr<object>>& _objects;\n         };\n         const_iterator begin()const { return const_iterator(_objects, _objects.begin()); }\n         const_iterator end()const   { return const_iterator(_objects, _objects.end());   }\n\n         size_t size()const { return _objects.size(); }\n      private:\n         vector< unique_ptr<object> > _objects;\n   };\n\n} } // graphene::db\n"
  },
  {
    "path": "libraries/db/include/graphene/db/undo_database.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/db/object.hpp>\n#include <deque>\n#include <fc/exception/exception.hpp>\n\nnamespace graphene { namespace db {\n\n   using std::unordered_map;\n   using fc::flat_set;\n   class object_database;\n\n   struct undo_state\n   {\n      unordered_map<object_id_type, unique_ptr<object> > old_values;\n      unordered_map<object_id_type, object_id_type>      old_index_next_ids;\n      std::unordered_set<object_id_type>                 new_ids;\n      unordered_map<object_id_type, unique_ptr<object> > removed;\n   };\n\n\n   /**\n    * @class undo_database\n    * @brief tracks changes to the state and allows changes to be undone\n    *\n    */\n   class undo_database\n   {\n      public:\n         undo_database( object_database& db ):_db(db){}\n\n         class session\n         {\n            public:\n               session( session&& mv )\n               :_db(mv._db),_apply_undo(mv._apply_undo)\n               {\n                  mv._apply_undo = false;\n               }\n               ~session();\n               void commit() { _apply_undo = false; _db.commit();  }\n               void undo()   { if( _apply_undo ) _db.undo(); _apply_undo = false; }\n               void merge()  { if( _apply_undo ) _db.merge(); _apply_undo = false; }\n\n               session& operator = ( session&& mv )\n               { try {\n                  if( this == &mv ) return *this;\n                  if( _apply_undo ) _db.undo();\n                  _apply_undo = mv._apply_undo;\n                  mv._apply_undo = false;\n                  return *this;\n               } FC_CAPTURE_AND_RETHROW() }\n\n            private:\n               friend class undo_database;\n               session(undo_database& db, bool disable_on_exit = false): _db(db),_disable_on_exit(disable_on_exit) {}\n               undo_database& _db;\n               bool _apply_undo = true;\n               bool _disable_on_exit = false;\n         };\n\n         void    disable();\n         void    enable();\n         bool    enabled()const { return !_disabled; }\n\n         session start_undo_session( bool force_enable = false );\n         /**\n          * This should be called just after an object is created\n          */\n         void on_create( const object& obj );\n         /**\n          * This should be called just before an object is modified\n          *\n          * If it's a new object as of this undo state, its pre-modification value is not stored, because prior to this\n          * undo state, it did not exist. Any modifications in this undo state are irrelevant, as the object will simply\n          * be removed if we undo.\n          */\n         void on_modify( const object& obj );\n         /**\n          * This should be called just before an object is removed.\n          *\n          * If it's a new object as of this undo state, its pre-removal value is not stored, because prior to this undo\n          * state, it did not exist. Now that it's been removed, it doesn't exist again, so nothing has happened.\n          * Instead, remove it from the list of newly created objects (which must be deleted if we undo), as we don't\n          * want to re-delete it if this state is undone.\n          */\n         void on_remove( const object& obj );\n\n         /**\n          *  Removes the last committed session,\n          *  note... this is dangerous if there are\n          *  active sessions... thus active sessions should\n          *  track\n          */\n         void pop_commit();\n\n         std::size_t size()const { return _stack.size(); }\n         void set_max_size(size_t new_max_size) { _max_size = new_max_size; }\n         size_t max_size()const { return _max_size; }\n         uint32_t active_sessions()const { return _active_sessions; }\n\n         const undo_state& head()const;\n\n      private:\n         void undo();\n         void merge();\n         void commit();\n\n         uint32_t                _active_sessions = 0;\n         bool                    _disabled = true;\n         std::deque<undo_state>  _stack;\n         object_database&        _db;\n         size_t                  _max_size = 256;\n   };\n\n} } // graphene::db\n"
  },
  {
    "path": "libraries/db/index.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <fc/io/raw.hpp>\n#include <graphene/db/index.hpp>\n#include <graphene/db/object_database.hpp>\n\nnamespace graphene { namespace db {\n   void base_primary_index::save_undo( const object& obj )\n   { _db.save_undo( obj ); }\n\n   void base_primary_index::on_add( const object& obj )\n   {\n      _db.save_undo_add( obj );\n      for( auto ob : _observers ) ob->on_add( obj );\n   }\n\n   void base_primary_index::on_remove( const object& obj )\n   { _db.save_undo_remove( obj ); for( auto ob : _observers ) ob->on_remove( obj ); }\n\n   void base_primary_index::on_modify( const object& obj )\n   {for( auto ob : _observers ) ob->on_modify(  obj ); }\n} } // graphene::chain\n"
  },
  {
    "path": "libraries/db/object_database.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/db/object_database.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/container/flat.hpp>\n#include <fc/thread/parallel.hpp>\n\nnamespace graphene { namespace db {\n\nobject_database::object_database()\n:_undo_db(*this)\n{\n   _index.resize(255);\n   _undo_db.enable();\n}\n\nobject_database::~object_database(){}\n\nvoid object_database::close()\n{\n}\n\nconst object* object_database::find_object( object_id_type id )const\n{\n   return get_index(id.space(),id.type()).find( id );\n}\nconst object& object_database::get_object( object_id_type id )const\n{\n   return get_index(id.space(),id.type()).get( id );\n}\n\nconst index& object_database::get_index(uint8_t space_id, uint8_t type_id)const\n{\n   FC_ASSERT( _index.size() > space_id, \"\", (\"space_id\",space_id)(\"type_id\",type_id)(\"index.size\",_index.size()) );\n   FC_ASSERT( _index[space_id].size() > type_id, \"\", (\"space_id\",space_id)(\"type_id\",type_id)(\"index[space_id].size\",_index[space_id].size()) );\n   const auto& tmp = _index[space_id][type_id];\n   FC_ASSERT( tmp );\n   return *tmp;\n}\nindex& object_database::get_mutable_index(uint8_t space_id, uint8_t type_id)\n{\n   FC_ASSERT( _index.size() > space_id, \"\", (\"space_id\",space_id)(\"type_id\",type_id)(\"index.size\",_index.size()) );\n   FC_ASSERT( _index[space_id].size() > type_id , \"\", (\"space_id\",space_id)(\"type_id\",type_id)(\"index[space_id].size\",_index[space_id].size()) );\n   const auto& idx = _index[space_id][type_id];\n   FC_ASSERT( idx, \"\", (\"space\",space_id)(\"type\",type_id) );\n   return *idx;\n}\n\nvoid object_database::flush()\n{\n//   ilog(\"Save object_database in ${d}\", (\"d\", _data_dir));\n   fc::create_directories( _data_dir / \"object_database.tmp\" / \"lock\" );\n   std::vector<fc::future<void>> tasks;\n   tasks.reserve(200);\n   for( uint32_t space = 0; space < _index.size(); ++space )\n   {\n      fc::create_directories( _data_dir / \"object_database.tmp\" / fc::to_string(space) );\n      const auto types = _index[space].size();\n      for( uint32_t type = 0; type  <  types; ++type )\n         if( _index[space][type] )\n            tasks.push_back( fc::do_parallel( [this,space,type] () {\n               _index[space][type]->save( _data_dir / \"object_database.tmp\" / fc::to_string(space)/fc::to_string(type) );\n            } ) );\n   }\n   for( auto& task : tasks )\n      task.wait();\n   fc::remove_all( _data_dir / \"object_database.tmp\" / \"lock\" );\n   if( fc::exists( _data_dir / \"object_database\" ) )\n      fc::rename( _data_dir / \"object_database\", _data_dir / \"object_database.old\" );\n   fc::rename( _data_dir / \"object_database.tmp\", _data_dir / \"object_database\" );\n   fc::remove_all( _data_dir / \"object_database.old\" );\n}\n\nvoid object_database::wipe(const fc::path& data_dir)\n{\n   close();\n   ilog(\"Wiping object database...\");\n   fc::remove_all(data_dir / \"object_database\");\n   ilog(\"Done wiping object databse.\");\n}\n\nvoid object_database::open(const fc::path& data_dir)\n{ try {\n   _data_dir = data_dir;\n   if( fc::exists( _data_dir / \"object_database\" / \"lock\" ) )\n   {\n       wlog(\"Ignoring locked object_database\");\n       return;\n   }\n   std::vector<fc::future<void>> tasks;\n   tasks.reserve(200);\n   ilog(\"Opening object database from ${d} ...\", (\"d\", data_dir));\n   for( uint32_t space = 0; space < _index.size(); ++space )\n      for( uint32_t type = 0; type  < _index[space].size(); ++type )\n         if( _index[space][type] )\n            tasks.push_back( fc::do_parallel( [this,space,type] () {\n               _index[space][type]->open( _data_dir / \"object_database\" / fc::to_string(space)/fc::to_string(type) );\n            } ) );\n   for( auto& task : tasks )\n      task.wait();\n   ilog( \"Done opening object database.\" );\n\n} FC_CAPTURE_AND_RETHROW( (data_dir) ) }\n\n\nvoid object_database::pop_undo()\n{ try {\n   _undo_db.pop_commit();\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid object_database::save_undo( const object& obj )\n{\n   _undo_db.on_modify( obj );\n}\n\nvoid object_database::save_undo_add( const object& obj )\n{\n   _undo_db.on_create( obj );\n}\n\nvoid object_database::save_undo_remove(const object& obj)\n{\n   _undo_db.on_remove( obj );\n}\n\n} } // namespace graphene::db\n"
  },
  {
    "path": "libraries/db/undo_database.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/db/object_database.hpp>\n#include <graphene/db/undo_database.hpp>\n#include <fc/reflect/variant.hpp>\n\nnamespace graphene { namespace db {\n\nvoid undo_database::enable()  { _disabled = false; }\nvoid undo_database::disable() { _disabled = true; }\n\nundo_database::session::~session()\n{\n   try {\n      if( _apply_undo ) _db.undo();\n   }\n   catch ( const fc::exception& e )\n   {\n      elog( \"${e}\", (\"e\",e.to_detail_string() ) );\n      std::terminate();\n   }\n   if( _disable_on_exit ) _db.disable();\n}\n\nundo_database::session undo_database::start_undo_session( bool force_enable )\n{\n   if( _disabled && !force_enable ) return session(*this);\n   bool disable_on_exit = _disabled  && force_enable;\n   if( force_enable ) \n      _disabled = false;\n\n   while( size() > max_size() )\n      _stack.pop_front();\n\n   _stack.emplace_back();\n   ++_active_sessions;\n   return session(*this, disable_on_exit );\n}\nvoid undo_database::on_create( const object& obj )\n{\n   if( _disabled ) return;\n\n   if( _stack.empty() )\n      _stack.emplace_back();\n   auto& state = _stack.back();\n   auto index_id = object_id_type( obj.id.space(), obj.id.type(), 0 );\n   auto itr = state.old_index_next_ids.find( index_id );\n   if( itr == state.old_index_next_ids.end() )\n      state.old_index_next_ids[index_id] = obj.id;\n   state.new_ids.insert(obj.id);\n}\nvoid undo_database::on_modify( const object& obj )\n{\n   if( _disabled ) return;\n\n   if( _stack.empty() )\n      _stack.emplace_back();\n   auto& state = _stack.back();\n   if( state.new_ids.find(obj.id) != state.new_ids.end() )\n      return;\n   auto itr =  state.old_values.find(obj.id);\n   if( itr != state.old_values.end() ) return;\n   state.old_values[obj.id] = obj.clone();\n}\nvoid undo_database::on_remove( const object& obj )\n{\n   if( _disabled ) return;\n\n   if( _stack.empty() )\n      _stack.emplace_back();\n   undo_state& state = _stack.back();\n   if( state.new_ids.count(obj.id) )\n   {\n      state.new_ids.erase(obj.id);\n      return;\n   }\n   if( state.old_values.count(obj.id) )\n   {\n      state.removed[obj.id] = std::move(state.old_values[obj.id]);\n      state.old_values.erase(obj.id);\n      return;\n   }\n   if( state.removed.count(obj.id) ) return;\n   state.removed[obj.id] = obj.clone();\n}\n\nvoid undo_database::undo()\n{ try {\n   FC_ASSERT( !_disabled );\n   FC_ASSERT( _active_sessions > 0 );\n   disable();\n\n   auto& state = _stack.back();\n   for( auto& item : state.old_values )\n   {\n      _db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );\n   }\n\n   for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr  )\n   {\n      _db.remove( _db.get_object(*ritr) );\n   }\n\n   for( auto& item : state.old_index_next_ids )\n   {\n      _db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );\n   }\n\n   for( auto& item : state.removed )\n      _db.insert( std::move(*item.second) );\n\n   _stack.pop_back();\n   enable();\n   --_active_sessions;\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid undo_database::merge()\n{\n   FC_ASSERT( _active_sessions > 0 );\n   if( _active_sessions == 1 && _stack.size() == 1 )\n   {\n      _stack.pop_back();\n      --_active_sessions;\n      return;\n   }\n   FC_ASSERT( _stack.size() >=2 );\n   auto& state = _stack.back();\n   auto& prev_state = _stack[_stack.size()-2];\n\n   // An object's relationship to a state can be:\n   // in new_ids            : new\n   // in old_values (was=X) : upd(was=X)\n   // in removed (was=X)    : del(was=X)\n   // not in any of above   : nop\n   //\n   // When merging A=prev_state and B=state we have a 4x4 matrix of all possibilities:\n   //\n   //                   |--------------------- B ----------------------|\n   //\n   //                +------------+------------+------------+------------+\n   //                | new        | upd(was=Y) | del(was=Y) | nop        |\n   //   +------------+------------+------------+------------+------------+\n   // / | new        | N/A        | new       A| nop       C| new       A|\n   // | +------------+------------+------------+------------+------------+\n   // | | upd(was=X) | N/A        | upd(was=X)A| del(was=X)C| upd(was=X)A|\n   // A +------------+------------+------------+------------+------------+\n   // | | del(was=X) | N/A        | N/A        | N/A        | del(was=X)A|\n   // | +------------+------------+------------+------------+------------+\n   // \\ | nop        | new       B| upd(was=Y)B| del(was=Y)B| nop      AB|\n   //   +------------+------------+------------+------------+------------+\n   //\n   // Each entry was composed by labelling what should occur in the given case.\n   //\n   // Type A means the composition of states contains the same entry as the first of the two merged states for that object.\n   // Type B means the composition of states contains the same entry as the second of the two merged states for that object.\n   // Type C means the composition of states contains an entry different from either of the merged states for that object.\n   // Type N/A means the composition of states violates causal timing.\n   // Type AB means both type A and type B simultaneously.\n   //\n   // The merge() operation is defined as modifying prev_state in-place to be the state object which represents the composition of\n   // state A and B.\n   //\n   // Type A (and AB) can be implemented as a no-op; prev_state already contains the correct value for the merged state.\n   // Type B (and AB) can be implemented by copying from state to prev_state.\n   // Type C needs special case-by-case logic.\n   // Type N/A can be ignored or assert(false) as it can only occur if prev_state and state have illegal values\n   // (a serious logic error which should never happen).\n   //\n\n   // We can only be outside type A/AB (the nop path) if B is not nop, so it suffices to iterate through B's three containers.\n\n   // *+upd\n   for( auto& obj : state.old_values )\n   {\n      if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )\n      {\n         // new+upd -> new, type A\n         continue;\n      }\n      if( prev_state.old_values.find(obj.second->id) != prev_state.old_values.end() )\n      {\n         // upd(was=X) + upd(was=Y) -> upd(was=X), type A\n         continue;\n      }\n      // del+upd -> N/A\n      assert( prev_state.removed.find(obj.second->id) == prev_state.removed.end() );\n      // nop+upd(was=Y) -> upd(was=Y), type B\n      prev_state.old_values[obj.second->id] = std::move(obj.second);\n   }\n\n   // *+new, but we assume the N/A cases don't happen, leaving type B nop+new -> new\n   for( auto id : state.new_ids )\n      prev_state.new_ids.insert(id);\n\n   // old_index_next_ids can only be updated, iterate over *+upd cases\n   for( auto& item : state.old_index_next_ids )\n   {\n      if( prev_state.old_index_next_ids.find( item.first ) == prev_state.old_index_next_ids.end() )\n      {\n         // nop+upd(was=Y) -> upd(was=Y), type B\n         prev_state.old_index_next_ids[item.first] = item.second;\n         continue;\n      }\n      else\n      {\n         // upd(was=X)+upd(was=Y) -> upd(was=X), type A\n         // type A implementation is a no-op, as discussed above, so there is no code here\n         continue;\n      }\n   }\n\n   // *+del\n   for( auto& obj : state.removed )\n   {\n      if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )\n      {\n         // new + del -> nop (type C)\n         prev_state.new_ids.erase(obj.second->id);\n         continue;\n      }\n      auto it = prev_state.old_values.find(obj.second->id);\n      if( it != prev_state.old_values.end() )\n      {\n         // upd(was=X) + del(was=Y) -> del(was=X)\n         prev_state.removed[obj.second->id] = std::move(it->second);\n         prev_state.old_values.erase(obj.second->id);\n         continue;\n      }\n      // del + del -> N/A\n      assert( prev_state.removed.find( obj.second->id ) == prev_state.removed.end() );\n      // nop + del(was=Y) -> del(was=Y)\n      prev_state.removed[obj.second->id] = std::move(obj.second);\n   }\n   _stack.pop_back();\n   --_active_sessions;\n}\nvoid undo_database::commit()\n{\n   FC_ASSERT( _active_sessions > 0 );\n   --_active_sessions;\n}\n\nvoid undo_database::pop_commit()\n{\n   FC_ASSERT( _active_sessions == 0 );\n   FC_ASSERT( !_stack.empty() );\n\n   disable();\n   try {\n      auto& state = _stack.back();\n\n      for( auto& item : state.old_values )\n      {\n         _db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );\n      }\n\n      for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr  )\n      {\n         _db.remove( _db.get_object(*ritr) );\n      }\n\n      for( auto& item : state.old_index_next_ids )\n      {\n         _db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );\n      }\n\n      for( auto& item : state.removed )\n         _db.insert( std::move(*item.second) );\n\n      _stack.pop_back();\n   }\n   catch ( const fc::exception& e )\n   {\n      elog( \"error popping commit ${e}\", (\"e\", e.to_detail_string() )  );\n      enable();\n      throw;\n   }\n   enable();\n}\nconst undo_state& undo_database::head()const\n{\n   FC_ASSERT( !_stack.empty() );\n   return _stack.back();\n}\n\n} } // graphene::db\n"
  },
  {
    "path": "libraries/egenesis/CMakeLists.txt",
    "content": "message( STATUS \"Generating egenesis\" )\n\nif( GRAPHENE_EGENESIS_JSON )\n  set( embed_genesis_args \"${GRAPHENE_EGENESIS_JSON}\" )\nelse( GRAPHENE_EGENESIS_JSON )\n  set( embed_genesis_args \"genesis.json\" )\nendif( GRAPHENE_EGENESIS_JSON )\n\nadd_custom_command(\n   OUTPUT\n      \"${CMAKE_CURRENT_BINARY_DIR}/egenesis_brief.cpp\"\n      \"${CMAKE_CURRENT_BINARY_DIR}/egenesis_full.cpp\"\n   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n   COMMAND ${CMAKE_COMMAND}\n        -DINIT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}\n        -DINIT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}\n        -Dembed_genesis_args=${embed_genesis_args}\n        -P ${CMAKE_CURRENT_SOURCE_DIR}/embed_genesis.cmake\n   DEPENDS\n      \"${GRAPHENE_EGENESIS_JSON}\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}/egenesis_brief.cpp.tmpl\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}/egenesis_full.cpp.tmpl\"\n)\n\nadd_library( graphene_egenesis_none egenesis_none.cpp\n             include/graphene/egenesis/egenesis.hpp )\nadd_library( graphene_egenesis_brief \"${CMAKE_CURRENT_BINARY_DIR}/egenesis_brief.cpp\"\n             include/graphene/egenesis/egenesis.hpp )\nadd_library( graphene_egenesis_full  \"${CMAKE_CURRENT_BINARY_DIR}/egenesis_full.cpp\"\n             include/graphene/egenesis/egenesis.hpp )\n\ntarget_link_libraries( graphene_egenesis_none graphene_chain fc )\ntarget_link_libraries( graphene_egenesis_brief graphene_chain fc )\ntarget_link_libraries( graphene_egenesis_full graphene_chain fc )\n\ntarget_include_directories( graphene_egenesis_none\n   PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\ntarget_include_directories( graphene_egenesis_brief\n   PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\ntarget_include_directories( graphene_egenesis_full\n   PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nINSTALL( TARGETS\n   graphene_egenesis_none graphene_egenesis_brief graphene_egenesis_full\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "libraries/egenesis/README-dev.md",
    "content": "Use this key to produce blocks with the `genesis-dev.json` Genesis.\nThe following line may be added directly to `config.ini`:\n\n```\nprivate-key = [\"BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\",\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"]\n```\n"
  },
  {
    "path": "libraries/egenesis/egenesis_brief.cpp.tmpl",
    "content": "${generated_file_banner}\n/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/types.hpp>\n#include <graphene/egenesis/egenesis.hpp>\n\nnamespace graphene { namespace egenesis {\n\nusing namespace graphene::chain;\n\nchain_id_type get_egenesis_chain_id()\n{\n   return chain_id_type( \"${chain_id}\" );\n}\n\nvoid compute_egenesis_json( std::string& result )\n{\n   result = \"\";\n}\n\nfc::sha256 get_egenesis_json_hash()\n{\n   return fc::sha256( \"${genesis_json_hash}\" );\n}\n\n} }\n"
  },
  {
    "path": "libraries/egenesis/egenesis_full.cpp.tmpl",
    "content": "${generated_file_banner}\n/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/types.hpp>\n#include <graphene/egenesis/egenesis.hpp>\n\nnamespace graphene { namespace egenesis {\n\nusing namespace graphene::chain;\n\nstatic const char* genesis_json_array[${genesis_json_array_height}] =\n{\n${genesis_json_array}\n};\n\nchain_id_type get_egenesis_chain_id()\n{\n   return chain_id_type( \"${chain_id}\" );\n}\n\nvoid compute_egenesis_json( std::string& result )\n{\n   result.reserve( ${genesis_json_length} );\n   result.resize(0);\n   for( size_t i=0; i<${genesis_json_array_height}; i++ )\n   {\n      result.append( genesis_json_array[i] );\n   }\n}\n\nfc::sha256 get_egenesis_json_hash()\n{\n   return fc::sha256( \"${genesis_json_hash}\" );\n}\n\n} }\n"
  },
  {
    "path": "libraries/egenesis/egenesis_none.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/egenesis/egenesis.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace egenesis {\n\nusing namespace graphene::chain;\n\nchain_id_type get_egenesis_chain_id()\n{\n   return chain_id_type();\n}\n\nvoid compute_egenesis_json( std::string& result )\n{\n   result = \"\";\n}\n\nfc::sha256 get_egenesis_json_hash()\n{\n   return fc::sha256::hash( \"\" );\n}\n\n} }\n"
  },
  {
    "path": "libraries/egenesis/genesis-dev.json",
    "content": "{\n  \"initial_timestamp\": \"2020-08-13T20:32:55\",\n  \"max_core_supply\": \"360000000000000\",\n  \"initial_parameters\": {\n    \"current_fees\": {\n      \"parameters\": [[\n          0,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 1000000\n          }\n        ],[\n          1,{\n            \"fee\": 500000\n          }\n        ],[\n          2,{\n            \"fee\": 0\n          }\n        ],[\n          3,{\n            \"fee\": 2000000\n          }\n        ],[\n          4,{}\n        ],[\n          5,{\n            \"basic_fee\": 500000,\n            \"premium_fee\": 200000000,\n            \"price_per_kbyte\": 100000\n          }\n        ],[\n          6,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 100000\n          }\n        ],[\n          7,{\n            \"fee\": 300000\n          }\n        ],[\n          8,{\n            \"membership_annual_fee\": 200000000,\n            \"membership_lifetime_fee\": 1000000000\n          }\n        ],[\n          9,{\n            \"fee\": 50000000\n          }\n        ],[\n          10,{\n            \"symbol3\": \"50000000000\",\n            \"symbol4\": \"30000000000\",\n            \"long_symbol\": 500000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          11,{\n            \"fee\": 50000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          12,{\n            \"fee\": 50000000\n          }\n        ],[\n          13,{\n            \"fee\": 50000000\n          }\n        ],[\n          14,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 100000\n          }\n        ],[\n          15,{\n            \"fee\": 2000000\n          }\n        ],[\n          16,{\n            \"fee\": 100000\n          }\n        ],[\n          17,{\n            \"fee\": 10000000\n          }\n        ],[\n          18,{\n            \"fee\": 50000000\n          }\n        ],[\n          19,{\n            \"fee\": 100000\n          }\n        ],[\n          20,{\n            \"fee\": 500000000\n          }\n        ],[\n          21,{\n            \"fee\": 2000000\n          }\n        ],[\n          22,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          23,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          24,{\n            \"fee\": 100000\n          }\n        ],[\n          25,{\n            \"fee\": 100000\n          }\n        ],[\n          26,{\n            \"fee\": 100000\n          }\n        ],[\n          27,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          28,{\n            \"fee\": 0\n          }\n        ],[\n          29,{\n            \"fee\": 500000000\n          }\n        ],[\n          30,{\n            \"fee\": 2000000\n          }\n        ],[\n          31,{\n            \"fee\": 100000\n          }\n        ],[\n          32,{\n            \"fee\": 100000\n          }\n        ],[\n          33,{\n            \"fee\": 2000000\n          }\n        ],[\n          34,{\n            \"fee\": 500000000\n          }\n        ],[\n          35,{\n            \"fee\": 100000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          36,{\n            \"fee\": 100000\n          }\n        ],[\n          37,{}\n        ],[\n          38,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          39,{\n            \"fee\": 500000,\n            \"price_per_output\": 500000\n          }\n        ],[\n          40,{\n            \"fee\": 500000,\n            \"price_per_output\": 500000\n          }\n        ],[\n          41,{\n            \"fee\": 500000\n          }\n        ],[\n          42,{}\n        ],[\n          43,{\n            \"fee\": 2000000\n          }\n        ],[\n          44,{}\n        ],[\n          45,{\n            \"fee\": 2000000\n          }\n        ],[\n          46,{}\n        ],[\n          47,{\n            \"fee\": 2000000\n          }\n        ],[\n          48,{\n            \"fee\": 2000000\n          }\n        ]\n      ],\n      \"scale\": 10000\n    },\n    \"block_interval\": 3,\n    \"maintenance_interval\": 600,\n    \"maintenance_skip_slots\": 3,\n    \"committee_proposal_review_period\": 1209600,\n    \"maximum_transaction_size\": 2048,\n    \"maximum_block_size\": 2000000,\n    \"maximum_time_until_expiration\": 86400,\n    \"maximum_proposal_lifetime\": 2419200,\n    \"maximum_asset_whitelist_authorities\": 10,\n    \"maximum_asset_feed_publishers\": 10,\n    \"maximum_witness_count\": 1001,\n    \"maximum_committee_count\": 1001,\n    \"maximum_authority_membership\": 10,\n    \"reserve_percent_of_fee\": 2000,\n    \"network_percent_of_fee\": 2000,\n    \"lifetime_referrer_percent_of_fee\": 3000,\n    \"cashback_vesting_period_seconds\": 31536000,\n    \"cashback_vesting_threshold\": 10000000,\n    \"count_non_member_votes\": true,\n    \"allow_non_member_whitelists\": false,\n    \"witness_pay_per_block\": 1000000,\n    \"worker_budget_per_day\": \"50000000000\",\n    \"max_predicate_opcode\": 1,\n    \"fee_liquidation_threshold\": 10000000,\n    \"accounts_per_fee_scale\": 1000,\n    \"account_fee_scale_bitshifts\": 4,\n    \"max_authority_depth\": 2,\n    \"extensions\": {\n        \"updatable_htlc_options\": {\n            \"max_timeout_secs\": 2592000,\n            \"max_preimage_size\": 1024000\n        }\n    }\n  },\n  \"initial_accounts\": [{\n      \"name\": \"init0\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init1\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init2\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init3\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init4\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init5\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init6\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init7\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init8\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init9\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init10\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"nathan\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": false\n    }\n  ],\n  \"initial_assets\": [],\n  \"initial_balances\": [{\n      \"owner\": \"NBSMZq4SyNoFGraCNuV5ZjbJHMvcYBLSV3rM\",\n      \"asset_symbol\": \"NBS\",\n      \"amount\": \"10000000000000\"\n    }\n  ],\n  \"initial_vesting_balances\": [],\n  \"initial_active_witnesses\": 11,\n  \"initial_witness_candidates\": [{\n      \"owner_name\": \"init0\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init1\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init2\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init3\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init4\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init5\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init6\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init7\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init8\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init9\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init10\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    }\n  ],\n  \"initial_committee_candidates\": [{\n      \"owner_name\": \"init0\"\n    },{\n      \"owner_name\": \"init1\"\n    },{\n      \"owner_name\": \"init2\"\n    },{\n      \"owner_name\": \"init3\"\n    },{\n      \"owner_name\": \"init4\"\n    },{\n      \"owner_name\": \"init5\"\n    },{\n      \"owner_name\": \"init6\"\n    },{\n      \"owner_name\": \"init7\"\n    },{\n      \"owner_name\": \"init8\"\n    },{\n      \"owner_name\": \"init9\"\n    },{\n      \"owner_name\": \"init10\"\n    }\n  ],\n  \"initial_worker_candidates\": [],\n  \"immutable_parameters\": {\n    \"min_committee_member_count\": 11,\n    \"min_witness_count\": 11,\n    \"num_special_accounts\": 0,\n    \"num_special_assets\": 0\n  }\n}\n"
  },
  {
    "path": "libraries/egenesis/include/graphene/egenesis/egenesis.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n\n#include <fc/crypto/sha256.hpp>\n#include <graphene/protocol/types.hpp>\n#include <graphene/chain/genesis_state.hpp>\n\nnamespace graphene { namespace egenesis {\n\n/**\n * Get the chain ID of the built-in egenesis, or chain_id_type()\n * if none was compiled in.\n */\ngraphene::chain::chain_id_type get_egenesis_chain_id();\n\n/**\n * Get the egenesis JSON, or the empty string if none was compiled in.\n */\nvoid compute_egenesis_json( std::string& result );\n\n/**\n * The file returned by compute_egenesis_json() should have this hash.\n */\nfc::sha256 get_egenesis_json_hash();\n\n} } // graphene::egenesis\n"
  },
  {
    "path": "libraries/egenesis/nbstest-genesis.json",
    "content": "{\n  \"initial_timestamp\": \"2020-08-13T20:32:55\",\n  \"max_core_supply\": \"360000000000000\",\n  \"initial_parameters\": {\n    \"current_fees\": {\n      \"parameters\": [[\n          0,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 1000000\n          }\n        ],[\n          1,{\n            \"fee\": 500000\n          }\n        ],[\n          2,{\n            \"fee\": 0\n          }\n        ],[\n          3,{\n            \"fee\": 2000000\n          }\n        ],[\n          4,{}\n        ],[\n          5,{\n            \"basic_fee\": 500000,\n            \"premium_fee\": 200000000,\n            \"price_per_kbyte\": 100000\n          }\n        ],[\n          6,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 100000\n          }\n        ],[\n          7,{\n            \"fee\": 300000\n          }\n        ],[\n          8,{\n            \"membership_annual_fee\": 200000000,\n            \"membership_lifetime_fee\": 1000000000\n          }\n        ],[\n          9,{\n            \"fee\": 50000000\n          }\n        ],[\n          10,{\n            \"symbol3\": \"50000000000\",\n            \"symbol4\": \"30000000000\",\n            \"long_symbol\": 500000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          11,{\n            \"fee\": 50000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          12,{\n            \"fee\": 50000000\n          }\n        ],[\n          13,{\n            \"fee\": 50000000\n          }\n        ],[\n          14,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 100000\n          }\n        ],[\n          15,{\n            \"fee\": 2000000\n          }\n        ],[\n          16,{\n            \"fee\": 100000\n          }\n        ],[\n          17,{\n            \"fee\": 10000000\n          }\n        ],[\n          18,{\n            \"fee\": 50000000\n          }\n        ],[\n          19,{\n            \"fee\": 100000\n          }\n        ],[\n          20,{\n            \"fee\": 500000000\n          }\n        ],[\n          21,{\n            \"fee\": 2000000\n          }\n        ],[\n          22,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          23,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          24,{\n            \"fee\": 100000\n          }\n        ],[\n          25,{\n            \"fee\": 100000\n          }\n        ],[\n          26,{\n            \"fee\": 100000\n          }\n        ],[\n          27,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          28,{\n            \"fee\": 0\n          }\n        ],[\n          29,{\n            \"fee\": 500000000\n          }\n        ],[\n          30,{\n            \"fee\": 2000000\n          }\n        ],[\n          31,{\n            \"fee\": 100000\n          }\n        ],[\n          32,{\n            \"fee\": 100000\n          }\n        ],[\n          33,{\n            \"fee\": 2000000\n          }\n        ],[\n          34,{\n            \"fee\": 500000000\n          }\n        ],[\n          35,{\n            \"fee\": 100000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          36,{\n            \"fee\": 100000\n          }\n        ],[\n          37,{}\n        ],[\n          38,{\n            \"fee\": 2000000,\n            \"price_per_kbyte\": 10\n          }\n        ],[\n          39,{\n            \"fee\": 500000,\n            \"price_per_output\": 500000\n          }\n        ],[\n          40,{\n            \"fee\": 500000,\n            \"price_per_output\": 500000\n          }\n        ],[\n          41,{\n            \"fee\": 500000\n          }\n        ],[\n          42,{}\n        ],[\n          43,{\n            \"fee\": 2000000\n          }\n        ],[\n          44,{}\n        ],[\n          45,{\n            \"fee\": 2000000\n          }\n        ],[\n          46,{}\n        ],[\n          47,{\n            \"fee\": 2000000\n          }\n        ],[\n          48,{\n            \"fee\": 2000000\n          }\n        ]\n      ],\n      \"scale\": 10000\n    },\n    \"block_interval\": 3,\n    \"maintenance_interval\": 600,\n    \"maintenance_skip_slots\": 3,\n    \"committee_proposal_review_period\": 1209600,\n    \"maximum_transaction_size\": 2048,\n    \"maximum_block_size\": 2000000,\n    \"maximum_time_until_expiration\": 86400,\n    \"maximum_proposal_lifetime\": 2419200,\n    \"maximum_asset_whitelist_authorities\": 10,\n    \"maximum_asset_feed_publishers\": 10,\n    \"maximum_witness_count\": 1001,\n    \"maximum_committee_count\": 1001,\n    \"maximum_authority_membership\": 10,\n    \"reserve_percent_of_fee\": 2000,\n    \"network_percent_of_fee\": 2000,\n    \"lifetime_referrer_percent_of_fee\": 3000,\n    \"cashback_vesting_period_seconds\": 31536000,\n    \"cashback_vesting_threshold\": 10000000,\n    \"count_non_member_votes\": true,\n    \"allow_non_member_whitelists\": false,\n    \"witness_pay_per_block\": 1000000,\n    \"worker_budget_per_day\": \"50000000000\",\n    \"max_predicate_opcode\": 1,\n    \"fee_liquidation_threshold\": 10000000,\n    \"accounts_per_fee_scale\": 1000,\n    \"account_fee_scale_bitshifts\": 4,\n    \"max_authority_depth\": 2,\n    \"extensions\": {\n        \"updatable_htlc_options\": {\n            \"max_timeout_secs\": 2592000,\n            \"max_preimage_size\": 1024000\n        }\n    }\n  },\n  \"initial_accounts\": [{\n      \"name\": \"init0\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init1\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init2\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init3\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init4\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init5\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init6\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init7\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init8\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init9\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"init10\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": true\n    },{\n      \"name\": \"nathan\",\n      \"owner_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"active_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n      \"is_lifetime_member\": false\n    }\n  ],\n  \"initial_assets\": [],\n  \"initial_balances\": [{\n      \"owner\": \"NBSMZq4SyNoFGraCNuV5ZjbJHMvcYBLSV3rM\",\n      \"asset_symbol\": \"NBS\",\n      \"amount\": \"10000000000000\"\n    }\n  ],\n  \"initial_vesting_balances\": [],\n  \"initial_active_witnesses\": 11,\n  \"initial_witness_candidates\": [{\n      \"owner_name\": \"init0\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init1\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init2\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init3\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init4\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init5\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init6\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init7\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init8\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init9\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    },{\n      \"owner_name\": \"init10\",\n      \"block_signing_key\": \"NBS6WqFcNmb8DA8SG2HJDL23TzSKk67HrCf6qTeqXhbCcohi11epm\",\n    }\n  ],\n  \"initial_committee_candidates\": [{\n      \"owner_name\": \"init0\"\n    },{\n      \"owner_name\": \"init1\"\n    },{\n      \"owner_name\": \"init2\"\n    },{\n      \"owner_name\": \"init3\"\n    },{\n      \"owner_name\": \"init4\"\n    },{\n      \"owner_name\": \"init5\"\n    },{\n      \"owner_name\": \"init6\"\n    },{\n      \"owner_name\": \"init7\"\n    },{\n      \"owner_name\": \"init8\"\n    },{\n      \"owner_name\": \"init9\"\n    },{\n      \"owner_name\": \"init10\"\n    }\n  ],\n  \"initial_worker_candidates\": [],\n  \"immutable_parameters\": {\n    \"min_committee_member_count\": 11,\n    \"min_witness_count\": 11,\n    \"num_special_accounts\": 0,\n    \"num_special_assets\": 0\n  }\n}\n"
  },
  {
    "path": "libraries/egenesis/seed-nodes.txt",
    "content": "\"155.138.131.99:20001\",\n\"155.138.144.189:20001\",\n\"149.28.189.111:20001\",\n\"139.180.188.83:20001\",\n\"144.202.1.184:20001\",\n\"144.202.126.61:19999\",\n\"101.32.206.196:20001\",\n\"81.68.231.71:20001\",\n\"49.233.49.236:20001\","
  },
  {
    "path": "libraries/egenesis/test.json",
    "content": ""
  },
  {
    "path": "libraries/net/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/net/*.hpp\")\n\nset(SOURCES node.cpp\n            stcp_socket.cpp\n            core_messages.cpp\n            exceptions.cpp\n            peer_database.cpp\n            peer_connection.cpp\n            message.cpp\n            message_oriented_connection.cpp)\n\nadd_library( graphene_net ${SOURCES} ${HEADERS} )\n\ntarget_link_libraries( graphene_net \n  PUBLIC fc graphene_db graphene_protocol )\ntarget_include_directories( graphene_net \n  PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\"\n  PRIVATE \"${CMAKE_SOURCE_DIR}/libraries/chain/include\"\n)\n\nif(MSVC)\n  set_source_files_properties( node.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\nif (USE_PCH)\n  set_target_properties(graphene_net PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)\n  cotire(graphene_net)\nendif(USE_PCH)\n\ninstall( TARGETS\n   graphene_net\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\ninstall( FILES ${HEADERS} DESTINATION \"include/graphene/net\" )\n"
  },
  {
    "path": "libraries/net/core_messages.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/net/core_messages.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace net {\n\n  const core_message_type_enum trx_message::type                             = core_message_type_enum::trx_message_type;\n  const core_message_type_enum block_message::type                           = core_message_type_enum::block_message_type;\n  const core_message_type_enum item_ids_inventory_message::type              = core_message_type_enum::item_ids_inventory_message_type;\n  const core_message_type_enum blockchain_item_ids_inventory_message::type   = core_message_type_enum::blockchain_item_ids_inventory_message_type;\n  const core_message_type_enum fetch_blockchain_item_ids_message::type       = core_message_type_enum::fetch_blockchain_item_ids_message_type;\n  const core_message_type_enum fetch_items_message::type                     = core_message_type_enum::fetch_items_message_type;\n  const core_message_type_enum item_not_available_message::type              = core_message_type_enum::item_not_available_message_type;\n  const core_message_type_enum hello_message::type                           = core_message_type_enum::hello_message_type;\n  const core_message_type_enum connection_accepted_message::type             = core_message_type_enum::connection_accepted_message_type;\n  const core_message_type_enum connection_rejected_message::type             = core_message_type_enum::connection_rejected_message_type;\n  const core_message_type_enum address_request_message::type                 = core_message_type_enum::address_request_message_type;\n  const core_message_type_enum address_message::type                         = core_message_type_enum::address_message_type;\n  const core_message_type_enum closing_connection_message::type              = core_message_type_enum::closing_connection_message_type;\n  const core_message_type_enum current_time_request_message::type            = core_message_type_enum::current_time_request_message_type;\n  const core_message_type_enum current_time_reply_message::type              = core_message_type_enum::current_time_reply_message_type;\n  const core_message_type_enum check_firewall_message::type                  = core_message_type_enum::check_firewall_message_type;\n  const core_message_type_enum check_firewall_reply_message::type            = core_message_type_enum::check_firewall_reply_message_type;\n  const core_message_type_enum get_current_connections_request_message::type = core_message_type_enum::get_current_connections_request_message_type;\n  const core_message_type_enum get_current_connections_reply_message::type   = core_message_type_enum::get_current_connections_reply_message_type;\n\n} } // graphene::net\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::trx_message, BOOST_PP_SEQ_NIL, (trx) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::block_message, BOOST_PP_SEQ_NIL, (block)(block_id) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::item_id, BOOST_PP_SEQ_NIL,\n                               (item_type)\n                               (item_hash) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::item_ids_inventory_message, BOOST_PP_SEQ_NIL,\n                                                  (item_type)\n                                                  (item_hashes_available) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::blockchain_item_ids_inventory_message, BOOST_PP_SEQ_NIL,\n                                                             (total_remaining_item_count)\n                                                             (item_type)\n                                                             (item_hashes_available) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::fetch_blockchain_item_ids_message, BOOST_PP_SEQ_NIL,\n                                                         (item_type)\n                                                         (blockchain_synopsis) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::fetch_items_message, BOOST_PP_SEQ_NIL,\n                                           (item_type)\n                                           (items_to_fetch) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::item_not_available_message, BOOST_PP_SEQ_NIL, (requested_item) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::hello_message, BOOST_PP_SEQ_NIL,\n                                     (user_agent)\n                                     (core_protocol_version)\n                                     (inbound_address)\n                                     (inbound_port)\n                                     (outbound_port)\n                                     (node_public_key)\n                                     (signed_shared_secret)\n                                     (chain_id)\n                                     (user_data) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::connection_accepted_message, BOOST_PP_SEQ_NIL, BOOST_PP_SEQ_NIL )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::connection_rejected_message, BOOST_PP_SEQ_NIL,\n                                                   (user_agent)\n                                                   (core_protocol_version)\n                                                   (remote_endpoint)\n                                                   (reason_code)\n                                                   (reason_string))\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::address_request_message, BOOST_PP_SEQ_NIL, BOOST_PP_SEQ_NIL )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::address_info, BOOST_PP_SEQ_NIL,\n                                    (remote_endpoint)\n                                    (last_seen_time)\n                                    (latency)\n                                    (node_id)\n                                    (direction)\n                                    (firewalled) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::address_message, BOOST_PP_SEQ_NIL, (addresses) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::closing_connection_message, BOOST_PP_SEQ_NIL,\n                                                  (reason_for_closing)\n                                                  (closing_due_to_error)\n                                                  (error) )\n\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::current_time_request_message, BOOST_PP_SEQ_NIL, (request_sent_time))\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::current_time_reply_message, BOOST_PP_SEQ_NIL,\n                                                 (request_sent_time)\n                                                 (request_received_time)\n                                                 (reply_transmitted_time))\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::check_firewall_message, BOOST_PP_SEQ_NIL, (node_id)(endpoint_to_check))\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::check_firewall_reply_message, BOOST_PP_SEQ_NIL,\n                                (node_id)(endpoint_checked)(result))\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::get_current_connections_request_message,\n                                BOOST_PP_SEQ_NIL, BOOST_PP_SEQ_NIL)\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::current_connection_data, BOOST_PP_SEQ_NIL,\n                                              (connection_duration)\n                                              (remote_endpoint)\n                                              (node_id)\n                                              (clock_offset)\n                                              (round_trip_delay)\n                                              (connection_direction)\n                                              (firewalled)\n                                              (user_data))\nFC_REFLECT_DERIVED_NO_TYPENAME(graphene::net::get_current_connections_reply_message, BOOST_PP_SEQ_NIL,\n                                                            (upload_rate_one_minute)\n                                                            (download_rate_one_minute)\n                                                            (upload_rate_fifteen_minutes)\n                                                            (download_rate_fifteen_minutes)\n                                                            (upload_rate_one_hour)\n                                                            (download_rate_one_hour)\n                                                            (current_connections))\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::trx_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::block_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::item_id )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::item_ids_inventory_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::blockchain_item_ids_inventory_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::fetch_blockchain_item_ids_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::fetch_items_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::item_not_available_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::hello_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::connection_accepted_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::connection_rejected_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::address_request_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::address_info )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::address_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::closing_connection_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::current_time_request_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::current_time_reply_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::check_firewall_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::check_firewall_reply_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::get_current_connections_request_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::current_connection_data )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::get_current_connections_reply_message )\n"
  },
  {
    "path": "libraries/net/exceptions.cpp",
    "content": "/*\n * Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/net/exceptions.hpp>\n\nnamespace graphene { namespace net {\n\n   FC_IMPLEMENT_EXCEPTION( net_exception, 90000, \"P2P Networking Exception\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( send_queue_overflow,                 net_exception, 90001,\n                                   \"send queue for this peer exceeded maximum size\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( insufficient_relay_fee,              net_exception, 90002,\n                                   \"insufficient relay fee\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( already_connected_to_requested_peer, net_exception, 90003,\n                                   \"already connected to requested peer\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( block_older_than_undo_history,       net_exception, 90004,\n                                   \"block is older than our undo history allows us to process\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( peer_is_on_an_unreachable_fork,      net_exception, 90005,\n                                   \"peer is on another fork\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( unlinkable_block_exception,          net_exception, 90006, \"unlinkable block\" )\n   FC_IMPLEMENT_DERIVED_EXCEPTION( block_timestamp_in_future_exception, net_exception, 90007,\n                                   \"block timestamp in the future\" )\n\n} }\n"
  },
  {
    "path": "libraries/net/include/graphene/net/config.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#define GRAPHENE_NET_PROTOCOL_VERSION                        106\n\n/**\n * Define this to enable debugging code in the p2p network interface.\n * This is code that would never be executed in normal operation, but is\n * used for automated testing (creating artificial net splits,\n * tracking where messages came from and when)\n */\n#define ENABLE_P2P_DEBUGGING_API                             1\n\n/**\n * 2MiB\n */\n#define MAX_MESSAGE_SIZE                                     1024*1024*2\n#define GRAPHENE_NET_DEFAULT_PEER_CONNECTION_RETRY_TIME      30 // seconds\n\n/**\n * AFter trying all peers, how long to wait before we check to\n * see if there are peers we can try again.\n */\n#define GRAPHENE_PEER_DATABASE_RETRY_DELAY                   15 // seconds\n\n#define GRAPHENE_NET_PEER_HANDSHAKE_INACTIVITY_TIMEOUT       5\n\n#define GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT                 20\n\n/* uncomment next line to use testnet seed ip and port */\n//#define GRAPHENE_TEST_NETWORK                                1\n\n#define GRAPHENE_NET_TEST_SEED_IP                            \"104.236.44.210\" // autogenerated\n#define GRAPHENE_NET_TEST_P2P_PORT                           1700\n#define GRAPHENE_NET_DEFAULT_P2P_PORT                        1776\n#define GRAPHENE_NET_DEFAULT_DESIRED_CONNECTIONS             20\n#define GRAPHENE_NET_DEFAULT_MAX_CONNECTIONS                 200\n\n#define GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES        (1024 * 1024)\n\n/**\n * When we receive a message from the network, we advertise it to\n * our peers and save a copy in a cache were we will find it if\n * a peer requests it.  We expire out old items out of the cache\n * after this number of blocks go by.\n * \n * Recently lowered from 30 to match the default expiration time\n * the web wallet imposes on transactions.\n */\n#define GRAPHENE_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS        5\n\n/**\n * We prevent a peer from offering us a list of blocks which, if we fetched them\n * all, would result in a blockchain that extended into the future.\n * This parameter gives us some wiggle room, allowing a peer to give us blocks\n * that would put our blockchain up to an hour in the future, just in case\n * our clock is a bit off.\n */\n#define GRAPHENE_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC     (60 * 60)\n\n#define GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES           2\n\n#define GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING      200\n\n/**\n * During normal operation, how many items will be fetched from each\n * peer at a time.  This will only come into play when the network\n * is being flooded -- typically transactions will be fetched as soon\n * as we find out about them, so only one item will be requested\n * at a time.\n * \n * No tests have been done to find the optimal value for this\n * parameter, so consider increasing or decreasing it if performance\n * during flooding is lacking.\n */\n#define GRAPHENE_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION  1 \n\n/**\n * Instead of fetching all item IDs from a peer, then fetching all blocks\n * from a peer, we will interleave them.  Fetch at least this many block IDs,\n * then switch into block-fetching mode until the number of blocks we know about\n * but haven't yet fetched drops below this\n */\n#define GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH               10000\n\n#define GRAPHENE_NET_MAX_TRX_PER_SECOND                      1000\n\n#define GRAPHENE_NET_MAX_NESTED_OBJECTS                      (250)\n\n#define MAXIMUM_PEERDB_SIZE 1000\n"
  },
  {
    "path": "libraries/net/include/graphene/net/core_messages.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/net/config.hpp>\n\n#include <fc/crypto/ripemd160.hpp>\n#include <fc/crypto/elliptic.hpp>\n#include <fc/crypto/sha256.hpp>\n#include <fc/network/ip.hpp>\n#include <fc/time.hpp>\n#include <fc/variant_object.hpp>\n#include <fc/exception/exception.hpp>\n#include <fc/io/enum_type.hpp>\n\n#include <graphene/protocol/block.hpp>\n\n#include <vector>\n\nnamespace graphene { namespace net {\n  using graphene::protocol::signed_transaction;\n  using graphene::protocol::block_id_type;\n  using graphene::protocol::transaction_id_type;\n  using graphene::protocol::signed_block;\n\n  typedef fc::ecc::public_key_data node_id_t;\n  typedef fc::ripemd160 item_hash_t;\n  struct item_id\n  {\n      uint32_t      item_type;\n      item_hash_t   item_hash;\n\n      item_id() {}\n      item_id(uint32_t type, const item_hash_t& hash) :\n        item_type(type),\n        item_hash(hash)\n      {}\n      bool operator==(const item_id& other) const\n      {\n        return item_type == other.item_type &&\n               item_hash == other.item_hash;\n      }\n  };\n\n  enum core_message_type_enum\n  {\n    trx_message_type                             = 1000,\n    block_message_type                           = 1001,\n    core_message_type_first                      = 5000,\n    item_ids_inventory_message_type              = 5001,\n    blockchain_item_ids_inventory_message_type   = 5002,\n    fetch_blockchain_item_ids_message_type       = 5003,\n    fetch_items_message_type                     = 5004,\n    item_not_available_message_type              = 5005,\n    hello_message_type                           = 5006,\n    connection_accepted_message_type             = 5007,\n    connection_rejected_message_type             = 5008,\n    address_request_message_type                 = 5009,\n    address_message_type                         = 5010,\n    closing_connection_message_type              = 5011,\n    current_time_request_message_type            = 5012,\n    current_time_reply_message_type              = 5013,\n    check_firewall_message_type                  = 5014,\n    check_firewall_reply_message_type            = 5015,\n    get_current_connections_request_message_type = 5016,\n    get_current_connections_reply_message_type   = 5017,\n    core_message_type_last                       = 5099\n  };\n\n  const uint32_t core_protocol_version = GRAPHENE_NET_PROTOCOL_VERSION;\n\n   struct trx_message\n   {\n      static const core_message_type_enum type;\n\n      graphene::protocol::precomputable_transaction trx;\n      trx_message() {}\n      trx_message(graphene::protocol::signed_transaction transaction) :\n        trx(std::move(transaction))\n      {}\n   };\n\n   struct block_message\n   {\n      static const core_message_type_enum type;\n\n      block_message(){}\n      block_message(const signed_block& blk )\n      :block(blk),block_id(blk.id()){}\n\n      signed_block    block;\n      block_id_type   block_id;\n\n   };\n\n  struct item_ids_inventory_message\n  {\n    static const core_message_type_enum type;\n\n    uint32_t item_type;\n    std::vector<item_hash_t> item_hashes_available;\n\n    item_ids_inventory_message() {}\n    item_ids_inventory_message(uint32_t item_type, const std::vector<item_hash_t>& item_hashes_available) :\n      item_type(item_type),\n      item_hashes_available(item_hashes_available)\n    {}\n  };\n\n  struct blockchain_item_ids_inventory_message\n  {\n    static const core_message_type_enum type;\n\n    uint32_t total_remaining_item_count;\n    uint32_t item_type;\n    std::vector<item_hash_t> item_hashes_available;\n\n    blockchain_item_ids_inventory_message() {}\n    blockchain_item_ids_inventory_message(uint32_t total_remaining_item_count,\n                                          uint32_t item_type,\n                                          const std::vector<item_hash_t>& item_hashes_available) :\n      total_remaining_item_count(total_remaining_item_count),\n      item_type(item_type),\n      item_hashes_available(item_hashes_available)\n    {}\n  };\n\n  struct fetch_blockchain_item_ids_message\n  {\n    static const core_message_type_enum type;\n\n    uint32_t item_type;\n    std::vector<item_hash_t> blockchain_synopsis;\n\n    fetch_blockchain_item_ids_message() {}\n    fetch_blockchain_item_ids_message(uint32_t item_type, const std::vector<item_hash_t>& blockchain_synopsis) :\n      item_type(item_type),\n      blockchain_synopsis(blockchain_synopsis)\n    {}\n  };\n\n  struct fetch_items_message\n  {\n    static const core_message_type_enum type;\n\n    uint32_t item_type;\n    std::vector<item_hash_t> items_to_fetch;\n\n    fetch_items_message() {}\n    fetch_items_message(uint32_t item_type, const std::vector<item_hash_t>& items_to_fetch) :\n      item_type(item_type),\n      items_to_fetch(items_to_fetch)\n    {}\n  };\n\n  struct item_not_available_message\n  {\n    static const core_message_type_enum type;\n\n    item_id requested_item;\n\n    item_not_available_message() {}\n    item_not_available_message(const item_id& requested_item) :\n      requested_item(requested_item)\n    {}\n  };\n\n  struct hello_message\n  {\n    static const core_message_type_enum type;\n\n    std::string                user_agent;\n    uint32_t                   core_protocol_version;\n    fc::ip::address            inbound_address;\n    uint16_t                   inbound_port;\n    uint16_t                   outbound_port;\n    node_id_t                  node_public_key;\n    fc::ecc::compact_signature signed_shared_secret;\n    fc::sha256                 chain_id;\n    fc::variant_object         user_data;\n\n    hello_message() {}\n    hello_message(const std::string& user_agent,\n                  uint32_t core_protocol_version,\n                  const fc::ip::address& inbound_address,\n                  uint16_t inbound_port,\n                  uint16_t outbound_port,\n                  const node_id_t& node_public_key,\n                  const fc::ecc::compact_signature& signed_shared_secret,\n                  const fc::sha256& chain_id_arg,\n                  const fc::variant_object& user_data ) :\n      user_agent(user_agent),\n      core_protocol_version(core_protocol_version),\n      inbound_address(inbound_address),\n      inbound_port(inbound_port),\n      outbound_port(outbound_port),\n      node_public_key(node_public_key),\n      signed_shared_secret(signed_shared_secret),\n      chain_id(chain_id_arg),\n      user_data(user_data)\n    {}\n  };\n\n  struct connection_accepted_message\n  {\n    static const core_message_type_enum type;\n\n    connection_accepted_message() {}\n  };\n\n  enum class rejection_reason_code { unspecified,\n                                     different_chain,\n                                     already_connected,\n                                     connected_to_self,\n                                     not_accepting_connections,\n                                     blocked,\n                                     invalid_hello_message,\n                                     client_too_old };\n\n  struct connection_rejected_message\n  {\n    static const core_message_type_enum type;\n\n    std::string                                   user_agent;\n    uint32_t                                      core_protocol_version;\n    fc::ip::endpoint                              remote_endpoint;\n    std::string                                   reason_string;\n    fc::enum_type<uint8_t, rejection_reason_code> reason_code;\n\n    connection_rejected_message() {}\n    connection_rejected_message(const std::string& user_agent, uint32_t core_protocol_version,\n                                const fc::ip::endpoint& remote_endpoint, rejection_reason_code reason_code,\n                                const std::string& reason_string) :\n      user_agent(user_agent),\n      core_protocol_version(core_protocol_version),\n      remote_endpoint(remote_endpoint),\n      reason_string(reason_string),\n      reason_code(reason_code)\n    {}\n  };\n\n  struct address_request_message\n  {\n    static const core_message_type_enum type;\n\n    address_request_message() {}\n  };\n\n  enum class peer_connection_direction { unknown, inbound, outbound };\n  enum class firewalled_state { unknown, firewalled, not_firewalled };\n\n  struct address_info\n  {\n    fc::ip::endpoint          remote_endpoint;\n    fc::time_point_sec        last_seen_time;\n    fc::microseconds          latency;\n    node_id_t                 node_id;\n    fc::enum_type<uint8_t, peer_connection_direction> direction;\n    fc::enum_type<uint8_t, firewalled_state> firewalled;\n\n    address_info() {}\n    address_info(const fc::ip::endpoint& remote_endpoint,\n                 const fc::time_point_sec last_seen_time,\n                 const fc::microseconds latency,\n                 const node_id_t& node_id,\n                 peer_connection_direction direction,\n                 firewalled_state firewalled) :\n      remote_endpoint(remote_endpoint),\n      last_seen_time(last_seen_time),\n      latency(latency),\n      node_id(node_id),\n      direction(direction),\n      firewalled(firewalled)\n    {}\n  };\n\n  struct address_message\n  {\n    static const core_message_type_enum type;\n\n    std::vector<address_info> addresses;\n  };\n\n  struct closing_connection_message\n  {\n    static const core_message_type_enum type;\n\n    std::string        reason_for_closing;\n    bool               closing_due_to_error;\n    fc::oexception     error;\n\n    closing_connection_message() : closing_due_to_error(false) {}\n    closing_connection_message(const std::string& reason_for_closing,\n                               bool closing_due_to_error = false,\n                               const fc::oexception& error = fc::oexception()) :\n      reason_for_closing(reason_for_closing),\n      closing_due_to_error(closing_due_to_error),\n      error(error)\n    {}\n  };\n\n  struct current_time_request_message\n  {\n    static const core_message_type_enum type;\n    fc::time_point request_sent_time;\n\n    current_time_request_message(){}\n    current_time_request_message(const fc::time_point request_sent_time) :\n      request_sent_time(request_sent_time)\n    {}\n  };\n\n  struct current_time_reply_message\n  {\n    static const core_message_type_enum type;\n    fc::time_point request_sent_time;\n    fc::time_point request_received_time;\n    fc::time_point reply_transmitted_time;\n\n    current_time_reply_message(){}\n    current_time_reply_message(const fc::time_point request_sent_time,\n                               const fc::time_point request_received_time,\n                               const fc::time_point reply_transmitted_time = fc::time_point()) :\n      request_sent_time(request_sent_time),\n      request_received_time(request_received_time),\n      reply_transmitted_time(reply_transmitted_time)\n    {}\n  };\n\n  struct check_firewall_message\n  {\n    static const core_message_type_enum type;\n    node_id_t node_id;\n    fc::ip::endpoint endpoint_to_check;\n  };\n\n  enum class firewall_check_result\n  {\n    unable_to_check,\n    unable_to_connect,\n    connection_successful\n  };\n\n  struct check_firewall_reply_message\n  {\n    static const core_message_type_enum type;\n    node_id_t node_id;\n    fc::ip::endpoint endpoint_checked;\n    fc::enum_type<uint8_t, firewall_check_result> result;\n  };\n\n  struct get_current_connections_request_message\n  {\n    static const core_message_type_enum type;\n  };\n\n  struct current_connection_data\n  {\n    uint32_t           connection_duration; // in seconds\n    fc::ip::endpoint   remote_endpoint;\n    node_id_t          node_id;\n    fc::microseconds   clock_offset;\n    fc::microseconds   round_trip_delay;\n    fc::enum_type<uint8_t, peer_connection_direction> connection_direction;\n    fc::enum_type<uint8_t, firewalled_state> firewalled;\n    fc::variant_object user_data;\n  };\n\n  struct get_current_connections_reply_message\n  {\n    static const core_message_type_enum type;\n    uint32_t upload_rate_one_minute;\n    uint32_t download_rate_one_minute;\n    uint32_t upload_rate_fifteen_minutes;\n    uint32_t download_rate_fifteen_minutes;\n    uint32_t upload_rate_one_hour;\n    uint32_t download_rate_one_hour;\n    std::vector<current_connection_data> current_connections;\n  };\n\n} } // graphene::net\n\nFC_REFLECT_ENUM( graphene::net::core_message_type_enum,\n                 (trx_message_type)\n                 (block_message_type)\n                 (core_message_type_first)\n                 (item_ids_inventory_message_type)\n                 (blockchain_item_ids_inventory_message_type)\n                 (fetch_blockchain_item_ids_message_type)\n                 (fetch_items_message_type)\n                 (item_not_available_message_type)\n                 (hello_message_type)\n                 (connection_accepted_message_type)\n                 (connection_rejected_message_type)\n                 (address_request_message_type)\n                 (address_message_type)\n                 (closing_connection_message_type)\n                 (current_time_request_message_type)\n                 (current_time_reply_message_type)\n                 (check_firewall_message_type)\n                 (check_firewall_reply_message_type)\n                 (get_current_connections_request_message_type)\n                 (get_current_connections_reply_message_type)\n                 (core_message_type_last) )\nFC_REFLECT_ENUM(graphene::net::rejection_reason_code, (unspecified)\n                                                 (different_chain)\n                                                 (already_connected)\n                                                 (connected_to_self)\n                                                 (not_accepting_connections)\n                                                 (blocked)\n                                                 (invalid_hello_message)\n                                                 (client_too_old))\nFC_REFLECT_ENUM(graphene::net::peer_connection_direction, (unknown)\n                                                     (inbound)\n                                                     (outbound))\nFC_REFLECT_ENUM(graphene::net::firewalled_state, (unknown)\n                                            (firewalled)\n                                            (not_firewalled))\nFC_REFLECT_ENUM(graphene::net::firewall_check_result, (unable_to_check)\n                                                 (unable_to_connect)\n                                                 (connection_successful))\n\nFC_REFLECT_TYPENAME( graphene::net::trx_message )\nFC_REFLECT_TYPENAME( graphene::net::block_message )\nFC_REFLECT_TYPENAME( graphene::net::item_id )\nFC_REFLECT_TYPENAME( graphene::net::item_ids_inventory_message )\nFC_REFLECT_TYPENAME( graphene::net::blockchain_item_ids_inventory_message )\nFC_REFLECT_TYPENAME( graphene::net::fetch_blockchain_item_ids_message )\nFC_REFLECT_TYPENAME( graphene::net::fetch_items_message )\nFC_REFLECT_TYPENAME( graphene::net::item_not_available_message )\nFC_REFLECT_TYPENAME( graphene::net::hello_message )\nFC_REFLECT_TYPENAME( graphene::net::connection_accepted_message )\nFC_REFLECT_TYPENAME( graphene::net::connection_rejected_message )\nFC_REFLECT_TYPENAME( graphene::net::address_request_message )\nFC_REFLECT_TYPENAME( graphene::net::address_info )\nFC_REFLECT_TYPENAME( graphene::net::address_message )\nFC_REFLECT_TYPENAME( graphene::net::closing_connection_message )\nFC_REFLECT_TYPENAME( graphene::net::current_time_request_message )\nFC_REFLECT_TYPENAME( graphene::net::current_time_reply_message )\nFC_REFLECT_TYPENAME( graphene::net::check_firewall_message )\nFC_REFLECT_TYPENAME( graphene::net::check_firewall_reply_message )\nFC_REFLECT_TYPENAME( graphene::net::get_current_connections_request_message )\nFC_REFLECT_TYPENAME( graphene::net::current_connection_data )\nFC_REFLECT_TYPENAME( graphene::net::get_current_connections_reply_message )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::trx_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::block_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::item_id )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::item_ids_inventory_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::blockchain_item_ids_inventory_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::fetch_blockchain_item_ids_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::fetch_items_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::item_not_available_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::hello_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::connection_accepted_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::connection_rejected_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::address_request_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::address_info )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::address_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::closing_connection_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::current_time_request_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::current_time_reply_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::check_firewall_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::check_firewall_reply_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::get_current_connections_request_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::current_connection_data )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::get_current_connections_reply_message )\n\n#include <unordered_map>\n#include <fc/crypto/city.hpp>\n#include <fc/crypto/sha224.hpp>\nnamespace std\n{\n    template<>\n    struct hash<graphene::net::item_id>\n    {\n       size_t operator()(const graphene::net::item_id& item_to_hash) const\n       {\n          return fc::city_hash_size_t((char*)&item_to_hash, sizeof(item_to_hash));\n       }\n    };\n}\n"
  },
  {
    "path": "libraries/net/include/graphene/net/exceptions.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <fc/exception/exception.hpp>\n\nnamespace graphene { namespace net {\n   // registered in node.cpp \n   \n   FC_DECLARE_EXCEPTION( net_exception, 90000 )\n   FC_DECLARE_DERIVED_EXCEPTION( send_queue_overflow,                 net_exception, 90001 ) \n   FC_DECLARE_DERIVED_EXCEPTION( insufficient_relay_fee,              net_exception, 90002 )\n   FC_DECLARE_DERIVED_EXCEPTION( already_connected_to_requested_peer, net_exception, 90003 )\n   FC_DECLARE_DERIVED_EXCEPTION( block_older_than_undo_history,       net_exception, 90004 )\n   FC_DECLARE_DERIVED_EXCEPTION( peer_is_on_an_unreachable_fork,      net_exception, 90005 )\n   FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception,          net_exception, 90006 )\n   FC_DECLARE_DERIVED_EXCEPTION( block_timestamp_in_future_exception, net_exception, 90007 )\n\n} }\n"
  },
  {
    "path": "libraries/net/include/graphene/net/message.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <boost/endian/buffers.hpp>\n\n#include <graphene/protocol/types.hpp>\n\n#include <fc/io/varint.hpp>\n#include <fc/network/ip.hpp>\n#include <fc/io/raw_fwd.hpp>\n#include <fc/crypto/ripemd160.hpp>\n#include <fc/reflect/typename.hpp>\n\nnamespace graphene { namespace net {\n\n  /**\n   *  Defines an 8 byte header that is always present because the minimum encrypted packet\n   *  size is 8 bytes (blowfish).  The maximum message size is defined in config.hpp. The channel,\n   *  and message type is also included because almost every channel will have a message type\n   *  field and we might as well include it in the 8 byte header to save space.\n   */\n  struct message_header\n  {\n     boost::endian::little_uint32_buf_t size;   // number of bytes in message, capped at MAX_MESSAGE_SIZE\n     boost::endian::little_uint32_buf_t msg_type;  // every channel gets a 16 bit message type specifier\n     message_header()\n     {\n        size = 0;\n        msg_type = 0;\n     }\n  };\n\n  typedef fc::uint160_t message_hash_type;\n\n  /**\n   *  Abstracts the process of packing/unpacking a message for a \n   *  particular channel.\n   */\n  struct message : public message_header\n  {\n     std::vector<char> data;\n\n     message(){}\n\n     message( message&& m )\n     :message_header(m),data( std::move(m.data) ){}\n\n     message( const message& m )\n     :message_header(m),data( m.data ){}\n\n     /**\n      *  Assumes that T::type specifies the message type\n      */\n     template<typename T>\n     message( const T& m ) \n     {\n        msg_type = T::type;\n        data     = fc::raw::pack(m);\n        size     = (uint32_t)data.size();\n     }\n\n     fc::uint160_t id()const\n     {\n        return fc::ripemd160::hash( data.data(), (uint32_t)data.size() );\n     }\n\n     /**\n      *  Automatically checks the type and deserializes T in the\n      *  opposite process from the constructor.\n      */\n     template<typename T>\n     T as()const \n     {\n         try {\n          FC_ASSERT( msg_type.value() == T::type );\n          T tmp;\n          if( data.size() )\n          {\n             fc::datastream<const char*> ds( data.data(), data.size() );\n             fc::raw::unpack( ds, tmp );\n          }\n          else\n          {\n             // just to make sure that tmp shouldn't have any data\n             fc::datastream<const char*> ds( nullptr, 0 );\n             fc::raw::unpack( ds, tmp );\n          }\n          return tmp;\n         } FC_RETHROW_EXCEPTIONS( warn, \n              \"error unpacking network message as a '${type}'  ${x} !=? ${msg_type}\", \n              (\"type\", fc::get_typename<T>::name() )\n              (\"x\", T::type)\n              (\"msg_type\", msg_type.value())\n              );\n     }\n  };\n\n} } // graphene::net\n\nFC_REFLECT_TYPENAME( graphene::net::message_header )\nFC_REFLECT_TYPENAME( graphene::net::message )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::message_header)\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::message)\n"
  },
  {
    "path": "libraries/net/include/graphene/net/message_oriented_connection.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <fc/network/tcp_socket.hpp>\n#include <graphene/net/message.hpp>\n\nnamespace graphene { namespace net {\n\n  namespace detail { class message_oriented_connection_impl; }\n\n  class message_oriented_connection;\n\n  /** receives incoming messages from a message_oriented_connection object */\n  class message_oriented_connection_delegate \n  {\n  public:\n    virtual void on_message(message_oriented_connection* originating_connection, const message& received_message) = 0;\n    virtual void on_connection_closed(message_oriented_connection* originating_connection) = 0;\n  };\n\n  /** uses a secure socket to create a connection that reads and writes a stream of `fc::net::message` objects */\n  class message_oriented_connection\n  {\n     public:\n       message_oriented_connection(message_oriented_connection_delegate* delegate = nullptr);\n       ~message_oriented_connection();\n       fc::tcp_socket& get_socket();\n\n       void accept();\n       void bind(const fc::ip::endpoint& local_endpoint);\n       void connect_to(const fc::ip::endpoint& remote_endpoint);\n\n       void send_message(const message& message_to_send);\n       void close_connection();\n       void destroy_connection();\n\n       uint64_t       get_total_bytes_sent() const;\n       uint64_t       get_total_bytes_received() const;\n       fc::time_point get_last_message_sent_time() const;\n       fc::time_point get_last_message_received_time() const;\n       fc::time_point get_connection_time() const;\n       fc::sha512     get_shared_secret() const;\n     private:\n       std::unique_ptr<detail::message_oriented_connection_impl> my;\n  };\n  typedef std::shared_ptr<message_oriented_connection> message_oriented_connection_ptr;\n\n} } // graphene::net\n"
  },
  {
    "path": "libraries/net/include/graphene/net/node.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/net/core_messages.hpp>\n#include <graphene/net/message.hpp>\n#include <graphene/net/peer_database.hpp>\n\n#include <graphene/protocol/types.hpp>\n\n#include <list>\n\nnamespace graphene { namespace net {\n\n  using fc::variant_object;\n  using graphene::protocol::chain_id_type;\n\n  namespace detail\n  {\n    class node_impl;\n    struct node_impl_deleter\n    {\n      void operator()(node_impl*);\n    };\n  }\n\n  // during network development, we need to track message propagation across the network\n  // using a structure like this:\n  struct message_propagation_data\n  {\n    fc::time_point received_time;\n    fc::time_point validated_time;\n    node_id_t originating_peer;\n  };\n\n   /**\n    *  @class node_delegate\n    *  @brief used by node reports status to client or fetch data from client\n    */\n   class node_delegate\n   {\n      public:\n         virtual ~node_delegate(){}\n\n         /**\n          *  If delegate has the item, the network has no need to fetch it.\n          */\n         virtual bool has_item( const net::item_id& id ) = 0;\n\n         /**\n          *  @brief Called when a new block comes in from the network\n          *\n          *  @param sync_mode true if the message was fetched through the sync process, false during normal operation\n          *  @returns true if this message caused the blockchain to switch forks, false if it did not\n          *\n          *  @throws exception if error validating the item, otherwise the item is\n          *          safe to broadcast on.\n          */\n         virtual bool handle_block( const graphene::net::block_message& blk_msg, bool sync_mode, \n                                    std::vector<fc::uint160_t>& contained_transaction_message_ids ) = 0;\n         \n         /**\n          *  @brief Called when a new transaction comes in from the network\n          *\n          *  @throws exception if error validating the item, otherwise the item is\n          *          safe to broadcast on.\n          */\n         virtual void handle_transaction( const graphene::net::trx_message& trx_msg ) = 0;\n\n         /**\n          *  @brief Called when a new message comes in from the network other than a\n          *         block or a transaction.  Currently there are no other possible \n          *         messages, so this should never be called.\n          *\n          *  @throws exception if error validating the item, otherwise the item is\n          *          safe to broadcast on.\n          */\n         virtual void handle_message( const message& message_to_process ) = 0;\n\n         /**\n          *  Assuming all data elements are ordered in some way, this method should\n          *  return up to limit ids that occur *after* from_id.\n          *  On return, remaining_item_count will be set to the number of items\n          *  in our blockchain after the last item returned in the result,\n          *  or 0 if the result contains the last item in the blockchain\n          */\n         virtual std::vector<item_hash_t> get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,\n                                                        uint32_t& remaining_item_count,\n                                                        uint32_t limit = 2000) = 0;\n\n         /**\n          *  Given the hash of the requested data, fetch the body.\n          */\n         virtual message get_item( const item_id& id ) = 0;\n\n         virtual chain_id_type get_chain_id()const = 0;\n\n         /**\n          * Returns a synopsis of the blockchain used for syncing.\n          * This consists of a list of selected item hashes from our current preferred\n          * blockchain, exponentially falling off into the past.  Horrible explanation.\n          *\n          * If the blockchain is empty, it will return the empty list.\n          * If the blockchain has one block, it will return a list containing just that block.\n          * If it contains more than one block:\n          *   the first element in the list will be the hash of the highest numbered block that\n          *       we cannot undo\n          *   the second element will be the hash of an item at the half way point in the undoable\n          *       segment of the blockchain\n          *   the third will be ~3/4 of the way through the undoable segment of the block chain\n          *   the fourth will be at ~7/8...\n          *     &c.\n          *   the last item in the list will be the hash of the most recent block on our preferred chain\n          */\n         virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point, \n                                                                  uint32_t number_of_blocks_after_reference_point) = 0;\n\n         /**\n          *  Call this after the call to handle_message succeeds.\n          *\n          *  @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call\n          *  @param item_count the number of items known to the node that haven't been sent to handle_item() yet.\n          *                    After `item_count` more calls to handle_item(), the node will be in sync\n          */\n         virtual void     sync_status( uint32_t item_type, uint32_t item_count ) = 0;\n\n         /**\n          *  Call any time the number of connected peers changes.\n          */\n         virtual void     connection_count_changed( uint32_t c ) = 0;\n\n         virtual uint32_t get_block_number(const item_hash_t& block_id) = 0;\n\n         /**\n          * Returns the time a block was produced (if block_id = 0, returns genesis time).\n          * If we don't know about the block, returns time_point_sec::min()\n          */\n         virtual fc::time_point_sec get_block_time(const item_hash_t& block_id) = 0;\n\n         virtual item_hash_t get_head_block_id() const = 0;\n\n         virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const = 0;\n\n         virtual void error_encountered(const std::string& message, const fc::oexception& error) = 0;\n         virtual uint8_t get_current_block_interval_in_seconds() const = 0;\n\n   };\n\n   /**\n    *  Information about connected peers that the client may want to make\n    *  available to the user.\n    */\n   struct peer_status\n   {\n      uint32_t         version;\n      fc::ip::endpoint host;\n      /** info contains the fields required by bitcoin-rpc's getpeerinfo call, we will likely\n          extend it with our own fields. */\n      fc::variant_object info;\n   };\n\n   /**\n    *  @class node\n    *  @brief provides application independent P2P broadcast and data synchronization\n    *\n    *  Unanswered questions:\n    *    when does the node start establishing network connections and accepting peers?\n    *    we don't have enough info to start synchronizing until sync_from() is called,\n    *    would we have any reason to connect before that?\n    */\n   class node : public std::enable_shared_from_this<node>\n   {\n      public:\n        node(const std::string& user_agent);\n        virtual ~node();\n\n        void close();\n\n        void      set_node_delegate( node_delegate* del );\n\n        void      load_configuration( const fc::path& configuration_directory );\n\n        virtual void      listen_to_p2p_network();\n        virtual void      connect_to_p2p_network();\n\n        /**\n         *  Add endpoint to internal level_map database of potential nodes\n         *  to attempt to connect to.  This database is consulted any time\n         *  the number connected peers falls below the target.\n         */\n        void      add_node( const fc::ip::endpoint& ep );\n\n        /*****\n         * @brief add a list of nodes to seed the p2p network\n         * @param seeds a vector of url strings\n         */\n        void add_seed_nodes( std::vector<std::string> seeds );\n\n        /****\n         * @brief add a node to seed the p2p network\n         * @param in the url as a string\n         */\n        void add_seed_node( const std::string& in);\n\n        /**\n         *  Attempt to connect to the specified endpoint immediately.\n         */\n        virtual void connect_to_endpoint( const fc::ip::endpoint& ep );\n\n        /**\n         * @brief Helper to convert a string to a collection of endpoints\n         *\n         * This converts a string (i.e. \"bitshares.eu:665535\" to a collection of endpoints.\n         * NOTE: Throws an exception if not in correct format or was unable to resolve URL.\n         *\n         * @param in the incoming string\n         * @returns a vector of endpoints\n         */\n        static std::vector<fc::ip::endpoint> resolve_string_to_ip_endpoints( const std::string& in );\n\n        /**\n         *  Specifies the network interface and port upon which incoming\n         *  connections should be accepted.\n         */\n        void      listen_on_endpoint( const fc::ip::endpoint& ep, bool wait_if_not_available );\n\n        /**\n         *  Call with true to enable listening for incoming connections\n         */\n        void accept_incoming_connections(bool accept);\n\n        /**\n         *  Specifies the port upon which incoming connections should be accepted.\n         *  @param port the port to listen on\n         *  @param wait_if_not_available if true and the port is not available, enter a\n         *                               sleep and retry loop to wait for it to become\n         *                               available.  If false and the port is not available,\n         *                               just choose a random available port\n         */\n        void      listen_on_port(uint16_t port, bool wait_if_not_available);\n\n        /**\n         * Returns the endpoint the node is listening on.  This is usually the same\n         * as the value previously passed in to listen_on_endpoint, unless we\n         * were unable to bind to that port.\n         */\n        virtual fc::ip::endpoint get_actual_listening_endpoint() const;\n\n        /**\n         *  @return a list of peers that are currently connected.\n         */\n        std::vector<peer_status> get_connected_peers() const;\n\n        /** return the number of peers we're actively connected to */\n        virtual uint32_t get_connection_count() const;\n\n        /**\n         *  Add message to outgoing inventory list, notify peers that\n         *  I have a message ready.\n         */\n        virtual void  broadcast( const message& item_to_broadcast );\n        virtual void  broadcast_transaction( const signed_transaction& trx )\n        {\n           broadcast( trx_message(trx) );\n        }\n\n        /**\n         *  Node starts the process of fetching all items after item_id of the\n         *  given item_type.   During this process messages are not broadcast.\n         */\n        virtual void      sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers);\n\n        bool      is_connected() const;\n\n        void set_advanced_node_parameters(const fc::variant_object& params);\n        fc::variant_object get_advanced_node_parameters();\n        message_propagation_data get_transaction_propagation_data(const graphene::protocol::transaction_id_type& transaction_id);\n        message_propagation_data get_block_propagation_data(const graphene::protocol::block_id_type& block_id);\n        node_id_t get_node_id() const;\n        void set_allowed_peers(const std::vector<node_id_t>& allowed_peers);\n\n        /**\n         * Instructs the node to forget everything in its peer database, mostly for debugging\n         * problems where nodes are failing to connect to the network\n         */\n        void clear_peer_database();\n\n        void set_total_bandwidth_limit(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second);\n\n        fc::variant_object network_get_info() const;\n        fc::variant_object network_get_usage_stats() const;\n\n        std::vector<potential_peer_record> get_potential_peers() const;\n\n        void disable_peer_advertising();\n        fc::variant_object get_call_statistics() const;\n      private:\n        std::unique_ptr<detail::node_impl, detail::node_impl_deleter> my;\n   };\n\n    class simulated_network : public node\n    {\n    public:\n      ~simulated_network();\n      simulated_network(const std::string& user_agent) : node(user_agent) {}\n      void      listen_to_p2p_network() override {}\n      void      connect_to_p2p_network() override {}\n      void      connect_to_endpoint(const fc::ip::endpoint& ep) override {}\n\n      fc::ip::endpoint get_actual_listening_endpoint() const override { return fc::ip::endpoint(); }\n\n      void      sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers) override {}\n      void      broadcast(const message& item_to_broadcast) override;\n      void      add_node_delegate(node_delegate* node_delegate_to_add);\n\n      virtual uint32_t get_connection_count() const override { return 8; }\n    private:\n      struct node_info;\n      void message_sender(node_info* destination_node);\n      std::list<node_info*> network_nodes;\n    };\n\n\n   typedef std::shared_ptr<node> node_ptr;\n   typedef std::shared_ptr<simulated_network> simulated_network_ptr;\n\n} } // graphene::net\n\nFC_REFLECT(graphene::net::message_propagation_data, (received_time)(validated_time)(originating_peer));\nFC_REFLECT( graphene::net::peer_status, (version)(host)(info) );\n"
  },
  {
    "path": "libraries/net/include/graphene/net/peer_connection.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/net/node.hpp>\n#include <graphene/net/peer_database.hpp>\n#include <graphene/net/message_oriented_connection.hpp>\n#include <graphene/net/config.hpp>\n\n#include <boost/tuple/tuple.hpp>\n\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/tag.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n\n#include <queue>\n#include <boost/container/deque.hpp>\n#include <fc/thread/future.hpp>\n\nnamespace graphene { namespace net\n  {\n    struct firewall_check_state_data\n    {\n      node_id_t        expected_node_id;\n      fc::ip::endpoint endpoint_to_test;\n\n      // if we're coordinating a firewall check for another node, these are the helper\n      // nodes we've already had do the test (if this structure is still relevant, that\n      // that means they have all had indeterminate results\n      std::set<node_id_t> nodes_already_tested;\n\n      // If we're a just a helper node, this is the node we report back to\n      // when we have a result\n      node_id_t        requesting_peer;\n    };\n\n    class peer_connection;\n    class peer_connection_delegate\n    {\n    public:\n      virtual ~peer_connection_delegate() = default;\n      virtual void on_message(peer_connection* originating_peer,\n                              const message& received_message) = 0;\n      virtual void on_connection_closed(peer_connection* originating_peer) = 0;\n      virtual message get_message_for_item(const item_id& item) = 0;\n    };\n\n    class peer_connection;\n    typedef std::shared_ptr<peer_connection> peer_connection_ptr;\n    class peer_connection : public message_oriented_connection_delegate,\n                            public std::enable_shared_from_this<peer_connection>\n    {\n    public:\n      enum class our_connection_state\n      {\n        disconnected,\n        just_connected, // if in this state, we have sent a hello_message\n        connection_accepted, // remote side has sent us a connection_accepted, we're operating normally with them\n        connection_rejected // remote side has sent us a connection_rejected, we may be exchanging address with them or may just be waiting for them to close\n      };\n      enum class their_connection_state\n      {\n        disconnected,\n        just_connected, // we have not yet received a hello_message\n        connection_accepted, // we have sent them a connection_accepted\n        connection_rejected // we have sent them a connection_rejected\n      };\n      enum class connection_negotiation_status\n      {\n        disconnected,\n        connecting,\n        connected,\n        accepting,\n        accepted,\n        hello_sent,\n        peer_connection_accepted,\n        peer_connection_rejected,\n        negotiation_complete,\n        closing,\n        closed\n      };\n    private:\n      peer_connection_delegate*      _node;\n      fc::optional<fc::ip::endpoint> _remote_endpoint;\n      message_oriented_connection    _message_connection;\n\n      /* a base class for messages on the queue, to hide the fact that some\n       * messages are complete messages and some are only hashes of messages.\n       */\n      struct queued_message\n      {\n        fc::time_point enqueue_time;\n        fc::time_point transmission_start_time;\n        fc::time_point transmission_finish_time;\n\n        queued_message(fc::time_point enqueue_time = fc::time_point::now()) :\n          enqueue_time(enqueue_time)\n        {}\n\n        virtual message get_message(peer_connection_delegate* node) = 0;\n        /** returns roughly the number of bytes of memory the message is consuming while\n         * it is sitting on the queue\n         */\n        virtual size_t get_size_in_queue() = 0;\n        virtual ~queued_message() {}\n      };\n\n      /* when you queue up a 'real_queued_message', a full copy of the message is\n       * stored on the heap until it is sent\n       */\n      struct real_queued_message : queued_message\n      {\n        message        message_to_send;\n        size_t         message_send_time_field_offset;\n\n        real_queued_message(message message_to_send,\n                            size_t message_send_time_field_offset = (size_t)-1) :\n          message_to_send(std::move(message_to_send)),\n          message_send_time_field_offset(message_send_time_field_offset)\n        {}\n\n        message get_message(peer_connection_delegate* node) override;\n        size_t get_size_in_queue() override;\n      };\n\n      /* when you queue up a 'virtual_queued_message', we just queue up the hash of the\n       * item we want to send.  When it reaches the top of the queue, we make a callback\n       * to the node to generate the message.\n       */\n      struct virtual_queued_message : queued_message\n      {\n        item_id item_to_send;\n\n        virtual_queued_message(item_id item_to_send) :\n          item_to_send(std::move(item_to_send))\n        {}\n\n        message get_message(peer_connection_delegate* node) override;\n        size_t get_size_in_queue() override;\n      };\n\n\n      size_t _total_queued_messages_size = 0;\n      std::queue<std::unique_ptr<queued_message>, std::list<std::unique_ptr<queued_message> > > _queued_messages;\n      fc::future<void> _send_queued_messages_done;\n    public:\n      fc::time_point connection_initiation_time;\n      fc::time_point connection_closed_time;\n      fc::time_point connection_terminated_time;\n      peer_connection_direction direction = peer_connection_direction::unknown;\n      //connection_state state;\n      firewalled_state is_firewalled = firewalled_state::unknown;\n      fc::microseconds clock_offset;\n      fc::microseconds round_trip_delay;\n\n      our_connection_state our_state = our_connection_state::disconnected;\n      bool they_have_requested_close = false;\n      their_connection_state their_state = their_connection_state::disconnected;\n      bool we_have_requested_close = false;\n\n      connection_negotiation_status negotiation_status = connection_negotiation_status::disconnected;\n      fc::oexception connection_closed_error;\n\n      fc::time_point get_connection_time()const { return _message_connection.get_connection_time(); }\n      fc::time_point get_connection_terminated_time()const { return connection_terminated_time; }\n\n      /// data about the peer node\n      /// @{\n      /** node_public_key from the hello message, zero-initialized before we get the hello */\n      node_id_t        node_public_key;\n      /** the unique identifier we'll use to refer to the node with.  zero-initialized before\n       * we receive the hello message, at which time it will be filled with either the \"node_id\"\n       * from the user_data field of the hello, or if none is present it will be filled with a\n       * copy of node_public_key */\n      node_id_t        node_id;\n      uint32_t         core_protocol_version = 0;\n      std::string      user_agent;\n      fc::optional<std::string> graphene_git_revision_sha;\n      fc::optional<fc::time_point_sec> graphene_git_revision_unix_timestamp;\n      fc::optional<std::string> fc_git_revision_sha;\n      fc::optional<fc::time_point_sec> fc_git_revision_unix_timestamp;\n      fc::optional<std::string> platform;\n      fc::optional<uint32_t> bitness;\n\n      // for inbound connections, these fields record what the peer sent us in\n      // its hello message.  For outbound, they record what we sent the peer\n      // in our hello message\n      fc::ip::address inbound_address;\n      uint16_t inbound_port = 0;\n      uint16_t outbound_port = 0;\n      /// @}\n\n      typedef std::unordered_map<item_id, fc::time_point> item_to_time_map_type;\n\n      /// blockchain synchronization state data\n      /// @{\n      boost::container::deque<item_hash_t> ids_of_items_to_get; /// id of items in the blockchain that this peer has told us about\n      std::set<item_hash_t> ids_of_items_being_processed; /// list of all items this peer has offered use that we've already handed to the client but the client hasn't finished processing\n      uint32_t number_of_unfetched_item_ids = 0; /// number of items in the blockchain that follow ids_of_items_to_get but the peer hasn't yet told us their ids\n      bool peer_needs_sync_items_from_us = false;\n      bool we_need_sync_items_from_peer = false;\n      fc::optional<boost::tuple<std::vector<item_hash_t>, fc::time_point> > item_ids_requested_from_peer; /// we check this to detect a timed-out request and in busy()\n      fc::time_point last_sync_item_received_time; /// the time we received the last sync item or the time we sent the last batch of sync item requests to this peer\n      std::set<item_hash_t> sync_items_requested_from_peer; /// ids of blocks we've requested from this peer during sync.  fetch from another peer if this peer disconnects\n      item_hash_t last_block_delegate_has_seen; /// the hash of the last block  this peer has told us about that the peer knows\n      fc::time_point_sec last_block_time_delegate_has_seen;\n      bool inhibit_fetching_sync_blocks = false;\n      /// @}\n\n      /// non-synchronization state data\n      /// @{\n      struct timestamped_item_id\n      {\n        item_id            item;\n        fc::time_point_sec timestamp;\n        timestamped_item_id(const item_id& item, const fc::time_point_sec timestamp) :\n          item(item),\n          timestamp(timestamp)\n        {}\n      };\n      struct timestamp_index{};\n      typedef boost::multi_index_container<timestamped_item_id,\n                                           boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::member<timestamped_item_id, item_id, &timestamped_item_id::item>,\n                                                                                                            std::hash<item_id> >,\n                                                                          boost::multi_index::ordered_non_unique<boost::multi_index::tag<timestamp_index>,\n                                                                                                                 boost::multi_index::member<timestamped_item_id, fc::time_point_sec, &timestamped_item_id::timestamp> > > > timestamped_items_set_type;\n      timestamped_items_set_type inventory_peer_advertised_to_us;\n      timestamped_items_set_type inventory_advertised_to_peer;\n\n      item_to_time_map_type items_requested_from_peer;  /// items we've requested from this peer during normal operation.  fetch from another peer if this peer disconnects\n      /// @}\n\n      // if they're flooding us with transactions, we set this to avoid fetching for a few seconds to let the\n      // blockchain catch up\n      fc::time_point transaction_fetching_inhibited_until;\n\n      uint32_t last_known_fork_block_number = 0;\n\n      fc::future<void> accept_or_connect_task_done;\n\n      firewall_check_state_data *firewall_check_state = nullptr;\n    private:\n#ifndef NDEBUG\n      fc::thread* _thread = nullptr;\n      unsigned _send_message_queue_tasks_running = 0; // temporary debugging\n#endif\n      bool _currently_handling_message = false; // true while we're in the middle of handling a message from the remote system\n      peer_connection(peer_connection_delegate* delegate);\n      void destroy();\n    public:\n      static peer_connection_ptr make_shared(peer_connection_delegate* delegate); // use this instead of the constructor\n      virtual ~peer_connection();\n\n      fc::tcp_socket& get_socket();\n      void accept_connection();\n      void connect_to(const fc::ip::endpoint& remote_endpoint, fc::optional<fc::ip::endpoint> local_endpoint = fc::optional<fc::ip::endpoint>());\n\n      void on_message(message_oriented_connection* originating_connection, const message& received_message) override;\n      void on_connection_closed(message_oriented_connection* originating_connection) override;\n\n      void send_queueable_message(std::unique_ptr<queued_message>&& message_to_send);\n      void send_message(const message& message_to_send, size_t message_send_time_field_offset = (size_t)-1);\n      void send_item(const item_id& item_to_send);\n      void close_connection();\n      void destroy_connection();\n\n      uint64_t get_total_bytes_sent() const;\n      uint64_t get_total_bytes_received() const;\n\n      fc::time_point get_last_message_sent_time() const;\n      fc::time_point get_last_message_received_time() const;\n\n      fc::optional<fc::ip::endpoint> get_remote_endpoint();\n      fc::ip::endpoint get_local_endpoint();\n      void set_remote_endpoint(fc::optional<fc::ip::endpoint> new_remote_endpoint);\n\n      bool busy() const;\n      bool idle() const;\n      bool is_currently_handling_message() const;\n\n      bool is_transaction_fetching_inhibited() const;\n      fc::sha512 get_shared_secret() const;\n      void clear_old_inventory();\n      bool is_inventory_advertised_to_us_list_full_for_transactions() const;\n      bool is_inventory_advertised_to_us_list_full() const;\n      bool performing_firewall_check() const;\n      fc::optional<fc::ip::endpoint> get_endpoint_for_connecting() const;\n    private:\n      void send_queued_messages_task();\n      void accept_connection_task();\n      void connect_to_task(const fc::ip::endpoint& remote_endpoint);\n    };\n    typedef std::shared_ptr<peer_connection> peer_connection_ptr;\n\n } } // end namespace graphene::net\n\n// not sent over the wire, just reflected for logging\nFC_REFLECT_ENUM(graphene::net::peer_connection::our_connection_state, (disconnected)\n                                                                 (just_connected)\n                                                                 (connection_accepted)\n                                                                 (connection_rejected))\nFC_REFLECT_ENUM(graphene::net::peer_connection::their_connection_state, (disconnected)\n                                                                   (just_connected)\n                                                                   (connection_accepted)\n                                                                   (connection_rejected))\nFC_REFLECT_ENUM(graphene::net::peer_connection::connection_negotiation_status, (disconnected)\n                                                                          (connecting)\n                                                                          (connected)\n                                                                          (accepting)\n                                                                          (accepted)\n                                                                          (hello_sent)\n                                                                          (peer_connection_accepted)\n                                                                          (peer_connection_rejected)\n                                                                          (negotiation_complete)\n                                                                          (closing)\n                                                                          (closed) )\n\nFC_REFLECT( graphene::net::peer_connection::timestamped_item_id, (item)(timestamp));\n"
  },
  {
    "path": "libraries/net/include/graphene/net/peer_database.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <boost/iterator/iterator_facade.hpp>\n\n#include <graphene/protocol/types.hpp>\n\n#include <fc/network/ip.hpp>\n#include <fc/time.hpp>\n#include <fc/io/enum_type.hpp>\n#include <fc/reflect/reflect.hpp>\n#include <fc/reflect/variant.hpp>\n#include <fc/exception/exception.hpp>\n\nnamespace graphene { namespace net {\n\n  enum potential_peer_last_connection_disposition\n  {\n    never_attempted_to_connect,\n    last_connection_failed,\n    last_connection_rejected,\n    last_connection_handshaking_failed,\n    last_connection_succeeded\n  };\n\n  struct potential_peer_record\n  {\n    fc::ip::endpoint                  endpoint;\n    fc::time_point_sec                last_seen_time;\n    fc::enum_type<uint8_t,potential_peer_last_connection_disposition> last_connection_disposition;\n    fc::time_point_sec                last_connection_attempt_time;\n    uint32_t                          number_of_successful_connection_attempts;\n    uint32_t                          number_of_failed_connection_attempts;\n    fc::optional<fc::exception>       last_error;\n\n    potential_peer_record() :\n      number_of_successful_connection_attempts(0),\n    number_of_failed_connection_attempts(0){}\n\n    potential_peer_record(fc::ip::endpoint endpoint,\n                          fc::time_point_sec last_seen_time = fc::time_point_sec(),\n                          potential_peer_last_connection_disposition last_connection_disposition = never_attempted_to_connect) :\n      endpoint(endpoint),\n      last_seen_time(last_seen_time),\n      last_connection_disposition(last_connection_disposition),\n      number_of_successful_connection_attempts(0),\n      number_of_failed_connection_attempts(0)\n    {}  \n  };\n\n  namespace detail\n  {\n    class peer_database_impl;\n\n    class peer_database_iterator_impl;\n    class peer_database_iterator : public boost::iterator_facade<peer_database_iterator, const potential_peer_record, boost::forward_traversal_tag>\n    {\n    public:\n      peer_database_iterator();\n      ~peer_database_iterator();\n      explicit peer_database_iterator(peer_database_iterator_impl* impl);\n      peer_database_iterator( const peer_database_iterator& c );\n\n    private:\n      friend class boost::iterator_core_access;\n      void increment();\n      bool equal(const peer_database_iterator& other) const;\n      const potential_peer_record& dereference() const;\n    private:      \n      std::unique_ptr<peer_database_iterator_impl> my;\n    };\n  }\n\n\n  class peer_database\n  {\n  public:\n    peer_database();\n    ~peer_database();\n\n    void open(const fc::path& databaseFilename);\n    void close();\n    void clear();\n\n    void erase(const fc::ip::endpoint& endpointToErase);\n\n    void update_entry(const potential_peer_record& updatedRecord);\n    potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);\n    fc::optional<potential_peer_record> lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);\n\n    typedef detail::peer_database_iterator iterator;\n    iterator begin() const;\n    iterator end() const;\n    size_t size() const;\n  private:\n    std::unique_ptr<detail::peer_database_impl> my;\n  };\n\n} } // end namespace graphene::net\n\nFC_REFLECT_TYPENAME( graphene::net::potential_peer_record )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::net::potential_peer_record)\n"
  },
  {
    "path": "libraries/net/include/graphene/net/stcp_socket.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <fc/network/tcp_socket.hpp>\n#include <fc/crypto/aes.hpp>\n#include <fc/crypto/elliptic.hpp>\n\nnamespace graphene { namespace net {\n\n/**\n *  Uses ECDH to negotiate a aes key for communicating\n *  with other nodes on the network.\n */\nclass stcp_socket : public virtual fc::iostream\n{\n  public:\n    stcp_socket();\n    ~stcp_socket();\n    fc::tcp_socket&  get_socket() { return _sock; }\n    void             accept();\n\n    void             connect_to( const fc::ip::endpoint& remote_endpoint );\n    void             bind( const fc::ip::endpoint& local_endpoint );\n\n    virtual size_t   readsome( char* buffer, size_t max );\n    virtual size_t   readsome( const std::shared_ptr<char>& buf, size_t len, size_t offset );\n    virtual bool     eof()const;\n\n    virtual size_t   writesome( const char* buffer, size_t len );\n    virtual size_t   writesome( const std::shared_ptr<const char>& buf, size_t len, size_t offset );\n\n    virtual void     flush();\n    virtual void     close();\n\n    using istream::get;\n    void             get( char& c ) { read( &c, 1 ); }\n    fc::sha512       get_shared_secret() const { return _shared_secret; }\n  private:\n    void do_key_exchange();\n\n    fc::sha512           _shared_secret;\n    fc::ecc::private_key _priv_key;\n    fc::tcp_socket       _sock;\n    fc::aes_encoder      _send_aes;\n    fc::aes_decoder      _recv_aes;\n    std::shared_ptr<char> _read_buffer;\n    std::shared_ptr<char> _write_buffer;\n#ifndef NDEBUG\n    bool _read_buffer_in_use;\n    bool _write_buffer_in_use;\n#endif\n};\n\ntypedef std::shared_ptr<stcp_socket> stcp_socket_ptr;\n\n} } // graphene::net\n"
  },
  {
    "path": "libraries/net/message.cpp",
    "content": "/*\n * Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <fc/io/raw.hpp>\n\n#include <graphene/net/message.hpp>\n\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::message_header, BOOST_PP_SEQ_NIL, (size)(msg_type) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::message, (graphene::net::message_header), (data) )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::message_header)\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::message)\n"
  },
  {
    "path": "libraries/net/message_oriented_connection.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <fc/thread/thread.hpp>\n#include <fc/thread/mutex.hpp>\n#include <fc/thread/scoped_lock.hpp>\n#include <fc/thread/future.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/io/enum_type.hpp>\n\n#include <graphene/net/message_oriented_connection.hpp>\n#include <graphene/net/stcp_socket.hpp>\n#include <graphene/net/config.hpp>\n\n#include <atomic>\n\n#ifdef DEFAULT_LOGGER\n# undef DEFAULT_LOGGER\n#endif\n#define DEFAULT_LOGGER \"p2p\"\n\n#ifndef NDEBUG\n# define VERIFY_CORRECT_THREAD() assert(_thread->is_current())\n#else\n# define VERIFY_CORRECT_THREAD() do {} while (0)\n#endif\n\nnamespace graphene { namespace net {\n  namespace detail\n  {\n    class message_oriented_connection_impl\n    {\n    private:\n      message_oriented_connection* _self;\n      message_oriented_connection_delegate *_delegate;\n      stcp_socket _sock;\n      fc::promise<void>::ptr _ready_for_sending;\n      fc::future<void> _read_loop_done;\n      uint64_t _bytes_received;\n      uint64_t _bytes_sent;\n\n      fc::time_point _connected_time;\n      fc::time_point _last_message_received_time;\n      fc::time_point _last_message_sent_time;\n\n      std::atomic_bool _send_message_in_progress;\n      std::atomic_bool _read_loop_in_progress;\n#ifndef NDEBUG\n      fc::thread* _thread;\n#endif\n\n      void read_loop();\n      void start_read_loop();\n    public:\n      fc::tcp_socket& get_socket();\n      void accept();\n      void connect_to(const fc::ip::endpoint& remote_endpoint);\n      void bind(const fc::ip::endpoint& local_endpoint);\n\n      message_oriented_connection_impl(message_oriented_connection* self,\n                                       message_oriented_connection_delegate* delegate = nullptr);\n      ~message_oriented_connection_impl();\n\n      void send_message(const message& message_to_send);\n      void close_connection();\n      void destroy_connection();\n\n      uint64_t get_total_bytes_sent() const;\n      uint64_t get_total_bytes_received() const;\n\n      fc::time_point get_last_message_sent_time() const;\n      fc::time_point get_last_message_received_time() const;\n      fc::time_point get_connection_time() const { return _connected_time; }\n      fc::sha512 get_shared_secret() const;\n    };\n\n    message_oriented_connection_impl::message_oriented_connection_impl(message_oriented_connection* self,\n                                                                       message_oriented_connection_delegate* delegate)\n    : _self(self),\n      _delegate(delegate),\n      _ready_for_sending(fc::promise<void>::create()),\n      _bytes_received(0),\n      _bytes_sent(0),\n      _send_message_in_progress(false),\n      _read_loop_in_progress(false)\n#ifndef NDEBUG\n      ,_thread(&fc::thread::current())\n#endif\n    {\n    }\n    message_oriented_connection_impl::~message_oriented_connection_impl()\n    {\n      VERIFY_CORRECT_THREAD();\n      destroy_connection();\n    }\n\n    fc::tcp_socket& message_oriented_connection_impl::get_socket()\n    {\n      VERIFY_CORRECT_THREAD();\n      return _sock.get_socket();\n    }\n\n    void message_oriented_connection_impl::accept()\n    {\n      VERIFY_CORRECT_THREAD();\n      _sock.accept();\n      assert(!_read_loop_done.valid()); // check to be sure we never launch two read loops\n      _read_loop_done = fc::async([=](){ read_loop(); }, \"message read_loop\");\n      _ready_for_sending->set_value();\n    }\n\n    void message_oriented_connection_impl::connect_to(const fc::ip::endpoint& remote_endpoint)\n    {\n      VERIFY_CORRECT_THREAD();\n      _sock.connect_to(remote_endpoint);\n      assert(!_read_loop_done.valid()); // check to be sure we never launch two read loops\n      _read_loop_done = fc::async([=](){ read_loop(); }, \"message read_loop\");\n      _ready_for_sending->set_value();\n    }\n\n    void message_oriented_connection_impl::bind(const fc::ip::endpoint& local_endpoint)\n    {\n      VERIFY_CORRECT_THREAD();\n      _sock.bind(local_endpoint);\n    }\n\n    class no_parallel_execution_guard final\n    {\n      std::atomic_bool* _flag;\n    public:\n      explicit no_parallel_execution_guard(std::atomic_bool* flag) : _flag(flag)\n      {\n         bool expected = false;\n         FC_ASSERT( flag->compare_exchange_strong( expected, true ), \"Only one thread at time can visit it\");\n      }\n      ~no_parallel_execution_guard()\n      {\n         *_flag = false;\n      }\n    };\n\n    void message_oriented_connection_impl::read_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      const int BUFFER_SIZE = 16;\n      const int LEFTOVER = BUFFER_SIZE - sizeof(message_header);\n      static_assert(BUFFER_SIZE >= sizeof(message_header), \"insufficient buffer\");\n\n      no_parallel_execution_guard guard( &_read_loop_in_progress );\n\n      _connected_time = fc::time_point::now();\n\n      fc::oexception exception_to_rethrow;\n      bool call_on_connection_closed = false;\n\n      try\n      {\n        message m;\n        char buffer[BUFFER_SIZE];\n        while( true )\n        {\n          _sock.read(buffer, BUFFER_SIZE);\n          _bytes_received += BUFFER_SIZE;\n          memcpy((char*)&m, buffer, sizeof(message_header));\n          FC_ASSERT( m.size.value() <= MAX_MESSAGE_SIZE, \"\", (\"m.size\",m.size.value())(\"MAX_MESSAGE_SIZE\",MAX_MESSAGE_SIZE) );\n\n          size_t remaining_bytes_with_padding = 16 * ((m.size.value() - LEFTOVER + 15) / 16);\n          m.data.resize(LEFTOVER + remaining_bytes_with_padding); //give extra 16 bytes to allow for padding added in send call\n          std::copy(buffer + sizeof(message_header), buffer + sizeof(buffer), m.data.begin());\n          if (remaining_bytes_with_padding)\n          {\n            _sock.read(&m.data[LEFTOVER], remaining_bytes_with_padding);\n            _bytes_received += remaining_bytes_with_padding;\n          }\n          m.data.resize(m.size.value()); // truncate off the padding bytes\n\n          _last_message_received_time = fc::time_point::now();\n\n          try\n          {\n            // message handling errors are warnings...\n            _delegate->on_message(_self, m);\n          }\n          /// Dedicated catches needed to distinguish from general fc::exception\n          catch ( const fc::canceled_exception& e ) { throw; }\n          catch ( const fc::eof_exception& e ) { throw; }\n          catch ( const fc::exception& e)\n          {\n            /// Here loop should be continued so exception should be just caught locally.\n            wlog( \"message transmission failed ${er}\", (\"er\", e.to_detail_string() ) );\n            throw;\n          }\n        }\n      }\n      catch ( const fc::canceled_exception& e )\n      {\n        wlog( \"caught a canceled_exception in read_loop.  this should mean we're in the process of deleting this object already, so there's no need to notify the delegate: ${e}\", (\"e\", e.to_detail_string() ) );\n        throw;\n      }\n      catch ( const fc::eof_exception& e )\n      {\n        wlog( \"disconnected ${e}\", (\"e\", e.to_detail_string() ) );\n        call_on_connection_closed = true;\n      }\n      catch ( const fc::exception& e )\n      {\n        elog( \"disconnected ${er}\", (\"er\", e.to_detail_string() ) );\n        call_on_connection_closed = true;\n        exception_to_rethrow = fc::unhandled_exception(FC_LOG_MESSAGE(warn, \"disconnected: ${e}\", (\"e\", e.to_detail_string())));\n      }\n      catch ( const std::exception& e )\n      {\n        elog( \"disconnected ${er}\", (\"er\", e.what() ) );\n        call_on_connection_closed = true;\n        exception_to_rethrow = fc::unhandled_exception(FC_LOG_MESSAGE(warn, \"disconnected: ${e}\", (\"e\", e.what())));\n      }\n      catch ( ... )\n      {\n        elog( \"unexpected exception\" );\n        call_on_connection_closed = true;\n        exception_to_rethrow = fc::unhandled_exception(FC_LOG_MESSAGE(warn, \"disconnected: ${e}\", (\"e\", fc::except_str())));\n      }\n\n      if (call_on_connection_closed)\n        _delegate->on_connection_closed(_self);\n\n      if (exception_to_rethrow)\n        throw *exception_to_rethrow;\n    }\n\n    void message_oriented_connection_impl::send_message(const message& message_to_send)\n    {\n      VERIFY_CORRECT_THREAD();\n#if 0 // this gets too verbose\n#ifndef NDEBUG\n      fc::optional<fc::ip::endpoint> remote_endpoint;\n      if (_sock.get_socket().is_open())\n        remote_endpoint = _sock.get_socket().remote_endpoint();\n      struct scope_logger {\n        const fc::optional<fc::ip::endpoint>& endpoint;\n        scope_logger(const fc::optional<fc::ip::endpoint>& endpoint) : endpoint(endpoint) { dlog(\"entering message_oriented_connection::send_message() for peer ${endpoint}\", (\"endpoint\", endpoint)); }\n        ~scope_logger() { dlog(\"leaving message_oriented_connection::send_message() for peer ${endpoint}\", (\"endpoint\", endpoint)); }\n      } send_message_scope_logger(remote_endpoint);\n#endif\n#endif\n      no_parallel_execution_guard guard( &_send_message_in_progress );\n      _ready_for_sending->wait();\n\n      try\n      {\n        size_t size_of_message_and_header = sizeof(message_header) + message_to_send.size.value();\n        if( message_to_send.size.value() > MAX_MESSAGE_SIZE )\n           elog(\"Trying to send a message larger than MAX_MESSAGE_SIZE. This probably won't work...\");\n        //pad the message we send to a multiple of 16 bytes\n        size_t size_with_padding = 16 * ((size_of_message_and_header + 15) / 16);\n        std::unique_ptr<char[]> padded_message(new char[size_with_padding]);\n\n        memcpy(padded_message.get(), (char*)&message_to_send, sizeof(message_header));\n        memcpy(padded_message.get() + sizeof(message_header), message_to_send.data.data(), message_to_send.size.value() );\n        char* padding_space = padded_message.get() + sizeof(message_header) + message_to_send.size.value();\n        memset(padding_space, 0, size_with_padding - size_of_message_and_header);\n        _sock.write(padded_message.get(), size_with_padding);\n        _sock.flush();\n        _bytes_sent += size_with_padding;\n        _last_message_sent_time = fc::time_point::now();\n      } FC_RETHROW_EXCEPTIONS( warn, \"unable to send message\" );\n    }\n\n    void message_oriented_connection_impl::close_connection()\n    {\n      VERIFY_CORRECT_THREAD();\n      _sock.close();\n    }\n\n    void message_oriented_connection_impl::destroy_connection()\n    {\n      VERIFY_CORRECT_THREAD();\n\n      fc::optional<fc::ip::endpoint> remote_endpoint;\n      if (_sock.get_socket().is_open())\n        remote_endpoint = _sock.get_socket().remote_endpoint();\n      ilog( \"in destroy_connection() for ${endpoint}\", (\"endpoint\", remote_endpoint) );\n\n      if (_send_message_in_progress)\n        elog(\"Error: message_oriented_connection is being destroyed while a send_message is in progress.  \"\n             \"The task calling send_message() should have been canceled already\");\n      assert(!_send_message_in_progress);\n\n      try\n      {\n        _read_loop_done.cancel_and_wait(__FUNCTION__);\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while canceling message_oriented_connection's read_loop, ignoring: ${e}\", (\"e\",e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while canceling message_oriented_connection's read_loop, ignoring\" );\n      }\n      _ready_for_sending->set_exception( std::make_shared<fc::canceled_exception>() );\n    }\n\n    uint64_t message_oriented_connection_impl::get_total_bytes_sent() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _bytes_sent;\n    }\n\n    uint64_t message_oriented_connection_impl::get_total_bytes_received() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _bytes_received;\n    }\n\n    fc::time_point message_oriented_connection_impl::get_last_message_sent_time() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _last_message_sent_time;\n    }\n\n    fc::time_point message_oriented_connection_impl::get_last_message_received_time() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _last_message_received_time;\n    }\n\n    fc::sha512 message_oriented_connection_impl::get_shared_secret() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _sock.get_shared_secret();\n    }\n\n  } // end namespace graphene::net::detail\n\n\n  message_oriented_connection::message_oriented_connection(message_oriented_connection_delegate* delegate) :\n    my(new detail::message_oriented_connection_impl(this, delegate))\n  {\n  }\n\n  message_oriented_connection::~message_oriented_connection()\n  {\n  }\n\n  fc::tcp_socket& message_oriented_connection::get_socket()\n  {\n    return my->get_socket();\n  }\n\n  void message_oriented_connection::accept()\n  {\n    my->accept();\n  }\n\n  void message_oriented_connection::connect_to(const fc::ip::endpoint& remote_endpoint)\n  {\n    my->connect_to(remote_endpoint);\n  }\n\n  void message_oriented_connection::bind(const fc::ip::endpoint& local_endpoint)\n  {\n    my->bind(local_endpoint);\n  }\n\n  void message_oriented_connection::send_message(const message& message_to_send)\n  {\n    my->send_message(message_to_send);\n  }\n\n  void message_oriented_connection::close_connection()\n  {\n    my->close_connection();\n  }\n\n  void message_oriented_connection::destroy_connection()\n  {\n    my->destroy_connection();\n  }\n\n  uint64_t message_oriented_connection::get_total_bytes_sent() const\n  {\n    return my->get_total_bytes_sent();\n  }\n\n  uint64_t message_oriented_connection::get_total_bytes_received() const\n  {\n    return my->get_total_bytes_received();\n  }\n\n  fc::time_point message_oriented_connection::get_last_message_sent_time() const\n  {\n    return my->get_last_message_sent_time();\n  }\n\n  fc::time_point message_oriented_connection::get_last_message_received_time() const\n  {\n    return my->get_last_message_received_time();\n  }\n  fc::time_point message_oriented_connection::get_connection_time() const\n  {\n    return my->get_connection_time();\n  }\n  fc::sha512 message_oriented_connection::get_shared_secret() const\n  {\n    return my->get_shared_secret();\n  }\n\n} } // end namespace graphene::net\n"
  },
  {
    "path": "libraries/net/node.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <sstream>\n#include <iomanip>\n#include <deque>\n#include <unordered_set>\n#include <list>\n#include <forward_list>\n#include <iostream>\n#include <algorithm>\n#include <tuple>\n#include <string>\n#include <boost/tuple/tuple.hpp>\n#include <boost/circular_buffer.hpp>\n\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/random_access_index.hpp>\n#include <boost/multi_index/tag.hpp>\n#include <boost/multi_index/sequenced_index.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n#include <boost/logic/tribool.hpp>\n#include <boost/range/algorithm_ext/push_back.hpp>\n#include <boost/range/algorithm/find.hpp>\n#include <boost/range/numeric.hpp>\n\n#include <boost/accumulators/accumulators.hpp>\n#include <boost/accumulators/statistics/stats.hpp>\n#include <boost/accumulators/statistics/rolling_mean.hpp>\n#include <boost/accumulators/statistics/min.hpp>\n#include <boost/accumulators/statistics/max.hpp>\n#include <boost/accumulators/statistics/sum.hpp>\n#include <boost/accumulators/statistics/count.hpp>\n\n#include <boost/preprocessor/seq/for_each.hpp>\n#include <boost/preprocessor/cat.hpp>\n#include <boost/preprocessor/stringize.hpp>\n\n#include <fc/thread/thread.hpp>\n#include <fc/thread/future.hpp>\n#include <fc/thread/non_preemptable_scope_check.hpp>\n#include <fc/thread/mutex.hpp>\n#include <fc/thread/scoped_lock.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/io/json.hpp>\n#include <fc/io/enum_type.hpp>\n#include <fc/io/raw.hpp>\n#include <fc/crypto/rand.hpp>\n#include <fc/network/rate_limiting.hpp>\n#include <fc/network/ip.hpp>\n#include <fc/network/resolve.hpp>\n\n#include <graphene/net/node.hpp>\n#include <graphene/net/peer_database.hpp>\n#include <graphene/net/peer_connection.hpp>\n#include <graphene/net/stcp_socket.hpp>\n#include <graphene/net/config.hpp>\n#include <graphene/net/exceptions.hpp>\n\n#include <graphene/chain/config.hpp>\n#include <graphene/chain/exceptions.hpp>\n// Nasty hack: A circular dependency around fee_schedule is resolved by fwd-declaring it and using a shared_ptr\n// to it in chain_parameters, which is used in an operation and thus must be serialized by the net library.\n// Resolving that forward declaration doesn't happen until now:\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/git_revision.hpp>\n\n//#define ENABLE_DEBUG_ULOGS\n\n#ifdef DEFAULT_LOGGER\n# undef DEFAULT_LOGGER\n#endif\n#define DEFAULT_LOGGER \"p2p\"\n\n#define P2P_IN_DEDICATED_THREAD 1\n\n#define INVOCATION_COUNTER(name) \\\n    static unsigned total_ ## name ## _counter = 0; \\\n    static unsigned active_ ## name ## _counter = 0; \\\n    struct name ## _invocation_logger { \\\n      unsigned *total; \\\n      unsigned *active; \\\n      name ## _invocation_logger(unsigned *total, unsigned *active) : \\\n        total(total), active(active) \\\n      { \\\n        ++*total; \\\n        ++*active; \\\n        dlog(\"NEWDEBUG: Entering \" #name \", now ${total} total calls, ${active} active calls\", (\"total\", *total)(\"active\", *active)); \\\n      } \\\n      ~name ## _invocation_logger() \\\n      { \\\n        --*active; \\\n        dlog(\"NEWDEBUG: Leaving \" #name \", now ${total} total calls, ${active} active calls\", (\"total\", *total)(\"active\", *active)); \\\n      } \\\n    } invocation_logger(&total_ ## name ## _counter, &active_ ## name ## _counter)\n\n//log these messages even at warn level when operating on the test network\n#ifdef GRAPHENE_TEST_NETWORK\n#define testnetlog wlog\n#else\n#define testnetlog(...) do {} while (0)\n#endif\n\nnamespace graphene { namespace net {\n\n  namespace detail\n  {\n    namespace bmi = boost::multi_index;\n    class blockchain_tied_message_cache\n    {\n    private:\n      static const uint32_t cache_duration_in_blocks = GRAPHENE_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS;\n\n      struct message_hash_index{};\n      struct message_contents_hash_index{};\n      struct block_clock_index{};\n      struct message_info\n      {\n        message_hash_type message_hash;\n        message           message_body;\n        uint32_t          block_clock_when_received;\n\n        // for network performance stats\n        message_propagation_data propagation_data;\n        fc::uint160_t     message_contents_hash; // hash of whatever the message contains (if it's a transaction, this is the transaction id, if it's a block, it's the block_id)\n\n        message_info( const message_hash_type& message_hash,\n                      const message&           message_body,\n                      uint32_t                 block_clock_when_received,\n                      const message_propagation_data& propagation_data,\n                      fc::uint160_t            message_contents_hash ) :\n          message_hash( message_hash ),\n          message_body( message_body ),\n          block_clock_when_received( block_clock_when_received ),\n          propagation_data( propagation_data ),\n          message_contents_hash( message_contents_hash )\n        {}\n      };\n      typedef boost::multi_index_container\n        < message_info,\n            bmi::indexed_by< bmi::ordered_unique< bmi::tag<message_hash_index>,\n                                                  bmi::member<message_info, message_hash_type, &message_info::message_hash> >,\n                             bmi::ordered_non_unique< bmi::tag<message_contents_hash_index>,\n                                                      bmi::member<message_info, fc::uint160_t, &message_info::message_contents_hash> >,\n                             bmi::ordered_non_unique< bmi::tag<block_clock_index>,\n                                                      bmi::member<message_info, uint32_t, &message_info::block_clock_when_received> > >\n        > message_cache_container;\n\n      message_cache_container _message_cache;\n\n      uint32_t block_clock;\n\n    public:\n      blockchain_tied_message_cache() :\n        block_clock( 0 )\n      {}\n      void block_accepted();\n      void cache_message( const message& message_to_cache, const message_hash_type& hash_of_message_to_cache,\n                        const message_propagation_data& propagation_data, const fc::uint160_t& message_content_hash );\n      message get_message( const message_hash_type& hash_of_message_to_lookup );\n      message_propagation_data get_message_propagation_data( const fc::uint160_t& hash_of_message_contents_to_lookup ) const;\n      size_t size() const { return _message_cache.size(); }\n    };\n\n    void blockchain_tied_message_cache::block_accepted()\n    {\n      ++block_clock;\n      if( block_clock > cache_duration_in_blocks )\n        _message_cache.get<block_clock_index>().erase(_message_cache.get<block_clock_index>().begin(),\n                                                      _message_cache.get<block_clock_index>().lower_bound(block_clock - cache_duration_in_blocks ) );\n    }\n\n    void blockchain_tied_message_cache::cache_message( const message& message_to_cache,\n                                                     const message_hash_type& hash_of_message_to_cache,\n                                                     const message_propagation_data& propagation_data,\n                                                     const fc::uint160_t& message_content_hash )\n    {\n      _message_cache.insert( message_info(hash_of_message_to_cache,\n                                         message_to_cache,\n                                         block_clock,\n                                         propagation_data,\n                                         message_content_hash ) );\n    }\n\n    message blockchain_tied_message_cache::get_message( const message_hash_type& hash_of_message_to_lookup )\n    {\n      message_cache_container::index<message_hash_index>::type::const_iterator iter =\n         _message_cache.get<message_hash_index>().find(hash_of_message_to_lookup );\n      if( iter != _message_cache.get<message_hash_index>().end() )\n        return iter->message_body;\n      FC_THROW_EXCEPTION(  fc::key_not_found_exception, \"Requested message not in cache\" );\n    }\n\n    message_propagation_data blockchain_tied_message_cache::get_message_propagation_data( const fc::uint160_t& hash_of_message_contents_to_lookup ) const\n    {\n      if( hash_of_message_contents_to_lookup != fc::uint160_t() )\n      {\n        message_cache_container::index<message_contents_hash_index>::type::const_iterator iter =\n           _message_cache.get<message_contents_hash_index>().find(hash_of_message_contents_to_lookup );\n        if( iter != _message_cache.get<message_contents_hash_index>().end() )\n          return iter->propagation_data;\n      }\n      FC_THROW_EXCEPTION(  fc::key_not_found_exception, \"Requested message not in cache\" );\n    }\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    // This specifies configuration info for the local node.  It's stored as JSON\n    // in the configuration directory (application data directory)\n    struct node_configuration\n    {\n      node_configuration() : accept_incoming_connections(true), wait_if_endpoint_is_busy(true) {}\n\n      fc::ip::endpoint listen_endpoint;\n      bool accept_incoming_connections;\n      bool wait_if_endpoint_is_busy;\n      /**\n       * Originally, our p2p code just had a 'node-id' that was a random number identifying this node\n       * on the network.  This is now a private key/public key pair, where the public key is used\n       * in place of the old random node-id.  The private part is unused, but might be used in\n       * the future to support some notion of trusted peers.\n       */\n      fc::ecc::private_key private_key;\n    };\n\n\n} } } // end namespace graphene::net::detail\nFC_REFLECT(graphene::net::detail::node_configuration, (listen_endpoint)\n                                                 (accept_incoming_connections)\n                                                 (wait_if_endpoint_is_busy)\n                                                 (private_key));\n\n#include \"node_impl.hxx\"\n\nnamespace graphene { namespace net { namespace detail {\n\n    void node_impl_deleter::operator()(node_impl* impl_to_delete)\n    {\n#ifdef P2P_IN_DEDICATED_THREAD\n      std::weak_ptr<fc::thread> weak_thread;\n      if (impl_to_delete)\n      {\n        std::shared_ptr<fc::thread> impl_thread(impl_to_delete->_thread);\n        weak_thread = impl_thread;\n        impl_thread->async([impl_to_delete](){ delete impl_to_delete; }, \"delete node_impl\").wait();\n        dlog(\"deleting the p2p thread\");\n      }\n      if (weak_thread.expired())\n        dlog(\"done deleting the p2p thread\");\n      else\n        dlog(\"failed to delete the p2p thread, we must be leaking a smart pointer somewhere\");\n#else // P2P_IN_DEDICATED_THREAD\n      delete impl_to_delete;\n#endif // P2P_IN_DEDICATED_THREAD\n    }\n\n#ifdef P2P_IN_DEDICATED_THREAD\n# define VERIFY_CORRECT_THREAD() assert(_thread->is_current())\n#else\n# define VERIFY_CORRECT_THREAD() do {} while (0)\n#endif\n\n#define MAXIMUM_NUMBER_OF_BLOCKS_TO_HANDLE_AT_ONE_TIME 200\n#define MAXIMUM_NUMBER_OF_BLOCKS_TO_PREFETCH (10 * MAXIMUM_NUMBER_OF_BLOCKS_TO_HANDLE_AT_ONE_TIME)\n\n    node_impl::node_impl(const std::string& user_agent) :\n#ifdef P2P_IN_DEDICATED_THREAD\n      _thread(std::make_shared<fc::thread>(\"p2p\")),\n#endif // P2P_IN_DEDICATED_THREAD\n      _delegate(nullptr),\n      _is_firewalled(firewalled_state::unknown),\n      _potential_peer_database_updated(false),\n      _sync_items_to_fetch_updated(false),\n      _suspend_fetching_sync_blocks(false),\n      _items_to_fetch_updated(false),\n      _items_to_fetch_sequence_counter(0),\n      _recent_block_interval_in_seconds(GRAPHENE_MAX_BLOCK_INTERVAL),\n      _user_agent_string(user_agent),\n      _desired_number_of_connections(GRAPHENE_NET_DEFAULT_DESIRED_CONNECTIONS),\n      _maximum_number_of_connections(GRAPHENE_NET_DEFAULT_MAX_CONNECTIONS),\n      _peer_connection_retry_timeout(GRAPHENE_NET_DEFAULT_PEER_CONNECTION_RETRY_TIME),\n      _peer_inactivity_timeout(GRAPHENE_NET_PEER_HANDSHAKE_INACTIVITY_TIMEOUT),\n      _most_recent_blocks_accepted(_maximum_number_of_connections),\n      _total_number_of_unfetched_items(0),\n      _rate_limiter(0, 0),\n      _last_reported_number_of_connections(0),\n      _peer_advertising_disabled(false),\n      _average_network_read_speed_seconds(60),\n      _average_network_write_speed_seconds(60),\n      _average_network_read_speed_minutes(60),\n      _average_network_write_speed_minutes(60),\n      _average_network_read_speed_hours(72),\n      _average_network_write_speed_hours(72),\n      _average_network_usage_second_counter(0),\n      _average_network_usage_minute_counter(0),\n      _node_is_shutting_down(false),\n      _maximum_number_of_blocks_to_handle_at_one_time(MAXIMUM_NUMBER_OF_BLOCKS_TO_HANDLE_AT_ONE_TIME),\n      _maximum_number_of_sync_blocks_to_prefetch(MAXIMUM_NUMBER_OF_BLOCKS_TO_PREFETCH),\n      _maximum_blocks_per_peer_during_syncing(GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING)\n    {\n      _rate_limiter.set_actual_rate_time_constant(fc::seconds(2));\n      fc::rand_bytes((char*) _node_id.data(), (int)_node_id.size());\n    }\n\n    node_impl::~node_impl()\n    {\n      VERIFY_CORRECT_THREAD();\n      ilog( \"cleaning up node\" );\n      _node_is_shutting_down = true;\n\n      for (const peer_connection_ptr& active_peer : _active_connections)\n      {\n        fc::optional<fc::ip::endpoint> inbound_endpoint = active_peer->get_endpoint_for_connecting();\n        if (inbound_endpoint)\n        {\n          fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint);\n          if (updated_peer_record)\n          {\n            updated_peer_record->last_seen_time = fc::time_point::now();\n            _potential_peer_db.update_entry(*updated_peer_record);\n          }\n        }\n      }\n\n      try\n      {\n        ilog( \"close\" );\n        close();\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"unexpected exception on close ${e}\", (\"e\", e) );\n      }\n      ilog( \"done\" );\n    }\n\n    void node_impl::save_node_configuration()\n    {\n      VERIFY_CORRECT_THREAD();\n      if( fc::exists(_node_configuration_directory ) )\n      {\n        fc::path configuration_file_name( _node_configuration_directory / NODE_CONFIGURATION_FILENAME );\n        try\n        {\n          fc::json::save_to_file( _node_configuration, configuration_file_name );\n        }\n        catch (const fc::canceled_exception&)\n        {\n          throw;\n        }\n        catch ( const fc::exception& except )\n        {\n          elog( \"error writing node configuration to file ${filename}: ${error}\",\n               ( \"filename\", configuration_file_name )(\"error\", except.to_detail_string() ) );\n        }\n      }\n    }\n\n    void node_impl::p2p_network_connect_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      while (!_p2p_network_connect_loop_done.canceled())\n      {\n        try\n        {\n          dlog(\"Starting an iteration of p2p_network_connect_loop().\");\n          display_current_connections();\n\n          // add-once peers bypass our checks on the maximum/desired number of connections (but they will still be counted against the totals once they're connected)\n          if (!_add_once_node_list.empty())\n          {\n            std::list<potential_peer_record> add_once_node_list;\n            add_once_node_list.swap(_add_once_node_list);\n            dlog(\"Processing \\\"add once\\\" node list containing ${count} peers:\", (\"count\", add_once_node_list.size()));\n            for (const potential_peer_record& add_once_peer : add_once_node_list)\n            {\n              dlog(\"    ${peer}\", (\"peer\", add_once_peer.endpoint));\n            }\n            for (const potential_peer_record& add_once_peer : add_once_node_list)\n            {\n              // see if we have an existing connection to that peer.  If we do, disconnect them and\n              // then try to connect the next time through the loop\n              peer_connection_ptr existing_connection_ptr = get_connection_to_endpoint( add_once_peer.endpoint );\n              if(!existing_connection_ptr)\n                connect_to_endpoint(add_once_peer.endpoint);\n            }\n            dlog(\"Done processing \\\"add once\\\" node list\");\n          }\n\n          while (is_wanting_new_connections())\n          {\n            bool initiated_connection_this_pass = false;\n            _potential_peer_database_updated = false;\n\n            for (peer_database::iterator iter = _potential_peer_db.begin();\n                 iter != _potential_peer_db.end() && is_wanting_new_connections();\n                 ++iter)\n            {\n              fc::microseconds delay_until_retry = fc::seconds((iter->number_of_failed_connection_attempts + 1) * _peer_connection_retry_timeout);\n\n              if (!is_connection_to_endpoint_in_progress(iter->endpoint) &&\n                  ((iter->last_connection_disposition != last_connection_failed &&\n                    iter->last_connection_disposition != last_connection_rejected &&\n                    iter->last_connection_disposition != last_connection_handshaking_failed) ||\n                   (fc::time_point::now() - iter->last_connection_attempt_time) > delay_until_retry))\n              {\n                connect_to_endpoint(iter->endpoint);\n                initiated_connection_this_pass = true;\n              }\n            }\n\n            if (!initiated_connection_this_pass && !_potential_peer_database_updated)\n              break;\n          }\n\n          display_current_connections();\n\n          // if we broke out of the while loop, that means either we have connected to enough nodes, or\n          // we don't have any good candidates to connect to right now.\n#if 0\n          try\n          {\n            _retrigger_connect_loop_promise = fc::promise<void>::create(\"graphene::net::retrigger_connect_loop\");\n            if( is_wanting_new_connections() || !_add_once_node_list.empty() )\n            {\n              if( is_wanting_new_connections() )\n                dlog( \"Still want to connect to more nodes, but I don't have any good candidates.  Trying again in 15 seconds\" );\n              else\n                dlog( \"I still have some \\\"add once\\\" nodes to connect to.  Trying again in 15 seconds\" );\n              _retrigger_connect_loop_promise->wait_until( fc::time_point::now() + fc::seconds(GRAPHENE_PEER_DATABASE_RETRY_DELAY ) );\n            }\n            else\n            {\n              dlog( \"I don't need any more connections, waiting forever until something changes\" );\n              _retrigger_connect_loop_promise->wait();\n            }\n          }\n          catch ( fc::timeout_exception& ) //intentionally not logged\n          {\n          }  // catch\n#else\n          fc::usleep(fc::seconds(10));\n#endif\n        }\n        catch (const fc::canceled_exception&)\n        {\n          throw;\n        }\n        FC_CAPTURE_AND_LOG( (0) )\n      }// while(!canceled)\n    }\n\n    void node_impl::trigger_p2p_network_connect_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog( \"Triggering connect loop now\" );\n      _potential_peer_database_updated = true;\n      //if( _retrigger_connect_loop_promise )\n      //  _retrigger_connect_loop_promise->set_value();\n    }\n\n   void node_impl::update_seed_nodes_task()\n   {\n      VERIFY_CORRECT_THREAD();\n\n      try\n      {\n         dlog(\"Starting an iteration of update_seed_nodes loop.\");\n         for( const std::string& endpoint_string : _seed_nodes )\n         {\n            resolve_seed_node_and_add( endpoint_string );\n         }\n         dlog(\"Done an iteration of update_seed_nodes loop.\");\n      }\n      catch (const fc::canceled_exception&)\n      {\n        throw;\n      }\n      FC_CAPTURE_AND_LOG( (_seed_nodes) )\n\n      schedule_next_update_seed_nodes_task();\n   }\n\n   void node_impl::schedule_next_update_seed_nodes_task()\n   {\n      VERIFY_CORRECT_THREAD();\n\n      if( _node_is_shutting_down )\n         return;\n\n      if( _update_seed_nodes_loop_done.valid() && _update_seed_nodes_loop_done.canceled() )\n         return;\n\n      _update_seed_nodes_loop_done = fc::schedule( [this]() { update_seed_nodes_task(); },\n                                                   fc::time_point::now() + fc::hours(3),\n                                                   \"update_seed_nodes_loop\" );\n   }\n\n    bool node_impl::have_already_received_sync_item( const item_hash_t& item_hash )\n    {\n      VERIFY_CORRECT_THREAD();\n      return std::find_if(_received_sync_items.begin(), _received_sync_items.end(),\n                          [&item_hash]( const graphene::net::block_message& message ) { return message.block_id == item_hash; } ) != _received_sync_items.end() ||\n             std::find_if(_new_received_sync_items.begin(), _new_received_sync_items.end(),\n                          [&item_hash]( const graphene::net::block_message& message ) { return message.block_id == item_hash; } ) != _new_received_sync_items.end();                          ;\n    }\n\n    void node_impl::request_sync_item_from_peer( const peer_connection_ptr& peer, const item_hash_t& item_to_request )\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog( \"requesting item ${item_hash} from peer ${endpoint}\", (\"item_hash\", item_to_request )(\"endpoint\", peer->get_remote_endpoint() ) );\n      item_id item_id_to_request( graphene::net::block_message_type, item_to_request );\n      _active_sync_requests.insert( active_sync_requests_map::value_type(item_to_request, fc::time_point::now() ) );\n      peer->last_sync_item_received_time = fc::time_point::now();\n      peer->sync_items_requested_from_peer.insert(item_to_request);\n      peer->send_message( fetch_items_message(item_id_to_request.item_type, std::vector<item_hash_t>{item_id_to_request.item_hash} ) );\n    }\n\n    void node_impl::request_sync_items_from_peer( const peer_connection_ptr& peer, const std::vector<item_hash_t>& items_to_request )\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog( \"requesting ${item_count} item(s) ${items_to_request} from peer ${endpoint}\",\n            (\"item_count\", items_to_request.size())(\"items_to_request\", items_to_request)(\"endpoint\", peer->get_remote_endpoint()) );\n      for (const item_hash_t& item_to_request : items_to_request)\n      {\n        _active_sync_requests.insert( active_sync_requests_map::value_type(item_to_request, fc::time_point::now() ) );\n        peer->last_sync_item_received_time = fc::time_point::now();\n        peer->sync_items_requested_from_peer.insert(item_to_request);\n      }\n      peer->send_message(fetch_items_message(graphene::net::block_message_type, items_to_request));\n    }\n\n    void node_impl::fetch_sync_items_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      while( !_fetch_sync_items_loop_done.canceled() )\n      {\n        _sync_items_to_fetch_updated = false;\n        dlog( \"beginning another iteration of the sync items loop\" );\n\n        if (!_suspend_fetching_sync_blocks)\n        {\n          std::map<peer_connection_ptr, std::vector<item_hash_t> > sync_item_requests_to_send;\n\n          {\n            ASSERT_TASK_NOT_PREEMPTED();\n            std::set<item_hash_t> sync_items_to_request;\n\n            // for each idle peer that we're syncing with\n            for( const peer_connection_ptr& peer : _active_connections )\n            {\n              if( peer->we_need_sync_items_from_peer &&\n                  sync_item_requests_to_send.find(peer) == sync_item_requests_to_send.end() && // if we've already scheduled a request for this peer, don't consider scheduling another\n                  peer->idle() )\n              {\n                if (!peer->inhibit_fetching_sync_blocks)\n                {\n                  // loop through the items it has that we don't yet have on our blockchain\n                  for( unsigned i = 0; i < peer->ids_of_items_to_get.size(); ++i )\n                  {\n                    item_hash_t item_to_potentially_request = peer->ids_of_items_to_get[i];\n                    // if we don't already have this item in our temporary storage and we haven't requested from another syncing peer\n                    if( !have_already_received_sync_item(item_to_potentially_request) && // already got it, but for some reson it's still in our list of items to fetch\n                        sync_items_to_request.find(item_to_potentially_request) == sync_items_to_request.end() &&  // we have already decided to request it from another peer during this iteration\n                        _active_sync_requests.find(item_to_potentially_request) == _active_sync_requests.end() ) // we've requested it in a previous iteration and we're still waiting for it to arrive\n                    {\n                      // then schedule a request from this peer\n                      sync_item_requests_to_send[peer].push_back(item_to_potentially_request);\n                      sync_items_to_request.insert( item_to_potentially_request );\n                      if (sync_item_requests_to_send[peer].size() >= _maximum_blocks_per_peer_during_syncing)\n                        break;\n                    }\n                  }\n                }\n              }\n            }\n          } // end non-preemptable section\n\n          // make all the requests we scheduled in the loop above\n          for( auto sync_item_request : sync_item_requests_to_send )\n            request_sync_items_from_peer( sync_item_request.first, sync_item_request.second );\n          sync_item_requests_to_send.clear();\n        }\n        else\n          dlog(\"fetch_sync_items_loop is suspended pending backlog processing\");\n\n        if( !_sync_items_to_fetch_updated )\n        {\n          dlog( \"no sync items to fetch right now, going to sleep\" );\n          _retrigger_fetch_sync_items_loop_promise = fc::promise<void>::create(\"graphene::net::retrigger_fetch_sync_items_loop\");\n          _retrigger_fetch_sync_items_loop_promise->wait();\n          _retrigger_fetch_sync_items_loop_promise.reset();\n        }\n      } // while( !canceled )\n    }\n\n    void node_impl::trigger_fetch_sync_items_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog( \"Triggering fetch sync items loop now\" );\n      _sync_items_to_fetch_updated = true;\n      if( _retrigger_fetch_sync_items_loop_promise )\n        _retrigger_fetch_sync_items_loop_promise->set_value();\n    }\n\n    bool node_impl::is_item_in_any_peers_inventory(const item_id& item) const\n    {\n      for( const peer_connection_ptr& peer : _active_connections )\n      {\n        if (peer->inventory_peer_advertised_to_us.find(item) != peer->inventory_peer_advertised_to_us.end() )\n          return true;\n      }\n      return false;\n    }\n\n    void node_impl::fetch_items_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      while (!_fetch_item_loop_done.canceled())\n      {\n        _items_to_fetch_updated = false;\n        dlog(\"beginning an iteration of fetch items (${count} items to fetch)\",\n             (\"count\", _items_to_fetch.size()));\n\n        fc::time_point oldest_timestamp_to_fetch = fc::time_point::now() - fc::seconds(_recent_block_interval_in_seconds * GRAPHENE_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS);\n        fc::time_point next_peer_unblocked_time = fc::time_point::maximum();\n\n        // we need to construct a list of items to request from each peer first,\n        // then send the messages (in two steps, to avoid yielding while iterating)\n        // we want to evenly distribute our requests among our peers.\n        struct requested_item_count_index {};\n        struct peer_and_items_to_fetch\n        {\n          peer_connection_ptr peer;\n          std::vector<item_id> item_ids;\n          peer_and_items_to_fetch(const peer_connection_ptr& peer) : peer(peer) {}\n          bool operator<(const peer_and_items_to_fetch& rhs) const { return peer < rhs.peer; }\n          size_t number_of_items() const { return item_ids.size(); }\n        };\n        typedef boost::multi_index_container<peer_and_items_to_fetch,\n                                             boost::multi_index::indexed_by<boost::multi_index::ordered_unique<boost::multi_index::member<peer_and_items_to_fetch, peer_connection_ptr, &peer_and_items_to_fetch::peer> >,\n                                                                            boost::multi_index::ordered_non_unique<boost::multi_index::tag<requested_item_count_index>,\n                                                                                                                   boost::multi_index::const_mem_fun<peer_and_items_to_fetch, size_t, &peer_and_items_to_fetch::number_of_items> > > > fetch_messages_to_send_set;\n        fetch_messages_to_send_set items_by_peer;\n\n        // initialize the fetch_messages_to_send with an empty set of items for all idle peers\n        for (const peer_connection_ptr& peer : _active_connections)\n          if (peer->idle())\n            items_by_peer.insert(peer_and_items_to_fetch(peer));\n\n        // now loop over all items we want to fetch\n        for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();)\n        {\n          if (item_iter->timestamp < oldest_timestamp_to_fetch)\n          {\n            // this item has probably already fallen out of our peers' caches, we'll just ignore it.\n            // this can happen during flooding, and the _items_to_fetch could otherwise get clogged\n            // with a bunch of items that we'll never be able to request from any peer\n            wlog(\"Unable to fetch item ${item} before its likely expiration time, removing it from our list of items to fetch\", (\"item\", item_iter->item));\n            item_iter = _items_to_fetch.erase(item_iter);\n          }\n          else\n          {\n            // find a peer that has it, we'll use the one who has the least requests going to it to load balance\n            bool item_fetched = false;\n            for (auto peer_iter = items_by_peer.get<requested_item_count_index>().begin(); peer_iter != items_by_peer.get<requested_item_count_index>().end(); ++peer_iter)\n            {\n              const peer_connection_ptr& peer = peer_iter->peer;\n              // if they have the item and we haven't already decided to ask them for too many other items\n              if (peer_iter->item_ids.size() < GRAPHENE_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION &&\n                  peer->inventory_peer_advertised_to_us.find(item_iter->item) != peer->inventory_peer_advertised_to_us.end())\n              {\n                if (item_iter->item.item_type == graphene::net::trx_message_type && peer->is_transaction_fetching_inhibited())\n                  next_peer_unblocked_time = std::min(peer->transaction_fetching_inhibited_until, next_peer_unblocked_time);\n                else\n                {\n                  //dlog(\"requesting item ${hash} from peer ${endpoint}\",\n                  //     (\"hash\", iter->item.item_hash)(\"endpoint\", peer->get_remote_endpoint()));\n                  item_id item_id_to_fetch = item_iter->item;\n                  peer->items_requested_from_peer.insert(peer_connection::item_to_time_map_type::value_type(item_id_to_fetch, fc::time_point::now()));\n                  item_iter = _items_to_fetch.erase(item_iter);\n                  item_fetched = true;\n                  items_by_peer.get<requested_item_count_index>().modify(peer_iter, [&item_id_to_fetch](peer_and_items_to_fetch& peer_and_items) {\n                    peer_and_items.item_ids.push_back(item_id_to_fetch);\n                  });\n                  break;\n                }\n              }  \n            }\n            if (!item_fetched)\n              ++item_iter;\n          }\n        }\n\n        // we've figured out which peer will be providing each item, now send the messages.\n        for (const peer_and_items_to_fetch& peer_and_items : items_by_peer)\n        {\n          // the item lists are heterogenous and\n          // the fetch_items_message can only deal with one item type at a time.  \n          std::map<uint32_t, std::vector<item_hash_t> > items_to_fetch_by_type;\n          for (const item_id& item : peer_and_items.item_ids)\n            items_to_fetch_by_type[item.item_type].push_back(item.item_hash);\n          for (auto& items_by_type : items_to_fetch_by_type)\n          {\n            dlog(\"requesting ${count} items of type ${type} from peer ${endpoint}: ${hashes}\",\n                 (\"count\", items_by_type.second.size())(\"type\", (uint32_t)items_by_type.first)\n                 (\"endpoint\", peer_and_items.peer->get_remote_endpoint())\n                 (\"hashes\", items_by_type.second));\n            peer_and_items.peer->send_message(fetch_items_message(items_by_type.first,\n                                                                  items_by_type.second));\n          }\n        }\n        items_by_peer.clear();\n\n        if (!_items_to_fetch_updated)\n        {\n          _retrigger_fetch_item_loop_promise = fc::promise<void>::create(\"graphene::net::retrigger_fetch_item_loop\");\n          fc::microseconds time_until_retrigger = fc::microseconds::maximum();\n          if (next_peer_unblocked_time != fc::time_point::maximum())\n            time_until_retrigger = next_peer_unblocked_time - fc::time_point::now();\n          try\n          {\n            if (time_until_retrigger > fc::microseconds(0))\n              _retrigger_fetch_item_loop_promise->wait(time_until_retrigger);\n          }\n          catch (const fc::timeout_exception&)\n          {\n            dlog(\"Resuming fetch_items_loop due to timeout -- one of our peers should no longer be throttled\");\n          }\n          _retrigger_fetch_item_loop_promise.reset();\n        }\n      } // while (!canceled)\n    }\n\n    void node_impl::trigger_fetch_items_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      _items_to_fetch_updated = true;\n      if( _retrigger_fetch_item_loop_promise )\n        _retrigger_fetch_item_loop_promise->set_value();\n    }\n\n    void node_impl::advertise_inventory_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      while (!_advertise_inventory_loop_done.canceled())\n      {\n        dlog(\"beginning an iteration of advertise inventory\");\n        // swap inventory into local variable, clearing the node's copy\n        std::unordered_set<item_id> inventory_to_advertise;\n        inventory_to_advertise.swap(_new_inventory);\n\n        // process all inventory to advertise and construct the inventory messages we'll send\n        // first, then send them all in a batch (to avoid any fiber interruption points while\n        // we're computing the messages)\n        std::list<std::pair<peer_connection_ptr, item_ids_inventory_message> > inventory_messages_to_send;\n\n        for (const peer_connection_ptr& peer : _active_connections)\n        {\n          // only advertise to peers who are in sync with us\n          idump((peer->peer_needs_sync_items_from_us));\n          if( !peer->peer_needs_sync_items_from_us )\n          {\n            std::map<uint32_t, std::vector<item_hash_t> > items_to_advertise_by_type;\n            // don't send the peer anything we've already advertised to it\n            // or anything it has advertised to us\n            // group the items we need to send by type, because we'll need to send one inventory message per type\n            unsigned total_items_to_send_to_this_peer = 0;\n            idump((inventory_to_advertise));\n            for (const item_id& item_to_advertise : inventory_to_advertise)\n            {\n               auto adv_to_peer = peer->inventory_advertised_to_peer.find(item_to_advertise);\n               auto adv_to_us   = peer->inventory_peer_advertised_to_us.find(item_to_advertise);\n\n              if (adv_to_peer == peer->inventory_advertised_to_peer.end() &&\n                  adv_to_us == peer->inventory_peer_advertised_to_us.end())\n              {\n                items_to_advertise_by_type[item_to_advertise.item_type].push_back(item_to_advertise.item_hash);\n                peer->inventory_advertised_to_peer.insert(peer_connection::timestamped_item_id(item_to_advertise, fc::time_point::now()));\n                ++total_items_to_send_to_this_peer;\n                if (item_to_advertise.item_type == trx_message_type)\n                  testnetlog(\"advertising transaction ${id} to peer ${endpoint}\", (\"id\", item_to_advertise.item_hash)(\"endpoint\", peer->get_remote_endpoint()));\n                dlog(\"advertising item ${id} to peer ${endpoint}\", (\"id\", item_to_advertise.item_hash)(\"endpoint\", peer->get_remote_endpoint()));\n              }\n              else\n              {\n                 if (adv_to_peer != peer->inventory_advertised_to_peer.end() )\n                    idump( (*adv_to_peer) );\n                 if (adv_to_us != peer->inventory_peer_advertised_to_us.end() )\n                    idump( (*adv_to_us) );\n              }\n            }\n              dlog(\"advertising ${count} new item(s) of ${types} type(s) to peer ${endpoint}\",\n                   (\"count\", total_items_to_send_to_this_peer)\n                   (\"types\", items_to_advertise_by_type.size())\n                   (\"endpoint\", peer->get_remote_endpoint()));\n            for (auto items_group : items_to_advertise_by_type)\n              inventory_messages_to_send.push_back(std::make_pair(peer, item_ids_inventory_message(items_group.first, items_group.second)));\n          }\n          peer->clear_old_inventory();\n        }\n\n        for (auto iter = inventory_messages_to_send.begin(); iter != inventory_messages_to_send.end(); ++iter)\n          iter->first->send_message(iter->second);\n        inventory_messages_to_send.clear();\n\n        if (_new_inventory.empty())\n        {\n          _retrigger_advertise_inventory_loop_promise = fc::promise<void>::create(\"graphene::net::retrigger_advertise_inventory_loop\");\n          _retrigger_advertise_inventory_loop_promise->wait();\n          _retrigger_advertise_inventory_loop_promise.reset();\n        }\n      } // while(!canceled)\n    }\n\n    void node_impl::trigger_advertise_inventory_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      if( _retrigger_advertise_inventory_loop_promise )\n        _retrigger_advertise_inventory_loop_promise->set_value();\n    }\n\n    void node_impl::terminate_inactive_connections_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      std::list<peer_connection_ptr> peers_to_disconnect_gently;\n      std::list<peer_connection_ptr> peers_to_disconnect_forcibly;\n      std::list<peer_connection_ptr> peers_to_send_keep_alive;\n      std::list<peer_connection_ptr> peers_to_terminate;\n\n      _recent_block_interval_in_seconds = _delegate->get_current_block_interval_in_seconds();\n\n      // Disconnect peers that haven't sent us any data recently\n      // These numbers are just guesses and we need to think through how this works better.\n      // If we and our peers get disconnected from the rest of the network, we will not\n      // receive any blocks or transactions from the rest of the world, and that will\n      // probably make us disconnect from our peers even though we have working connections to\n      // them (but they won't have sent us anything since they aren't getting blocks either).\n      // This might not be so bad because it could make us initiate more connections and\n      // reconnect with the rest of the network, or it might just futher isolate us.\n      {\n        // As usual, the first step is to walk through all our peers and figure out which\n        // peers need action (disconneting, sending keepalives, etc), then we walk through \n        // those lists yielding at our leisure later.\n        ASSERT_TASK_NOT_PREEMPTED();\n\n        uint32_t handshaking_timeout = _peer_inactivity_timeout;\n        fc::time_point handshaking_disconnect_threshold = fc::time_point::now() - fc::seconds(handshaking_timeout);\n        for( const peer_connection_ptr handshaking_peer : _handshaking_connections )\n          if( handshaking_peer->connection_initiation_time < handshaking_disconnect_threshold &&\n              handshaking_peer->get_last_message_received_time() < handshaking_disconnect_threshold &&\n              handshaking_peer->get_last_message_sent_time() < handshaking_disconnect_threshold )\n          {\n            wlog( \"Forcibly disconnecting from handshaking peer ${peer} due to inactivity of at least ${timeout} seconds\",\n                  ( \"peer\", handshaking_peer->get_remote_endpoint() )(\"timeout\", handshaking_timeout ) );\n            wlog(\"Peer's negotiating status: ${status}, bytes sent: ${sent}, bytes received: ${received}\",\n                  (\"status\", handshaking_peer->negotiation_status)\n                  (\"sent\", handshaking_peer->get_total_bytes_sent())\n                  (\"received\", handshaking_peer->get_total_bytes_received()));\n            handshaking_peer->connection_closed_error = fc::exception(FC_LOG_MESSAGE(warn, \"Terminating handshaking connection due to inactivity of ${timeout} seconds.  Negotiating status: ${status}, bytes sent: ${sent}, bytes received: ${received}\",\n                                                                                      (\"peer\", handshaking_peer->get_remote_endpoint())\n                                                                                      (\"timeout\", handshaking_timeout)\n                                                                                      (\"status\", handshaking_peer->negotiation_status)\n                                                                                      (\"sent\", handshaking_peer->get_total_bytes_sent())\n                                                                                      (\"received\", handshaking_peer->get_total_bytes_received())));\n            peers_to_disconnect_forcibly.push_back( handshaking_peer );\n          }\n\n        // timeout for any active peers is two block intervals\n        uint32_t active_disconnect_timeout = 10 * _recent_block_interval_in_seconds;\n        uint32_t active_send_keepalive_timeout = active_disconnect_timeout / 2;\n\n        // set the ignored request time out to 6 second.  When we request a block\n        // or transaction from a peer, this timeout determines how long we wait for them\n        // to reply before we give up and ask another peer for the item.\n        // Ideally this should be significantly shorter than the block interval, because\n        // we'd like to realize the block isn't coming and fetch it from a different\n        // peer before the next block comes in.\n        // Increased to 6 from 1 in #1660 due to heavy load. May need to adjust further\n        // Note: #1660 is https://github.com/steemit/steem/issues/1660\n        fc::microseconds active_ignored_request_timeout = fc::seconds(6);\n\n        fc::time_point active_disconnect_threshold = fc::time_point::now() - fc::seconds(active_disconnect_timeout);\n        fc::time_point active_send_keepalive_threshold = fc::time_point::now() - fc::seconds(active_send_keepalive_timeout);\n        fc::time_point active_ignored_request_threshold = fc::time_point::now() - active_ignored_request_timeout;\n        for( const peer_connection_ptr& active_peer : _active_connections )\n        {\n          if( active_peer->connection_initiation_time < active_disconnect_threshold &&\n              active_peer->get_last_message_received_time() < active_disconnect_threshold )\n          {\n            wlog( \"Closing connection with peer ${peer} due to inactivity of at least ${timeout} seconds\",\n                  ( \"peer\", active_peer->get_remote_endpoint() )(\"timeout\", active_disconnect_timeout ) );\n            peers_to_disconnect_gently.push_back( active_peer );\n          }\n          else\n          {\n            bool disconnect_due_to_request_timeout = false;\n            if (!active_peer->sync_items_requested_from_peer.empty() &&\n                active_peer->last_sync_item_received_time < active_ignored_request_threshold)\n            {\n              wlog(\"Disconnecting peer ${peer} because they haven't made any progress on my remaining ${count} sync item requests\",\n                   (\"peer\", active_peer->get_remote_endpoint())(\"count\", active_peer->sync_items_requested_from_peer.size()));\n              disconnect_due_to_request_timeout = true;\n            }\n            if (!disconnect_due_to_request_timeout &&\n                active_peer->item_ids_requested_from_peer &&\n                active_peer->item_ids_requested_from_peer->get<1>() < active_ignored_request_threshold)\n              {\n                wlog(\"Disconnecting peer ${peer} because they didn't respond to my request for sync item ids after ${synopsis}\",\n                      (\"peer\", active_peer->get_remote_endpoint())\n                      (\"synopsis\", active_peer->item_ids_requested_from_peer->get<0>()));\n                disconnect_due_to_request_timeout = true;\n              }\n            if (!disconnect_due_to_request_timeout)\n              for (const peer_connection::item_to_time_map_type::value_type& item_and_time : active_peer->items_requested_from_peer)\n                if (item_and_time.second < active_ignored_request_threshold)\n                {\n                  wlog(\"Disconnecting peer ${peer} because they didn't respond to my request for item ${id}\",\n                        (\"peer\", active_peer->get_remote_endpoint())(\"id\", item_and_time.first.item_hash));\n                  disconnect_due_to_request_timeout = true;\n                  break;\n                }\n            if (disconnect_due_to_request_timeout)\n            {\n              // we should probably disconnect nicely and give them a reason, but right now the logic\n              // for rescheduling the requests only executes when the connection is fully closed,\n              // and we want to get those requests rescheduled as soon as possible\n              peers_to_disconnect_forcibly.push_back(active_peer);\n            }\n            else if (active_peer->connection_initiation_time < active_send_keepalive_threshold &&\n                     active_peer->get_last_message_received_time() < active_send_keepalive_threshold)\n            {\n              wlog( \"Sending a keepalive message to peer ${peer} who hasn't sent us any messages in the last ${timeout} seconds\",\n                    ( \"peer\", active_peer->get_remote_endpoint() )(\"timeout\", active_send_keepalive_timeout ) );\n              peers_to_send_keep_alive.push_back(active_peer);\n            }            \n            else if (active_peer->we_need_sync_items_from_peer && \n                     !active_peer->is_currently_handling_message() &&\n                     !active_peer->item_ids_requested_from_peer &&\n                     active_peer->ids_of_items_to_get.empty())\n            {\n              // This is a state we should never get into in the first place, but if we do, we should disconnect the peer\n              // to re-establish the connection.\n              fc_wlog(fc::logger::get(\"sync\"), \"Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.\",\n                      (\"peer\", active_peer->get_remote_endpoint()));\n              wlog(\"Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.\",\n                      (\"peer\", active_peer->get_remote_endpoint()));\n              peers_to_disconnect_forcibly.push_back(active_peer);\n            }\n          }\n        }\n\n        fc::time_point closing_disconnect_threshold = fc::time_point::now() - fc::seconds(GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT);\n        for( const peer_connection_ptr& closing_peer : _closing_connections )\n          if( closing_peer->connection_closed_time < closing_disconnect_threshold )\n          {\n            // we asked this peer to close their connectoin to us at least GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT\n            // seconds ago, but they haven't done it yet.  Terminate the connection now\n            wlog( \"Forcibly disconnecting peer ${peer} who failed to close their connection in a timely manner\",\n                  ( \"peer\", closing_peer->get_remote_endpoint() ) );\n            peers_to_disconnect_forcibly.push_back( closing_peer );\n          }\n\n        uint32_t failed_terminate_timeout_seconds = 120;\n        fc::time_point failed_terminate_threshold = fc::time_point::now() - fc::seconds(failed_terminate_timeout_seconds);\n        for (const peer_connection_ptr& peer : _terminating_connections )\n          if (peer->get_connection_terminated_time() != fc::time_point::min() &&\n              peer->get_connection_terminated_time() < failed_terminate_threshold)\n          {\n            wlog(\"Terminating connection with peer ${peer}, closing the connection didn't work\", (\"peer\", peer->get_remote_endpoint()));\n            peers_to_terminate.push_back(peer);\n          }\n\n        // That's the end of the sorting step; now all peers that require further processing are now in one of the\n        // lists peers_to_disconnect_gently,  peers_to_disconnect_forcibly, peers_to_send_keep_alive, or peers_to_terminate\n\n        // if we've decided to delete any peers, do it now; in its current implementation this doesn't yield,\n        // and once we start yielding, we may find that we've moved that peer to another list (closed or active)\n        // and that triggers assertions, maybe even errors\n        for (const peer_connection_ptr& peer : peers_to_terminate )\n        {\n          assert(_terminating_connections.find(peer) != _terminating_connections.end());\n          _terminating_connections.erase(peer);\n          schedule_peer_for_deletion(peer);\n        }\n        peers_to_terminate.clear();\n\n        // if we're going to abruptly disconnect anyone, do it here \n        // (it doesn't yield).  I don't think there would be any harm if this were \n        // moved to the yielding section\n        for( const peer_connection_ptr& peer : peers_to_disconnect_forcibly )\n        {\n          move_peer_to_terminating_list(peer);\n          peer->close_connection();\n        }\n        peers_to_disconnect_forcibly.clear();\n      } // end ASSERT_TASK_NOT_PREEMPTED()\n\n      // Now process the peers that we need to do yielding functions with (disconnect sends a message with the\n      // disconnect reason, so it may yield)\n      for( const peer_connection_ptr& peer : peers_to_disconnect_gently )\n      {\n        fc::exception detailed_error( FC_LOG_MESSAGE(warn, \"Disconnecting due to inactivity\",\n                                                      ( \"last_message_received_seconds_ago\", (peer->get_last_message_received_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() )\n                                                      ( \"last_message_sent_seconds_ago\", (peer->get_last_message_sent_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() )\n                                                      ( \"inactivity_timeout\", _active_connections.find(peer ) != _active_connections.end() ? _peer_inactivity_timeout * 10 : _peer_inactivity_timeout ) ) );\n        disconnect_from_peer( peer.get(), \"Disconnecting due to inactivity\", false, detailed_error );\n      }\n      peers_to_disconnect_gently.clear();\n\n      for( const peer_connection_ptr& peer : peers_to_send_keep_alive )\n        peer->send_message(current_time_request_message(),\n                           offsetof(current_time_request_message, request_sent_time));\n      peers_to_send_keep_alive.clear();\n\n      if (!_node_is_shutting_down && !_terminate_inactive_connections_loop_done.canceled())\n         _terminate_inactive_connections_loop_done = fc::schedule( [this](){ terminate_inactive_connections_loop(); },\n                                                                   fc::time_point::now() + fc::seconds(GRAPHENE_NET_PEER_HANDSHAKE_INACTIVITY_TIMEOUT / 2),\n                                                                   \"terminate_inactive_connections_loop\" );\n    }\n\n    void node_impl::fetch_updated_peer_lists_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n\n      std::list<peer_connection_ptr> original_active_peers(_active_connections.begin(), _active_connections.end());\n      for( const peer_connection_ptr& active_peer : original_active_peers )\n      {\n        try\n        {\n          active_peer->send_message(address_request_message());\n        }\n        catch ( const fc::canceled_exception& )\n        {\n          throw;\n        }\n        catch (const fc::exception& e)\n        {\n          dlog(\"Caught exception while sending address request message to peer ${peer} : ${e}\",\n               (\"peer\", active_peer->get_remote_endpoint())(\"e\", e));\n        }\n      }\n\n      // this has nothing to do with updating the peer list, but we need to prune this list \n      // at regular intervals, this is a fine place to do it.\n      fc::time_point_sec oldest_failed_ids_to_keep(fc::time_point::now() - fc::minutes(15));\n      auto oldest_failed_ids_to_keep_iter = _recently_failed_items.get<peer_connection::timestamp_index>().lower_bound(oldest_failed_ids_to_keep);\n      auto begin_iter = _recently_failed_items.get<peer_connection::timestamp_index>().begin();\n      _recently_failed_items.get<peer_connection::timestamp_index>().erase(begin_iter, oldest_failed_ids_to_keep_iter);\n\n      if (!_node_is_shutting_down && !_fetch_updated_peer_lists_loop_done.canceled() )\n         _fetch_updated_peer_lists_loop_done = fc::schedule( [this](){ fetch_updated_peer_lists_loop(); },\n                                                             fc::time_point::now() + fc::minutes(15),\n                                                             \"fetch_updated_peer_lists_loop\" );\n    }\n    void node_impl::update_bandwidth_data(uint32_t bytes_read_this_second, uint32_t bytes_written_this_second)\n    {\n      VERIFY_CORRECT_THREAD();\n      _average_network_read_speed_seconds.push_back(bytes_read_this_second);\n      _average_network_write_speed_seconds.push_back(bytes_written_this_second);\n      ++_average_network_usage_second_counter;\n      if (_average_network_usage_second_counter >= 60)\n      {\n        _average_network_usage_second_counter = 0;\n        ++_average_network_usage_minute_counter;\n        uint32_t average_read_this_minute = (uint32_t)boost::accumulate(_average_network_read_speed_seconds, uint64_t(0)) / (uint32_t)_average_network_read_speed_seconds.size();\n        _average_network_read_speed_minutes.push_back(average_read_this_minute);\n        uint32_t average_written_this_minute = (uint32_t)boost::accumulate(_average_network_write_speed_seconds, uint64_t(0)) / (uint32_t)_average_network_write_speed_seconds.size();\n        _average_network_write_speed_minutes.push_back(average_written_this_minute);\n        if (_average_network_usage_minute_counter >= 60)\n        {\n          _average_network_usage_minute_counter = 0;\n          uint32_t average_read_this_hour = (uint32_t)boost::accumulate(_average_network_read_speed_minutes, uint64_t(0)) / (uint32_t)_average_network_read_speed_minutes.size();\n          _average_network_read_speed_hours.push_back(average_read_this_hour);\n          uint32_t average_written_this_hour = (uint32_t)boost::accumulate(_average_network_write_speed_minutes, uint64_t(0)) / (uint32_t)_average_network_write_speed_minutes.size();\n          _average_network_write_speed_hours.push_back(average_written_this_hour);\n        }\n      }\n    }\n    void node_impl::bandwidth_monitor_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::time_point_sec current_time = fc::time_point::now();\n\n      if (_bandwidth_monitor_last_update_time == fc::time_point_sec::min())\n        _bandwidth_monitor_last_update_time = current_time;\n\n      uint32_t seconds_since_last_update = current_time.sec_since_epoch() - _bandwidth_monitor_last_update_time.sec_since_epoch();\n      seconds_since_last_update = std::max(UINT32_C(1), seconds_since_last_update);\n      uint32_t bytes_read_this_second = _rate_limiter.get_actual_download_rate();\n      uint32_t bytes_written_this_second = _rate_limiter.get_actual_upload_rate();\n      for (uint32_t i = 0; i < seconds_since_last_update - 1; ++i)\n        update_bandwidth_data(0, 0);\n      update_bandwidth_data(bytes_read_this_second, bytes_written_this_second);\n      _bandwidth_monitor_last_update_time = current_time;\n\n      if (!_node_is_shutting_down && !_bandwidth_monitor_loop_done.canceled())\n        _bandwidth_monitor_loop_done = fc::schedule( [=](){ bandwidth_monitor_loop(); },\n                                                     fc::time_point::now() + fc::seconds(1),\n                                                     \"bandwidth_monitor_loop\" );\n    }\n\n    void node_impl::dump_node_status_task()\n    {\n      VERIFY_CORRECT_THREAD();\n      dump_node_status();\n      if (!_node_is_shutting_down && !_dump_node_status_task_done.canceled())\n        _dump_node_status_task_done = fc::schedule([=](){ dump_node_status_task(); },\n                                                   fc::time_point::now() + fc::minutes(1),\n                                                   \"dump_node_status_task\");\n    }\n\n    void node_impl::delayed_peer_deletion_task()\n    {\n      VERIFY_CORRECT_THREAD();\n#ifdef USE_PEERS_TO_DELETE_MUTEX\n      fc::scoped_lock<fc::mutex> lock(_peers_to_delete_mutex);\n      dlog(\"in delayed_peer_deletion_task with ${count} in queue\", (\"count\", _peers_to_delete.size()));\n      _peers_to_delete.clear();\n      dlog(\"_peers_to_delete cleared\");\n#else\n      while (!_peers_to_delete.empty())\n      {\n        std::list<peer_connection_ptr> peers_to_delete_copy;\n        dlog(\"beginning an iteration of delayed_peer_deletion_task with ${count} in queue\", (\"count\", _peers_to_delete.size()));\n        peers_to_delete_copy.swap(_peers_to_delete);\n      }\n      dlog(\"leaving delayed_peer_deletion_task\");\n#endif\n    }\n\n    void node_impl::schedule_peer_for_deletion(const peer_connection_ptr& peer_to_delete)\n    {\n      VERIFY_CORRECT_THREAD();\n\n      assert(_handshaking_connections.find(peer_to_delete) == _handshaking_connections.end());\n      assert(_active_connections.find(peer_to_delete) == _active_connections.end());\n      assert(_closing_connections.find(peer_to_delete) == _closing_connections.end());\n      assert(_terminating_connections.find(peer_to_delete) == _terminating_connections.end());\n\n#ifdef USE_PEERS_TO_DELETE_MUTEX\n      dlog(\"scheduling peer for deletion: ${peer} (may block on a mutex here)\", (\"peer\", peer_to_delete->get_remote_endpoint()));\n\n      unsigned number_of_peers_to_delete;\n      {\n        fc::scoped_lock<fc::mutex> lock(_peers_to_delete_mutex);\n        _peers_to_delete.emplace_back(peer_to_delete);\n        number_of_peers_to_delete = _peers_to_delete.size();\n      }\n      dlog(\"peer scheduled for deletion: ${peer}\", (\"peer\", peer_to_delete->get_remote_endpoint()));\n\n      if (!_node_is_shutting_down &&\n          (!_delayed_peer_deletion_task_done.valid() || _delayed_peer_deletion_task_done.ready()))\n      {\n        dlog(\"asyncing delayed_peer_deletion_task to delete ${size} peers\", (\"size\", number_of_peers_to_delete));\n        _delayed_peer_deletion_task_done = fc::async([this](){ delayed_peer_deletion_task(); }, \"delayed_peer_deletion_task\" );\n    }\n      else\n        dlog(\"delayed_peer_deletion_task is already scheduled (current size of _peers_to_delete is ${size})\", (\"size\", number_of_peers_to_delete));\n#else\n      dlog(\"scheduling peer for deletion: ${peer} (this will not block)\", (\"peer\", peer_to_delete->get_remote_endpoint()));\n      _peers_to_delete.push_back(peer_to_delete);\n      if (!_node_is_shutting_down &&\n          (!_delayed_peer_deletion_task_done.valid() || _delayed_peer_deletion_task_done.ready()))\n      {\n        dlog(\"asyncing delayed_peer_deletion_task to delete ${size} peers\", (\"size\", _peers_to_delete.size()));\n        _delayed_peer_deletion_task_done = fc::async([this](){ delayed_peer_deletion_task(); }, \"delayed_peer_deletion_task\" );\n      }\n      else\n        dlog(\"delayed_peer_deletion_task is already scheduled (current size of _peers_to_delete is ${size})\", (\"size\", _peers_to_delete.size()));\n\n#endif\n    }\n\n    bool node_impl::is_accepting_new_connections()\n    {\n      VERIFY_CORRECT_THREAD();\n      return !_p2p_network_connect_loop_done.canceled() && get_number_of_connections() <= _maximum_number_of_connections;\n    }\n\n    bool node_impl::is_wanting_new_connections()\n    {\n      VERIFY_CORRECT_THREAD();\n      return !_p2p_network_connect_loop_done.canceled() && get_number_of_connections() < _desired_number_of_connections;\n    }\n\n    uint32_t node_impl::get_number_of_connections()\n    {\n      VERIFY_CORRECT_THREAD();\n      return (uint32_t)(_handshaking_connections.size() + _active_connections.size());\n    }\n\n    peer_connection_ptr node_impl::get_peer_by_node_id(const node_id_t& node_id)\n    {\n      for (const peer_connection_ptr& active_peer : _active_connections)\n        if (node_id == active_peer->node_id)\n          return active_peer;\n      for (const peer_connection_ptr& handshaking_peer : _handshaking_connections)\n        if (node_id == handshaking_peer->node_id)\n          return handshaking_peer;\n      return peer_connection_ptr();\n    }\n\n    bool node_impl::is_already_connected_to_id(const node_id_t& node_id)\n    {\n      VERIFY_CORRECT_THREAD();\n      if (node_id == _node_id)\n      {\n        dlog(\"is_already_connected_to_id returning true because the peer is us\");\n        return true;\n      }\n      for (const peer_connection_ptr active_peer : _active_connections)\n        if (node_id == active_peer->node_id)\n        {\n          dlog(\"is_already_connected_to_id returning true because the peer is already in our active list\");\n          return true;\n        }\n      for (const peer_connection_ptr handshaking_peer : _handshaking_connections)\n        if (node_id == handshaking_peer->node_id)\n        {\n          dlog(\"is_already_connected_to_id returning true because the peer is already in our handshaking list\");\n          return true;\n        }\n      return false;\n    }\n\n    // merge addresses received from a peer into our database\n    bool node_impl::merge_address_info_with_potential_peer_database(const std::vector<address_info> addresses)\n    {\n      VERIFY_CORRECT_THREAD();\n      bool new_information_received = false;\n      for (const address_info& address : addresses)\n      {\n        if (address.firewalled == graphene::net::firewalled_state::not_firewalled)\n        {\n          potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(address.remote_endpoint);\n          if (address.last_seen_time > updated_peer_record.last_seen_time)\n            new_information_received = true;\n          updated_peer_record.last_seen_time = std::max(address.last_seen_time, updated_peer_record.last_seen_time);\n          _potential_peer_db.update_entry(updated_peer_record);\n        }\n      }\n      return new_information_received;\n    }\n\n    void node_impl::display_current_connections()\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog(\"Currently have ${current} of [${desired}/${max}] connections\",\n           (\"current\", get_number_of_connections())\n           (\"desired\", _desired_number_of_connections)\n           (\"max\", _maximum_number_of_connections));\n      dlog(\"   my id is ${id}\", (\"id\", _node_id));\n\n      for (const peer_connection_ptr& active_connection : _active_connections)\n      {\n        dlog(\"        active: ${endpoint} with ${id}   [${direction}]\",\n             (\"endpoint\", active_connection->get_remote_endpoint())\n             (\"id\", active_connection->node_id)\n             (\"direction\", active_connection->direction));\n      }\n      for (const peer_connection_ptr& handshaking_connection : _handshaking_connections)\n      {\n        dlog(\"   handshaking: ${endpoint} with ${id}  [${direction}]\",\n             (\"endpoint\", handshaking_connection->get_remote_endpoint())\n             (\"id\", handshaking_connection->node_id)\n             (\"direction\", handshaking_connection->direction));\n      }\n    }\n\n    void node_impl::on_message( peer_connection* originating_peer, const message& received_message )\n    {\n      VERIFY_CORRECT_THREAD();\n      message_hash_type message_hash = received_message.id();\n      dlog(\"handling message ${type} ${hash} size ${size} from peer ${endpoint}\",\n           (\"type\", graphene::net::core_message_type_enum(received_message.msg_type.value()))(\"hash\", message_hash)\n           (\"size\", received_message.size)\n           (\"endpoint\", originating_peer->get_remote_endpoint()));\n      switch ( received_message.msg_type.value() )\n      {\n      case core_message_type_enum::hello_message_type:\n        on_hello_message(originating_peer, received_message.as<hello_message>());\n        break;\n      case core_message_type_enum::connection_accepted_message_type:\n        on_connection_accepted_message(originating_peer, received_message.as<connection_accepted_message>());\n        break;\n      case core_message_type_enum::connection_rejected_message_type:\n        on_connection_rejected_message(originating_peer, received_message.as<connection_rejected_message>());\n        break;\n      case core_message_type_enum::address_request_message_type:\n        on_address_request_message(originating_peer, received_message.as<address_request_message>());\n        break;\n      case core_message_type_enum::address_message_type:\n        on_address_message(originating_peer, received_message.as<address_message>());\n        break;\n      case core_message_type_enum::fetch_blockchain_item_ids_message_type:\n        on_fetch_blockchain_item_ids_message(originating_peer, received_message.as<fetch_blockchain_item_ids_message>());\n        break;\n      case core_message_type_enum::blockchain_item_ids_inventory_message_type:\n        on_blockchain_item_ids_inventory_message(originating_peer, received_message.as<blockchain_item_ids_inventory_message>());\n        break;\n      case core_message_type_enum::fetch_items_message_type:\n        on_fetch_items_message(originating_peer, received_message.as<fetch_items_message>());\n        break;\n      case core_message_type_enum::item_not_available_message_type:\n        on_item_not_available_message(originating_peer, received_message.as<item_not_available_message>());\n        break;\n      case core_message_type_enum::item_ids_inventory_message_type:\n        on_item_ids_inventory_message(originating_peer, received_message.as<item_ids_inventory_message>());\n        break;\n      case core_message_type_enum::closing_connection_message_type:\n        on_closing_connection_message(originating_peer, received_message.as<closing_connection_message>());\n        break;\n      case core_message_type_enum::block_message_type:\n        process_block_message(originating_peer, received_message, message_hash);\n        break;\n      case core_message_type_enum::current_time_request_message_type:\n        on_current_time_request_message(originating_peer, received_message.as<current_time_request_message>());\n        break;\n      case core_message_type_enum::current_time_reply_message_type:\n        on_current_time_reply_message(originating_peer, received_message.as<current_time_reply_message>());\n        break;\n      case core_message_type_enum::check_firewall_message_type:\n        on_check_firewall_message(originating_peer, received_message.as<check_firewall_message>());\n        break;\n      case core_message_type_enum::check_firewall_reply_message_type:\n        on_check_firewall_reply_message(originating_peer, received_message.as<check_firewall_reply_message>());\n        break;\n      case core_message_type_enum::get_current_connections_request_message_type:\n        on_get_current_connections_request_message(originating_peer, received_message.as<get_current_connections_request_message>());\n        break;\n      case core_message_type_enum::get_current_connections_reply_message_type:\n        on_get_current_connections_reply_message(originating_peer, received_message.as<get_current_connections_reply_message>());\n        break;\n\n      default:\n        // ignore any message in between core_message_type_first and _last that we don't handle above\n        // to allow us to add messages in the future\n        if (received_message.msg_type.value() < core_message_type_enum::core_message_type_first ||\n            received_message.msg_type.value() > core_message_type_enum::core_message_type_last)\n          process_ordinary_message(originating_peer, received_message, message_hash);\n        break;\n      }\n    }\n\n\n    fc::variant_object node_impl::generate_hello_user_data()\n    {\n      VERIFY_CORRECT_THREAD();\n      // for the time being, shoehorn a bunch of properties into the user_data variant object,\n      // which lets us add and remove fields without changing the protocol.  Once we\n      // settle on what we really want in there, we'll likely promote them to first\n      // class fields in the hello message\n      fc::mutable_variant_object user_data;\n      user_data[\"fc_git_revision_sha\"] = fc::git_revision_sha;\n      user_data[\"fc_git_revision_unix_timestamp\"] = fc::git_revision_unix_timestamp;\n#if defined( __APPLE__ )\n      user_data[\"platform\"] = \"osx\";\n#elif defined( __OpenBSD__ )\n      user_data[\"platform\"] = \"obsd\";\n#elif defined( __linux__ )\n      user_data[\"platform\"] = \"linux\";\n#elif defined( _MSC_VER )\n      user_data[\"platform\"] = \"win32\";\n#else\n      user_data[\"platform\"] = \"other\";\n#endif\n      user_data[\"bitness\"] = sizeof(void*) * 8;\n\n      user_data[\"node_id\"] = fc::variant( _node_id, 1 );\n\n      item_hash_t head_block_id = _delegate->get_head_block_id();\n      user_data[\"last_known_block_hash\"] = fc::variant( head_block_id, 1 );\n      user_data[\"last_known_block_number\"] = _delegate->get_block_number(head_block_id);\n      user_data[\"last_known_block_time\"] = _delegate->get_block_time(head_block_id);\n\n      if (!_hard_fork_block_numbers.empty())\n        user_data[\"last_known_fork_block_number\"] = _hard_fork_block_numbers.back();\n\n      return user_data;\n    }\n    void node_impl::parse_hello_user_data_for_peer(peer_connection* originating_peer, const fc::variant_object& user_data)\n    {\n      VERIFY_CORRECT_THREAD();\n      // try to parse data out of the user_agent string\n      if (user_data.contains(\"graphene_git_revision_sha\"))\n        originating_peer->graphene_git_revision_sha = user_data[\"graphene_git_revision_sha\"].as_string();\n      if (user_data.contains(\"graphene_git_revision_unix_timestamp\"))\n        originating_peer->graphene_git_revision_unix_timestamp = fc::time_point_sec(user_data[\"graphene_git_revision_unix_timestamp\"].as<uint32_t>(1));\n      if (user_data.contains(\"fc_git_revision_sha\"))\n        originating_peer->fc_git_revision_sha = user_data[\"fc_git_revision_sha\"].as_string();\n      if (user_data.contains(\"fc_git_revision_unix_timestamp\"))\n        originating_peer->fc_git_revision_unix_timestamp = fc::time_point_sec(user_data[\"fc_git_revision_unix_timestamp\"].as<uint32_t>(1));\n      if (user_data.contains(\"platform\"))\n        originating_peer->platform = user_data[\"platform\"].as_string();\n      if (user_data.contains(\"bitness\"))\n        originating_peer->bitness = user_data[\"bitness\"].as<uint32_t>(1);\n      if (user_data.contains(\"node_id\"))\n        originating_peer->node_id = user_data[\"node_id\"].as<node_id_t>(1);\n      if (user_data.contains(\"last_known_fork_block_number\"))\n        originating_peer->last_known_fork_block_number = user_data[\"last_known_fork_block_number\"].as<uint32_t>(1);\n    }\n\n    void node_impl::on_hello_message( peer_connection* originating_peer, const hello_message& hello_message_received )\n    {\n      VERIFY_CORRECT_THREAD();\n      // this already_connected check must come before we fill in peer data below\n      node_id_t peer_node_id = hello_message_received.node_public_key;\n      try\n      {\n        peer_node_id = hello_message_received.user_data[\"node_id\"].as<node_id_t>(1);\n      }\n      catch (const fc::exception&)\n      {\n        // either it's not there or it's not a valid session id.  either way, ignore.\n      }\n      bool already_connected_to_this_peer = is_already_connected_to_id(peer_node_id);\n\n      // validate the node id\n      fc::sha256::encoder shared_secret_encoder;\n      fc::sha512 shared_secret = originating_peer->get_shared_secret();\n      shared_secret_encoder.write(shared_secret.data(), sizeof(shared_secret));\n      fc::ecc::public_key expected_node_public_key(hello_message_received.signed_shared_secret, shared_secret_encoder.result(), false);\n\n      // store off the data provided in the hello message\n      originating_peer->user_agent = hello_message_received.user_agent;\n      originating_peer->node_public_key = hello_message_received.node_public_key;\n      originating_peer->node_id = hello_message_received.node_public_key; // will probably be overwritten in parse_hello_user_data_for_peer()\n      originating_peer->core_protocol_version = hello_message_received.core_protocol_version;\n      originating_peer->inbound_address = hello_message_received.inbound_address;\n      originating_peer->inbound_port = hello_message_received.inbound_port;\n      originating_peer->outbound_port = hello_message_received.outbound_port;\n\n      parse_hello_user_data_for_peer(originating_peer, hello_message_received.user_data);\n\n      // if they didn't provide a last known fork, try to guess it\n      if (originating_peer->last_known_fork_block_number == 0 &&\n          originating_peer->graphene_git_revision_unix_timestamp)\n      {\n        uint32_t unix_timestamp = originating_peer->graphene_git_revision_unix_timestamp->sec_since_epoch();\n        originating_peer->last_known_fork_block_number = _delegate->estimate_last_known_fork_from_git_revision_timestamp(unix_timestamp);\n      }\n\n      // now decide what to do with it\n      if (originating_peer->their_state == peer_connection::their_connection_state::just_connected)\n      {\n        if (hello_message_received.node_public_key != expected_node_public_key.serialize())\n        {\n          wlog(\"Invalid signature in hello message from peer ${peer}\", (\"peer\", originating_peer->get_remote_endpoint()));\n          std::string rejection_message(\"Invalid signature in hello message\");\n          connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version,\n                                                          originating_peer->get_socket().remote_endpoint(),\n                                                          rejection_reason_code::invalid_hello_message,\n                                                          rejection_message);\n\n          originating_peer->their_state = peer_connection::their_connection_state::connection_rejected;\n          originating_peer->send_message( message(connection_rejected ) );\n          // for this type of message, we're immediately disconnecting this peer\n          disconnect_from_peer( originating_peer, \"Invalid signature in hello message\" );\n          return;\n        }\n        if (hello_message_received.chain_id != _chain_id)\n        {\n          wlog(\"Received hello message from peer on a different chain: ${message}\", (\"message\", hello_message_received));\n          std::ostringstream rejection_message;\n          rejection_message << \"You're on a different chain than I am.  I'm on \" << _chain_id.str() <<\n                              \" and you're on \" << hello_message_received.chain_id.str();\n          connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version,\n                                                          originating_peer->get_socket().remote_endpoint(),\n                                                          rejection_reason_code::different_chain,\n                                                          rejection_message.str());\n\n          originating_peer->their_state = peer_connection::their_connection_state::connection_rejected;\n          originating_peer->send_message(message(connection_rejected));\n          // for this type of message, we're immediately disconnecting this peer, instead of trying to\n          // allowing her to ask us for peers (any of our peers will be on the same chain as us, so there's no\n          // benefit of sharing them)\n          disconnect_from_peer(originating_peer, \"You are on a different chain from me\");\n          return;\n        }\n        if (originating_peer->last_known_fork_block_number != 0)\n        {\n          uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(originating_peer->last_known_fork_block_number);\n          if (next_fork_block_number != 0)\n          {\n            // we know about a fork they don't.  See if we've already passed that block.  If we have, don't let them\n            // connect because we won't be able to give them anything useful\n            uint32_t head_block_num = _delegate->get_block_number(_delegate->get_head_block_id());\n            if (next_fork_block_number < head_block_num)\n            {\n#ifdef ENABLE_DEBUG_ULOGS\n              ulog(\"Rejecting connection from peer because their version is too old.  Their version date: ${date}\", (\"date\", originating_peer->graphene_git_revision_unix_timestamp));\n#endif\n              wlog(\"Received hello message from peer running a version of that can only understand blocks up to #${their_hard_fork}, but I'm at head block number #${my_block_number}\",\n                   (\"their_hard_fork\", next_fork_block_number)(\"my_block_number\", head_block_num));\n              std::ostringstream rejection_message;\n              rejection_message << \"Your client is outdated -- you can only understand blocks up to #\" << next_fork_block_number << \", but I'm already on block #\" << head_block_num;\n              connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version,\n                                                              originating_peer->get_socket().remote_endpoint(),\n                                                              rejection_reason_code::unspecified,\n                                                              rejection_message.str() );\n\n              originating_peer->their_state = peer_connection::their_connection_state::connection_rejected;\n              originating_peer->send_message(message(connection_rejected));\n              // for this type of message, we're immediately disconnecting this peer, instead of trying to\n              // allowing her to ask us for peers (any of our peers will be on the same chain as us, so there's no\n              // benefit of sharing them)\n              disconnect_from_peer(originating_peer, \"Your client is too old, please upgrade\");\n              return;\n            }\n          }\n        }\n        if (already_connected_to_this_peer)\n        {\n\n          connection_rejected_message connection_rejected;\n          if (_node_id == originating_peer->node_id)\n            connection_rejected = connection_rejected_message(_user_agent_string, core_protocol_version,\n                                                              originating_peer->get_socket().remote_endpoint(),\n                                                              rejection_reason_code::connected_to_self,\n                                                              \"I'm connecting to myself\");\n          else\n            connection_rejected = connection_rejected_message(_user_agent_string, core_protocol_version,\n                                                              originating_peer->get_socket().remote_endpoint(),\n                                                              rejection_reason_code::already_connected,\n                                                              \"I'm already connected to you\");\n          originating_peer->their_state = peer_connection::their_connection_state::connection_rejected;\n          originating_peer->send_message(message(connection_rejected));\n          dlog(\"Received a hello_message from peer ${peer} that I'm already connected to (with id ${id}), rejection\",\n               (\"peer\", originating_peer->get_remote_endpoint())\n               (\"id\", originating_peer->node_id));\n        }\n#ifdef ENABLE_P2P_DEBUGGING_API\n        else if(!_allowed_peers.empty() &&\n                _allowed_peers.find(originating_peer->node_id) == _allowed_peers.end())\n        {\n          connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version,\n                                                          originating_peer->get_socket().remote_endpoint(),\n                                                          rejection_reason_code::blocked,\n                                                          \"you are not in my allowed_peers list\");\n          originating_peer->their_state = peer_connection::their_connection_state::connection_rejected;\n          originating_peer->send_message( message(connection_rejected ) );\n          dlog( \"Received a hello_message from peer ${peer} who isn't in my allowed_peers list, rejection\", (\"peer\", originating_peer->get_remote_endpoint() ) );\n        }\n#endif // ENABLE_P2P_DEBUGGING_API\n        else\n        {\n          // whether we're planning on accepting them as a peer or not, they seem to be a valid node,\n          // so add them to our database if they're not firewalled\n\n          // in the hello message, the peer sent us the IP address and port it thought it was connecting from.\n          // If they match the IP and port we see, we assume that they're actually on the internet and they're not\n          // firewalled.\n          fc::ip::endpoint peers_actual_outbound_endpoint = originating_peer->get_socket().remote_endpoint();\n          if( peers_actual_outbound_endpoint.get_address() == originating_peer->inbound_address &&\n              peers_actual_outbound_endpoint.port() == originating_peer->outbound_port )\n          {\n            if( originating_peer->inbound_port == 0 )\n            {\n              dlog( \"peer does not appear to be firewalled, but they did not give an inbound port so I'm treating them as if they are.\" );\n              originating_peer->is_firewalled = firewalled_state::firewalled;\n            }\n            else\n            {\n              // peer is not firewalled, add it to our database\n              fc::ip::endpoint peers_inbound_endpoint(originating_peer->inbound_address, originating_peer->inbound_port);\n              potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(peers_inbound_endpoint);\n              _potential_peer_db.update_entry(updated_peer_record);\n              originating_peer->is_firewalled = firewalled_state::not_firewalled;\n            }\n          }\n          else\n          {\n            dlog(\"peer is firewalled: they think their outbound endpoint is ${reported_endpoint}, but I see it as ${actual_endpoint}\",\n                 (\"reported_endpoint\", fc::ip::endpoint(originating_peer->inbound_address, originating_peer->outbound_port))\n                 (\"actual_endpoint\", peers_actual_outbound_endpoint));\n            originating_peer->is_firewalled = firewalled_state::firewalled;\n          }\n\n          if (!is_accepting_new_connections())\n          {\n            connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version,\n                                                            originating_peer->get_socket().remote_endpoint(),\n                                                            rejection_reason_code::not_accepting_connections,\n                                                            \"not accepting any more incoming connections\");\n            originating_peer->their_state = peer_connection::their_connection_state::connection_rejected;\n            originating_peer->send_message(message(connection_rejected));\n            dlog(\"Received a hello_message from peer ${peer}, but I'm not accepting any more connections, rejection\",\n                 (\"peer\", originating_peer->get_remote_endpoint()));\n          }\n          else\n          {\n            originating_peer->their_state = peer_connection::their_connection_state::connection_accepted;\n            originating_peer->send_message(message(connection_accepted_message()));\n            dlog(\"Received a hello_message from peer ${peer}, sending reply to accept connection\",\n                 (\"peer\", originating_peer->get_remote_endpoint()));\n          }\n        }\n      }\n      else\n      {\n        // we can wind up here if we've connected to ourself, and the source and\n        // destination endpoints are the same, causing messages we send out\n        // to arrive back on the initiating socket instead of the receiving\n        // socket.  If we did a complete job of enumerating local addresses,\n        // we could avoid directly connecting to ourselves, or at least detect\n        // immediately when we did it and disconnect.\n\n        // The only way I know of that we'd get an unexpected hello that we\n        //  can't really guard against is if we do a simulatenous open, we\n        // probably need to think through that case.  We're not attempting that\n        // yet, though, so it's ok to just disconnect here.\n        wlog(\"unexpected hello_message from peer, disconnecting\");\n        disconnect_from_peer(originating_peer, \"Received a unexpected hello_message\");\n      }\n    }\n\n    void node_impl::on_connection_accepted_message(peer_connection* originating_peer, const connection_accepted_message& connection_accepted_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog(\"Received a connection_accepted in response to my \\\"hello\\\" from ${peer}\", (\"peer\", originating_peer->get_remote_endpoint()));\n      originating_peer->negotiation_status = peer_connection::connection_negotiation_status::peer_connection_accepted;\n      originating_peer->our_state = peer_connection::our_connection_state::connection_accepted;\n      originating_peer->send_message(address_request_message());\n      fc::time_point now = fc::time_point::now();\n      if (_is_firewalled == firewalled_state::unknown &&\n          _last_firewall_check_message_sent < now - fc::minutes(5) &&\n          originating_peer->core_protocol_version >= 106)\n      {\n        wlog(\"I don't know if I'm firewalled.  Sending a firewall check message to peer ${peer}\",\n             (\"peer\", originating_peer->get_remote_endpoint()));\n        originating_peer->firewall_check_state = new firewall_check_state_data;\n\n        originating_peer->send_message(check_firewall_message());\n        _last_firewall_check_message_sent = now;\n      }\n    }\n\n    void node_impl::on_connection_rejected_message(peer_connection* originating_peer, const connection_rejected_message& connection_rejected_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      if (originating_peer->our_state == peer_connection::our_connection_state::just_connected)\n      {\n        ilog(\"Received a rejection from ${peer} in response to my \\\"hello\\\", reason: \\\"${reason}\\\"\",\n             (\"peer\", originating_peer->get_remote_endpoint())\n             (\"reason\", connection_rejected_message_received.reason_string));\n\n        if (connection_rejected_message_received.reason_code == rejection_reason_code::connected_to_self)\n        {\n          _potential_peer_db.erase(originating_peer->get_socket().remote_endpoint());\n          move_peer_to_closing_list(originating_peer->shared_from_this());\n          originating_peer->close_connection();\n        }\n        else\n        {\n          // update our database to record that we were rejected so we won't try to connect again for a while\n          // this only happens on connections we originate, so we should already know that peer is not firewalled\n          fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(originating_peer->get_socket().remote_endpoint());\n          if (updated_peer_record)\n          {\n            updated_peer_record->last_connection_disposition = last_connection_rejected;\n            updated_peer_record->last_connection_attempt_time = fc::time_point::now();\n            _potential_peer_db.update_entry(*updated_peer_record);\n          }\n        }\n\n        originating_peer->negotiation_status = peer_connection::connection_negotiation_status::peer_connection_rejected;\n        originating_peer->our_state = peer_connection::our_connection_state::connection_rejected;\n        originating_peer->send_message(address_request_message());\n      }\n      else\n        FC_THROW( \"unexpected connection_rejected_message from peer\" );\n    }\n\n    void node_impl::on_address_request_message(peer_connection* originating_peer, const address_request_message& address_request_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog(\"Received an address request message\");\n\n      address_message reply;\n      if (!_peer_advertising_disabled)\n      {\n        reply.addresses.reserve(_active_connections.size());\n        for (const peer_connection_ptr& active_peer : _active_connections)\n        {\n          fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*active_peer->get_remote_endpoint());\n          if (updated_peer_record)\n          {\n            updated_peer_record->last_seen_time = fc::time_point::now();\n            _potential_peer_db.update_entry(*updated_peer_record);\n          }\n\n          reply.addresses.emplace_back(address_info(*active_peer->get_remote_endpoint(),\n                                                    fc::time_point::now(),\n                                                    active_peer->round_trip_delay,\n                                                    active_peer->node_id,\n                                                    active_peer->direction,\n                                                    active_peer->is_firewalled));\n        }\n      }\n      originating_peer->send_message(reply);\n    }\n\n    void node_impl::on_address_message(peer_connection* originating_peer, const address_message& address_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog(\"Received an address message containing ${size} addresses\", (\"size\", address_message_received.addresses.size()));\n      for (const address_info& address : address_message_received.addresses)\n      {\n        dlog(\"    ${endpoint} last seen ${time}\", (\"endpoint\", address.remote_endpoint)(\"time\", address.last_seen_time));\n      }\n      std::vector<graphene::net::address_info> updated_addresses = address_message_received.addresses;\n      for (address_info& address : updated_addresses)\n        address.last_seen_time = fc::time_point_sec(fc::time_point::now());\n      bool new_information_received = merge_address_info_with_potential_peer_database(updated_addresses);\n      if (new_information_received)\n        trigger_p2p_network_connect_loop();\n\n      if (_handshaking_connections.find(originating_peer->shared_from_this()) != _handshaking_connections.end())\n      {\n        // if we were handshaking, we need to continue with the next step in handshaking (which is either\n        // ending handshaking and starting synchronization or disconnecting)\n        if( originating_peer->our_state == peer_connection::our_connection_state::connection_rejected)\n          disconnect_from_peer(originating_peer, \"You rejected my connection request (hello message) so I'm disconnecting\");\n        else if (originating_peer->their_state == peer_connection::their_connection_state::connection_rejected)\n          disconnect_from_peer(originating_peer, \"I rejected your connection request (hello message) so I'm disconnecting\");\n        else\n        {\n          fc::optional<fc::ip::endpoint> inbound_endpoint = originating_peer->get_endpoint_for_connecting();\n          if (inbound_endpoint)\n          {\n            // mark the connection as successful in the database\n            fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint);\n            if (updated_peer_record)\n            {\n              updated_peer_record->last_connection_disposition = last_connection_succeeded;\n              _potential_peer_db.update_entry(*updated_peer_record);\n            }\n          }\n\n          originating_peer->negotiation_status = peer_connection::connection_negotiation_status::negotiation_complete;\n          move_peer_to_active_list(originating_peer->shared_from_this());\n          new_peer_just_added(originating_peer->shared_from_this());\n        }\n      }\n      // else if this was an active connection, then this was just a reply to our periodic address requests.\n      // we've processed it, there's nothing else to do\n    }\n\n    void node_impl::on_fetch_blockchain_item_ids_message(peer_connection* originating_peer,\n                                                         const fetch_blockchain_item_ids_message& fetch_blockchain_item_ids_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      item_id peers_last_item_seen = item_id(fetch_blockchain_item_ids_message_received.item_type, item_hash_t());\n      if (fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty())\n      {\n        dlog(\"sync: received a request for item ids starting at the beginning of the chain from peer ${peer_endpoint} (full request: ${synopsis})\",\n             (\"peer_endpoint\", originating_peer->get_remote_endpoint())\n             (\"synopsis\", fetch_blockchain_item_ids_message_received.blockchain_synopsis));\n      }\n      else\n      {\n        item_hash_t peers_last_item_hash_seen = fetch_blockchain_item_ids_message_received.blockchain_synopsis.back();\n        dlog(\"sync: received a request for item ids after ${last_item_seen} from peer ${peer_endpoint} (full request: ${synopsis})\",\n             (\"last_item_seen\", peers_last_item_hash_seen)\n             (\"peer_endpoint\", originating_peer->get_remote_endpoint())\n             (\"synopsis\", fetch_blockchain_item_ids_message_received.blockchain_synopsis));\n        peers_last_item_seen.item_hash = peers_last_item_hash_seen;\n      }\n\n      blockchain_item_ids_inventory_message reply_message;\n      reply_message.item_type = fetch_blockchain_item_ids_message_received.item_type;\n      reply_message.total_remaining_item_count = 0;\n      try\n      {\n        reply_message.item_hashes_available = _delegate->get_block_ids(fetch_blockchain_item_ids_message_received.blockchain_synopsis,\n                                                                       reply_message.total_remaining_item_count);\n      }\n      catch (const peer_is_on_an_unreachable_fork&)\n      {\n        dlog(\"Peer is on a fork and there's no set of blocks we can provide to switch them to our fork\");\n        // we reply with an empty list as if we had an empty blockchain; \n        // we don't want to disconnect because they may be able to provide\n        // us with blocks on their chain\n      }\n\n      bool disconnect_from_inhibited_peer = false;\n      // if our client doesn't have any items after the item the peer requested, it will send back\n      // a list containing the last item the peer requested\n      idump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis));\n      if( reply_message.item_hashes_available.empty() )\n        originating_peer->peer_needs_sync_items_from_us = false; /* I have no items in my blockchain */\n      else if( !fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty() &&\n               reply_message.item_hashes_available.size() == 1 &&\n               std::find(fetch_blockchain_item_ids_message_received.blockchain_synopsis.begin(),\n                         fetch_blockchain_item_ids_message_received.blockchain_synopsis.end(),\n                         reply_message.item_hashes_available.back() ) != fetch_blockchain_item_ids_message_received.blockchain_synopsis.end() )\n      {\n        /* the last item in the peer's list matches the last item in our list */\n        originating_peer->peer_needs_sync_items_from_us = false;\n        if (originating_peer->inhibit_fetching_sync_blocks)\n          disconnect_from_inhibited_peer = true; // delay disconnecting until after we send our reply to this fetch_blockchain_item_ids_message\n      }\n      else\n        originating_peer->peer_needs_sync_items_from_us = true;\n\n      if (!originating_peer->peer_needs_sync_items_from_us)\n      {\n        dlog(\"sync: peer is already in sync with us\");\n        // if we thought we had all the items this peer had, but now it turns out that we don't\n        // have the last item it requested to send from,\n        // we need to kick off another round of synchronization\n        if (!originating_peer->we_need_sync_items_from_peer &&\n            !fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty() &&\n            !_delegate->has_item(peers_last_item_seen))\n        {\n          dlog(\"sync: restarting sync with peer ${peer}\", (\"peer\", originating_peer->get_remote_endpoint()));\n          start_synchronizing_with_peer(originating_peer->shared_from_this());\n        }\n      }\n      else\n      {\n        dlog(\"sync: peer is out of sync, sending peer ${count} items ids: first: ${first_item_id}, last: ${last_item_id}\",\n             (\"count\", reply_message.item_hashes_available.size())\n             (\"first_item_id\", reply_message.item_hashes_available.front())\n             (\"last_item_id\", reply_message.item_hashes_available.back()));\n        if (!originating_peer->we_need_sync_items_from_peer &&\n            !fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty() &&\n            !_delegate->has_item(peers_last_item_seen))\n        {\n          dlog(\"sync: restarting sync with peer ${peer}\", (\"peer\", originating_peer->get_remote_endpoint()));\n          start_synchronizing_with_peer(originating_peer->shared_from_this());\n        }\n      }\n      originating_peer->send_message(reply_message);\n\n      if (disconnect_from_inhibited_peer)\n        {\n        // the peer has all of our blocks, and we don't want any of theirs, so disconnect them\n        disconnect_from_peer(originating_peer, \"you are on a fork that I'm unable to switch to\");\n        return;\n        }\n\n      if (originating_peer->direction == peer_connection_direction::inbound &&\n          _handshaking_connections.find(originating_peer->shared_from_this()) != _handshaking_connections.end())\n      {\n        // handshaking is done, move the connection to fully active status and start synchronizing\n        dlog(\"peer ${endpoint} which was handshaking with us has started synchronizing with us, start syncing with it\",\n             (\"endpoint\", originating_peer->get_remote_endpoint()));\n        fc::optional<fc::ip::endpoint> inbound_endpoint = originating_peer->get_endpoint_for_connecting();\n        if (inbound_endpoint)\n        {\n          // mark the connection as successful in the database\n          potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(*inbound_endpoint);\n          updated_peer_record.last_connection_disposition = last_connection_succeeded;\n          _potential_peer_db.update_entry(updated_peer_record);\n        }\n\n        // transition it to our active list\n        move_peer_to_active_list(originating_peer->shared_from_this());\n        new_peer_just_added(originating_peer->shared_from_this());\n      }\n    }\n\n    uint32_t node_impl::calculate_unsynced_block_count_from_all_peers()\n    {\n      VERIFY_CORRECT_THREAD();\n      uint32_t max_number_of_unfetched_items = 0;\n      for( const peer_connection_ptr& peer : _active_connections )\n      {\n        uint32_t this_peer_number_of_unfetched_items = (uint32_t)peer->ids_of_items_to_get.size() + peer->number_of_unfetched_item_ids;\n        max_number_of_unfetched_items = std::max(max_number_of_unfetched_items,\n                                                 this_peer_number_of_unfetched_items);\n      }\n      return max_number_of_unfetched_items;\n    }\n\n    // get a blockchain synopsis that makes sense to send to the given peer.\n    // If the peer isn't yet syncing with us, this is just a synopsis of our active blockchain\n    // If the peer is syncing with us, it is a synopsis of our active blockchain plus the\n    //    blocks the peer has already told us it has\n    std::vector<item_hash_t> node_impl::create_blockchain_synopsis_for_peer( const peer_connection* peer )\n    {\n      VERIFY_CORRECT_THREAD();\n      item_hash_t reference_point = peer->last_block_delegate_has_seen;\n\n      // when we call _delegate->get_blockchain_synopsis(), we may yield and there's a\n      // chance this peer's state will change before we get control back.  Save off\n      // the stuff necessary for generating the synopsis.\n      // This is pretty expensive, we should find a better way to do this\n      std::vector<item_hash_t> original_ids_of_items_to_get(peer->ids_of_items_to_get.begin(), peer->ids_of_items_to_get.end());\n      uint32_t number_of_blocks_after_reference_point = original_ids_of_items_to_get.size();\n\n      std::vector<item_hash_t> synopsis = _delegate->get_blockchain_synopsis(reference_point, number_of_blocks_after_reference_point);\n\n#if 0\n      // just for debugging, enable this and set a breakpoint to step through\n      if (synopsis.empty())\n        synopsis = _delegate->get_blockchain_synopsis(reference_point, number_of_blocks_after_reference_point);\n\n      // TODO: it's possible that the returned synopsis is empty if the blockchain is empty (that's fine)\n      // or if the reference point is now past our undo history (that's not). \n      // in the second case, we should mark this peer as one we're unable to sync with and\n      // disconnect them.\n      if (reference_point != item_hash_t() && synopsis.empty())\n        FC_THROW_EXCEPTION(block_older_than_undo_history, \"You are on a fork I'm unable to switch to\");\n#endif\n\n      if( number_of_blocks_after_reference_point )\n      {\n        // then the synopsis is incomplete, add the missing elements from ids_of_items_to_get\n        uint32_t first_block_num_in_ids_to_get = _delegate->get_block_number(original_ids_of_items_to_get.front());\n        uint32_t true_high_block_num = first_block_num_in_ids_to_get + original_ids_of_items_to_get.size() - 1;\n\n        // in order to generate a seamless synopsis, we need to be using the same low_block_num as the \n        // backend code; the first block in the synopsis will be the low block number it used\n        uint32_t low_block_num = synopsis.empty() ? 1 : _delegate->get_block_number(synopsis.front());\n        \n        do\n        {\n          if( low_block_num >= first_block_num_in_ids_to_get )\n            synopsis.push_back(original_ids_of_items_to_get[low_block_num - first_block_num_in_ids_to_get]);\n          low_block_num += (true_high_block_num - low_block_num + 2 ) / 2;\n        }\n        while ( low_block_num <= true_high_block_num );\n        assert(synopsis.back() == original_ids_of_items_to_get.back());\n      }\n      return synopsis;\n    }\n\n    void node_impl::fetch_next_batch_of_item_ids_from_peer( peer_connection* peer, bool reset_fork_tracking_data_for_peer /* = false */ )\n    {\n      VERIFY_CORRECT_THREAD();\n      if( reset_fork_tracking_data_for_peer )\n      {\n        peer->last_block_delegate_has_seen = item_hash_t();\n        peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hash_t());\n      }\n\n      fc::oexception synopsis_exception;\n      try\n      {\n        std::vector<item_hash_t> blockchain_synopsis = create_blockchain_synopsis_for_peer( peer );\n      \n        item_hash_t last_item_seen = blockchain_synopsis.empty() ? item_hash_t() : blockchain_synopsis.back();\n        dlog( \"sync: sending a request for the next items after ${last_item_seen} to peer ${peer}, (full request is ${blockchain_synopsis})\",\n             ( \"last_item_seen\", last_item_seen )\n             ( \"peer\", peer->get_remote_endpoint() )\n             ( \"blockchain_synopsis\", blockchain_synopsis ) );\n        peer->item_ids_requested_from_peer = boost::make_tuple( blockchain_synopsis, fc::time_point::now() );\n        peer->send_message( fetch_blockchain_item_ids_message(_sync_item_type, blockchain_synopsis ) );\n      }\n      catch (const block_older_than_undo_history& e)\n      {\n        synopsis_exception = e;\n      }\n      if (synopsis_exception)\n        disconnect_from_peer(peer, \"You are on a fork I'm unable to switch to\");\n    }\n\n    void node_impl::on_blockchain_item_ids_inventory_message(peer_connection* originating_peer,\n                                                             const blockchain_item_ids_inventory_message& blockchain_item_ids_inventory_message_received )\n    {\n      VERIFY_CORRECT_THREAD();\n      // ignore unless we asked for the data\n      if( originating_peer->item_ids_requested_from_peer )\n      {\n        // verify that the peer's the block ids the peer sent is a valid response to our request;\n        // It should either be an empty list of blocks, or a list of blocks that builds off of one of \n        // the blocks in the synopsis we sent\n        if (!blockchain_item_ids_inventory_message_received.item_hashes_available.empty())\n        {\n          // what's more, it should be a sequential list of blocks, verify that first\n          uint32_t first_block_number_in_reponse = _delegate->get_block_number(blockchain_item_ids_inventory_message_received.item_hashes_available.front());\n          for (unsigned i = 1; i < blockchain_item_ids_inventory_message_received.item_hashes_available.size(); ++i)\n          {\n            uint32_t actual_num = _delegate->get_block_number(blockchain_item_ids_inventory_message_received.item_hashes_available[i]);\n            uint32_t expected_num = first_block_number_in_reponse + i;\n            if (actual_num != expected_num)\n            {\n            wlog(\"Invalid response from peer ${peer_endpoint}.  The list of blocks they provided is not sequential, \"\n                 \"the ${position}th block in their reply was block number ${actual_num}, \"\n                 \"but it should have been number ${expected_num}\",\n                 (\"peer_endpoint\", originating_peer->get_remote_endpoint())\n                 (\"position\", i)\n                 (\"actual_num\", actual_num)\n                 (\"expected_num\", expected_num));\n            fc::exception error_for_peer(FC_LOG_MESSAGE(error, \n                                                        \"You gave an invalid response to my request for sync blocks.  The list of blocks you provided is not sequential, \"\n                                                        \"the ${position}th block in their reply was block number ${actual_num}, \"\n                                                        \"but it should have been number ${expected_num}\",\n                                                        (\"position\", i)\n                                                        (\"actual_num\", actual_num)\n                                                        (\"expected_num\", expected_num)));\n            disconnect_from_peer(originating_peer,\n                                 \"You gave an invalid response to my request for sync blocks\",\n                                 true, error_for_peer);\n            return;\n            }\n          }\n\n          const std::vector<item_hash_t>& synopsis_sent_in_request = originating_peer->item_ids_requested_from_peer->get<0>();\n          const item_hash_t& first_item_hash = blockchain_item_ids_inventory_message_received.item_hashes_available.front();\n\n          if (synopsis_sent_in_request.empty())\n          {\n            // if we sent an empty synopsis, we were asking for all blocks, so the first block should be block 1\n            if (_delegate->get_block_number(first_item_hash) != 1)\n            {\n              wlog(\"Invalid response from peer ${peer_endpoint}.  We requested a list of sync blocks starting from the beginning of the chain, \"\n                   \"but they provided a list of blocks starting with ${first_block}\",\n                   (\"peer_endpoint\", originating_peer->get_remote_endpoint())\n                   (\"first_block\", first_item_hash));\n              fc::exception error_for_peer(FC_LOG_MESSAGE(error, \"You gave an invalid response for my request for sync blocks.  I asked for blocks starting from the beginning of the chain, \"\n                                                          \"but you returned a list of blocks starting with ${first_block}\",  \n                                                          (\"first_block\", first_item_hash)));\n              disconnect_from_peer(originating_peer,\n                                   \"You gave an invalid response to my request for sync blocks\",\n                                   true, error_for_peer);\n              return;\n            }\n          }\n          else // synopsis was not empty, we expect a response building off one of the blocks we sent\n          {\n            if (boost::range::find(synopsis_sent_in_request, first_item_hash) == synopsis_sent_in_request.end())\n            {\n              wlog(\"Invalid response from peer ${peer_endpoint}.  We requested a list of sync blocks based on the synopsis ${synopsis}, but they \"\n                   \"provided a list of blocks starting with ${first_block}\",\n                   (\"peer_endpoint\", originating_peer->get_remote_endpoint())\n                   (\"synopsis\", synopsis_sent_in_request)\n                   (\"first_block\", first_item_hash));\n              fc::exception error_for_peer(FC_LOG_MESSAGE(error, \"You gave an invalid response for my request for sync blocks.  I asked for blocks following something in \"\n                                                          \"${synopsis}, but you returned a list of blocks starting with ${first_block} which wasn't one of your choices\",  \n                                                          (\"synopsis\", synopsis_sent_in_request)\n                                                          (\"first_block\", first_item_hash)));\n              disconnect_from_peer(originating_peer,\n                                   \"You gave an invalid response to my request for sync blocks\",\n                                   true, error_for_peer);\n              return;\n            }\n          }\n        }\n        originating_peer->item_ids_requested_from_peer.reset();\n\n        // if exceptions are throw after clearing the item_ids_requested_from_peer (above),\n        // it could leave our sync in a stalled state.  Wrap a try/catch around the rest\n        // of the function so we can log if this ever happens.\n        try\n        {\n          dlog( \"sync: received a list of ${count} available items from ${peer_endpoint}\",\n               ( \"count\", blockchain_item_ids_inventory_message_received.item_hashes_available.size() )\n               ( \"peer_endpoint\", originating_peer->get_remote_endpoint() ) );\n          //for( const item_hash_t& item_hash : blockchain_item_ids_inventory_message_received.item_hashes_available )\n          //{\n          //  dlog( \"sync:     ${hash}\", (\"hash\", item_hash ) );\n          //}\n\n          // if the peer doesn't have any items after the one we asked for\n          if( blockchain_item_ids_inventory_message_received.total_remaining_item_count == 0 &&\n              ( blockchain_item_ids_inventory_message_received.item_hashes_available.empty() || // there are no items in the peer's blockchain.  this should only happen if our blockchain was empty when we requested, might want to verify that.\n               ( blockchain_item_ids_inventory_message_received.item_hashes_available.size() == 1 &&\n                _delegate->has_item( item_id(blockchain_item_ids_inventory_message_received.item_type,\n                                            blockchain_item_ids_inventory_message_received.item_hashes_available.front() ) ) ) ) && // we've already seen the last item in the peer's blockchain\n              originating_peer->ids_of_items_to_get.empty() &&\n              originating_peer->number_of_unfetched_item_ids == 0 ) // <-- is the last check necessary?\n          {\n            dlog( \"sync: peer said we're up-to-date, entering normal operation with this peer\" );\n            originating_peer->we_need_sync_items_from_peer = false;\n\n            uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();\n            _total_number_of_unfetched_items = new_number_of_unfetched_items;\n            if( new_number_of_unfetched_items == 0 )\n              _delegate->sync_status( blockchain_item_ids_inventory_message_received.item_type, 0 );\n\n            return;\n          }\n\n          std::deque<item_hash_t> item_hashes_received( blockchain_item_ids_inventory_message_received.item_hashes_available.begin(),\n                                                       blockchain_item_ids_inventory_message_received.item_hashes_available.end() );\n          originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;\n          // flush any items this peer sent us that we've already received and processed from another peer\n          if (!item_hashes_received.empty() &&\n              originating_peer->ids_of_items_to_get.empty())\n          {\n            bool is_first_item_for_other_peer = false;\n            for (const peer_connection_ptr& peer : _active_connections)\n              if (peer != originating_peer->shared_from_this() &&\n                  !peer->ids_of_items_to_get.empty() &&\n                  peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front())\n              {\n                dlog(\"The item ${newitem} is the first item for peer ${peer}\",\n                     (\"newitem\", blockchain_item_ids_inventory_message_received.item_hashes_available.front())\n                     (\"peer\", peer->get_remote_endpoint()));\n                is_first_item_for_other_peer = true;\n                break;\n              }\n            dlog(\"is_first_item_for_other_peer: ${is_first}.  item_hashes_received.size() = ${size}\",\n                 (\"is_first\", is_first_item_for_other_peer)(\"size\", item_hashes_received.size()));\n            if (!is_first_item_for_other_peer)\n            {\n              while (!item_hashes_received.empty() &&\n                     _delegate->has_item(item_id(blockchain_item_ids_inventory_message_received.item_type,\n                                                 item_hashes_received.front())))\n              {\n                assert(item_hashes_received.front() != item_hash_t());\n                originating_peer->last_block_delegate_has_seen = item_hashes_received.front();\n                originating_peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hashes_received.front());\n                dlog(\"popping item because delegate has already seen it.  peer ${peer}'s last block the delegate has seen is now ${block_id} (actual block #${actual_block_num})\",\n                     (\"peer\", originating_peer->get_remote_endpoint())\n                     (\"block_id\", originating_peer->last_block_delegate_has_seen)\n                     (\"actual_block_num\", _delegate->get_block_number(item_hashes_received.front())));\n\n                item_hashes_received.pop_front();\n              }\n              dlog(\"after removing all items we have already seen, item_hashes_received.size() = ${size}\", (\"size\", item_hashes_received.size()));\n            }\n          }\n          else if (!item_hashes_received.empty())\n          {\n            // we received a list of items and we already have a list of items to fetch from this peer.\n            // In the normal case, this list will immediately follow the existing list, meaning the\n            // last hash of our existing list will match the first hash of the new list.\n\n            // In the much less likely case, we've received a partial list of items from the peer, then\n            // the peer switched forks before sending us the remaining list.  In this case, the first\n            // hash in the new list may not be the last hash in the existing list (it may be earlier, or\n            // it may not exist at all.\n\n            // In either case, pop items off the back of our existing list until we find our first\n            // item, then append our list.\n            while (!originating_peer->ids_of_items_to_get.empty())\n            {\n              if (item_hashes_received.front() != originating_peer->ids_of_items_to_get.back())\n                originating_peer->ids_of_items_to_get.pop_back();\n              else\n                break;\n            }\n            if (originating_peer->ids_of_items_to_get.empty())\n            {\n              // this happens when the peer has switched forks between the last inventory message and\n              // this one, and there weren't any unfetched items in common\n              // We don't know where in the blockchain the new front() actually falls, all we can\n              // expect is that it is a block that we knew about because it should be one of the\n              // blocks we sent in the initial synopsis.\n              assert(_delegate->has_item(item_id(_sync_item_type, item_hashes_received.front())));\n              originating_peer->last_block_delegate_has_seen = item_hashes_received.front();\n              originating_peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hashes_received.front());\n              item_hashes_received.pop_front();\n            }\n            else\n            {\n              // the common simple case: the new list extends the old.  pop off the duplicate element\n              originating_peer->ids_of_items_to_get.pop_back();\n            }\n          }\n\n          if (!item_hashes_received.empty() && !originating_peer->ids_of_items_to_get.empty())\n            assert(item_hashes_received.front() != originating_peer->ids_of_items_to_get.back());\n\n          // at any given time, there's a maximum number of blocks that can possibly be out there\n          // [(now - genesis time) / block interval].  If they offer us more blocks than that,\n          // they must be an attacker or have a buggy client.\n          fc::time_point_sec minimum_time_of_last_offered_block =\n              originating_peer->last_block_time_delegate_has_seen + // timestamp of the block immediately before the first unfetched block\n              originating_peer->number_of_unfetched_item_ids * GRAPHENE_MIN_BLOCK_INTERVAL;\n          fc::time_point_sec now = fc::time_point::now();\n          if (minimum_time_of_last_offered_block > now + GRAPHENE_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC)\n          {\n            wlog(\"Disconnecting from peer ${peer} who offered us an implausible number of blocks, their last block would be in the future (${timestamp})\",\n                 (\"peer\", originating_peer->get_remote_endpoint())\n                 (\"timestamp\", minimum_time_of_last_offered_block));\n            fc::exception error_for_peer(FC_LOG_MESSAGE(error, \"You offered me a list of more sync blocks than could possibly exist.  Total blocks offered: ${blocks}, Minimum time of the last block you offered: ${minimum_time_of_last_offered_block}, Now: ${now}\",\n                                                        (\"blocks\", originating_peer->number_of_unfetched_item_ids)\n                                                        (\"minimum_time_of_last_offered_block\", minimum_time_of_last_offered_block)\n                                                        (\"now\", now)));\n            disconnect_from_peer(originating_peer,\n                                 \"You offered me a list of more sync blocks than could possibly exist\",\n                                 true, error_for_peer);\n            return;\n          }\n\n          // append the remaining items to the peer's list\n          boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received);\n          \n          uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();\n          if (new_number_of_unfetched_items != _total_number_of_unfetched_items)\n            _delegate->sync_status(blockchain_item_ids_inventory_message_received.item_type,\n                                   new_number_of_unfetched_items);\n          _total_number_of_unfetched_items = new_number_of_unfetched_items;\n\n          if (blockchain_item_ids_inventory_message_received.total_remaining_item_count != 0)\n          {\n            // the peer hasn't sent us all the items it knows about.\n            if (originating_peer->ids_of_items_to_get.size() > GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH)\n            {\n              // we have a good number of item ids from this peer, start fetching blocks from it;\n              // we'll switch back later to finish the job.\n              trigger_fetch_sync_items_loop();\n            }\n            else\n            {\n              // keep fetching the peer's list of sync items until we get enough to switch into block-\n              // fetchimg mode\n              fetch_next_batch_of_item_ids_from_peer(originating_peer);\n            }\n          }\n          else\n          {\n            // the peer has told us about all of the items it knows\n            if (!originating_peer->ids_of_items_to_get.empty())\n            {\n              // we now know about all of the items the peer knows about, and there are some items on the list\n              // that we should try to fetch.  Kick off the fetch loop.\n              trigger_fetch_sync_items_loop();\n            }\n            else\n            {\n              // If we get here, the peer has sent us a non-empty list of items, but we have already\n              // received all of the items from other peers.  Send a new request to the peer to\n              // see if we're really in sync\n              fetch_next_batch_of_item_ids_from_peer(originating_peer);\n            }\n          }\n        }\n        catch (const fc::canceled_exception&)\n        {\n          throw;\n        }\n        catch (const fc::exception& e)\n        {\n          elog(\"Caught unexpected exception: ${e}\", (\"e\", e));\n          assert(false && \"exceptions not expected here\");\n        }\n        catch (const std::exception& e)\n        {\n          elog(\"Caught unexpected exception: ${e}\", (\"e\", e.what()));\n          assert(false && \"exceptions not expected here\");\n        }\n        catch (...)\n        {\n          elog(\"Caught unexpected exception, could break sync operation\");\n        }\n      }\n      else\n      {\n        wlog(\"sync: received a list of sync items available, but I didn't ask for any!\");\n      }\n    }\n\n    message node_impl::get_message_for_item(const item_id& item)\n    {\n      try\n      {\n        return _message_cache.get_message(item.item_hash);\n      }\n      catch (fc::key_not_found_exception&)\n      {}\n      try\n      {\n        return _delegate->get_item(item);\n      }\n      catch (fc::key_not_found_exception&)\n      {}\n      return item_not_available_message(item);\n    }\n\n    void node_impl::on_fetch_items_message(peer_connection* originating_peer, const fetch_items_message& fetch_items_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog(\"received items request for ids ${ids} of type ${type} from peer ${endpoint}\",\n           (\"ids\", fetch_items_message_received.items_to_fetch)\n           (\"type\", fetch_items_message_received.item_type)\n           (\"endpoint\", originating_peer->get_remote_endpoint()));\n\n      fc::optional<message> last_block_message_sent;\n\n      std::list<message> reply_messages;\n      for (const item_hash_t& item_hash : fetch_items_message_received.items_to_fetch)\n      {\n        try\n        {\n          message requested_message = _message_cache.get_message(item_hash);\n          dlog(\"received item request for item ${id} from peer ${endpoint}, returning the item from my message cache\",\n               (\"endpoint\", originating_peer->get_remote_endpoint())\n               (\"id\", requested_message.id()));\n          reply_messages.push_back(requested_message);\n          if (fetch_items_message_received.item_type == block_message_type)\n            last_block_message_sent = requested_message;\n          continue;\n        }\n        catch (fc::key_not_found_exception&)\n        {\n           // it wasn't in our local cache, that's ok ask the client\n        }\n\n        item_id item_to_fetch(fetch_items_message_received.item_type, item_hash);\n        try\n        {\n          message requested_message = _delegate->get_item(item_to_fetch);\n          dlog(\"received item request from peer ${endpoint}, returning the item from delegate with id ${id} size ${size}\",\n               (\"id\", requested_message.id())\n               (\"size\", requested_message.size)\n               (\"endpoint\", originating_peer->get_remote_endpoint()));\n          reply_messages.push_back(requested_message);\n          if (fetch_items_message_received.item_type == block_message_type)\n            last_block_message_sent = requested_message;\n          continue;\n        }\n        catch (fc::key_not_found_exception&)\n        {\n          reply_messages.push_back(item_not_available_message(item_to_fetch));\n          dlog(\"received item request from peer ${endpoint} but we don't have it\",\n               (\"endpoint\", originating_peer->get_remote_endpoint()));\n        }\n      }\n\n      // if we sent them a block, update our record of the last block they've seen accordingly\n      if (last_block_message_sent)\n      {\n        graphene::net::block_message block = last_block_message_sent->as<graphene::net::block_message>();\n        originating_peer->last_block_delegate_has_seen = block.block_id;\n        originating_peer->last_block_time_delegate_has_seen = _delegate->get_block_time(block.block_id);\n      }\n\n      for (const message& reply : reply_messages)\n      {\n        if (reply.msg_type.value() == block_message_type)\n          originating_peer->send_item(item_id(block_message_type, reply.as<graphene::net::block_message>().block_id));\n        else\n          originating_peer->send_message(reply);\n      }\n    }\n\n    void node_impl::on_item_not_available_message( peer_connection* originating_peer, const item_not_available_message& item_not_available_message_received )\n    {\n      VERIFY_CORRECT_THREAD();\n      const item_id& requested_item = item_not_available_message_received.requested_item;\n      auto regular_item_iter = originating_peer->items_requested_from_peer.find(requested_item);\n      if (regular_item_iter != originating_peer->items_requested_from_peer.end())\n      {\n        originating_peer->items_requested_from_peer.erase( regular_item_iter );\n        originating_peer->inventory_peer_advertised_to_us.erase( requested_item );\n        if (is_item_in_any_peers_inventory(requested_item))\n          _items_to_fetch.insert(prioritized_item_id(requested_item, _items_to_fetch_sequence_counter++));\n        wlog(\"Peer doesn't have the requested item.\");\n        trigger_fetch_items_loop();\n        return;\n      }\n\n      auto sync_item_iter = originating_peer->sync_items_requested_from_peer.find(requested_item.item_hash);\n      if (sync_item_iter != originating_peer->sync_items_requested_from_peer.end())\n      {\n        originating_peer->sync_items_requested_from_peer.erase(sync_item_iter);\n\n        if (originating_peer->peer_needs_sync_items_from_us)\n          originating_peer->inhibit_fetching_sync_blocks = true;\n        else\n          disconnect_from_peer(originating_peer, \"You are missing a sync item you claim to have, your database is probably corrupted. Try --rebuild-index.\",true,\n                               fc::exception(FC_LOG_MESSAGE(error,\"You are missing a sync item you claim to have, your database is probably corrupted. Try --rebuild-index.\",\n                               (\"item_id\", requested_item))));\n        wlog(\"Peer doesn't have the requested sync item.  This really shouldn't happen\");\n        trigger_fetch_sync_items_loop();\n        return;\n      }\n\n      dlog(\"Peer doesn't have an item we're looking for, which is fine because we weren't looking for it\");\n    }\n\n    void node_impl::on_item_ids_inventory_message(peer_connection* originating_peer, const item_ids_inventory_message& item_ids_inventory_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n\n      // expire old inventory so we'll be making decisions our about whether to fetch blocks below based only on recent inventory\n      originating_peer->clear_old_inventory();\n\n      dlog( \"received inventory of ${count} items from peer ${endpoint}\",\n           ( \"count\", item_ids_inventory_message_received.item_hashes_available.size() )(\"endpoint\", originating_peer->get_remote_endpoint() ) );\n      for( const item_hash_t& item_hash : item_ids_inventory_message_received.item_hashes_available )\n      {\n        item_id advertised_item_id(item_ids_inventory_message_received.item_type, item_hash);\n        bool we_advertised_this_item_to_a_peer = false;\n        bool we_requested_this_item_from_a_peer = false;\n        for (const peer_connection_ptr peer : _active_connections)\n        {\n          if (peer->inventory_advertised_to_peer.find(advertised_item_id) != peer->inventory_advertised_to_peer.end())\n          {\n            we_advertised_this_item_to_a_peer = true;\n            break;\n          }\n          if (peer->items_requested_from_peer.find(advertised_item_id) != peer->items_requested_from_peer.end())\n            we_requested_this_item_from_a_peer = true;\n        }\n\n        // if we have already advertised it to a peer, we must have it, no need to do anything else\n        if (!we_advertised_this_item_to_a_peer)\n        {\n          // if the peer has flooded us with transactions, don't add these to the inventory to prevent our\n          // inventory list from growing without bound.  We try to allow fetching blocks even when\n          // we've stopped fetching transactions.\n          if ((item_ids_inventory_message_received.item_type == graphene::net::trx_message_type &&\n               originating_peer->is_inventory_advertised_to_us_list_full_for_transactions()) ||\n              originating_peer->is_inventory_advertised_to_us_list_full())\n            break;\n          originating_peer->inventory_peer_advertised_to_us.insert(peer_connection::timestamped_item_id(advertised_item_id, fc::time_point::now()));\n          if (!we_requested_this_item_from_a_peer)\n          {\n            if (_recently_failed_items.find(item_id(item_ids_inventory_message_received.item_type, item_hash)) != _recently_failed_items.end())\n            {\n              dlog(\"not adding ${item_hash} to our list of items to fetch because we've recently fetched a copy and it failed to push\",\n                   (\"item_hash\", item_hash));\n            }\n            else\n            {\n              auto items_to_fetch_iter = _items_to_fetch.get<item_id_index>().find(advertised_item_id);\n              if (items_to_fetch_iter == _items_to_fetch.get<item_id_index>().end())\n              {\n                // it's new to us\n                _items_to_fetch.insert(prioritized_item_id(advertised_item_id, _items_to_fetch_sequence_counter++));\n                dlog(\"adding item ${item_hash} from inventory message to our list of items to fetch\",\n                     (\"item_hash\", item_hash));\n                trigger_fetch_items_loop();\n              }\n              else\n              {\n                // another peer has told us about this item already, but this peer just told us it has the item\n                // too, we can expect it to be around in this peer's cache for longer, so update its timestamp\n                _items_to_fetch.get<item_id_index>().modify(items_to_fetch_iter,\n                                                            [](prioritized_item_id& item) { item.timestamp = fc::time_point::now(); });\n              } \n            }\n          }\n        }\n      }\n\n    }\n\n    void node_impl::on_closing_connection_message( peer_connection* originating_peer,\n          const closing_connection_message& closing_connection_message_received )\n    {\n      VERIFY_CORRECT_THREAD();\n      originating_peer->they_have_requested_close = true;\n\n      if( closing_connection_message_received.closing_due_to_error )\n      {\n        wlog( \"Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}\",\n             ( \"peer\", originating_peer->get_remote_endpoint() )\n             ( \"msg\", closing_connection_message_received.reason_for_closing )\n             ( \"error\", closing_connection_message_received.error ) );\n        std::ostringstream message;\n        message << \"Peer \" << fc::variant( originating_peer->get_remote_endpoint(),\n                                           GRAPHENE_NET_MAX_NESTED_OBJECTS ).as_string() <<\n                  \" disconnected us: \" << closing_connection_message_received.reason_for_closing;\n        fc::exception detailed_error(FC_LOG_MESSAGE(warn,\n              \"Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}\",\n              ( \"peer\", originating_peer->get_remote_endpoint() )\n              ( \"msg\", closing_connection_message_received.reason_for_closing )\n              ( \"error\", closing_connection_message_received.error ) ));\n        _delegate->error_encountered( message.str(),\n                                      detailed_error );\n      }\n      else\n      {\n        wlog( \"Peer ${peer} is disconnecting us because: ${msg}\",\n             ( \"peer\", originating_peer->get_remote_endpoint() )\n             ( \"msg\", closing_connection_message_received.reason_for_closing ) );\n      }\n      if( originating_peer->we_have_requested_close )\n        originating_peer->close_connection();\n      }\n\n    void node_impl::on_connection_closed(peer_connection* originating_peer)\n    {\n      VERIFY_CORRECT_THREAD();\n      peer_connection_ptr originating_peer_ptr = originating_peer->shared_from_this();\n      _rate_limiter.remove_tcp_socket( &originating_peer->get_socket() );\n\n      // if we closed the connection (due to timeout or handshake failure), we should have recorded an\n      // error message to store in the peer database when we closed the connection\n      fc::optional<fc::ip::endpoint> inbound_endpoint = originating_peer->get_endpoint_for_connecting();\n      if (originating_peer->connection_closed_error && inbound_endpoint)\n      {\n        fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint);\n        if (updated_peer_record)\n        {\n          updated_peer_record->last_error = *originating_peer->connection_closed_error;\n          _potential_peer_db.update_entry(*updated_peer_record);\n        }\n      }\n\n      _closing_connections.erase(originating_peer_ptr);\n      _handshaking_connections.erase(originating_peer_ptr);\n      _terminating_connections.erase(originating_peer_ptr);\n      if (_active_connections.find(originating_peer_ptr) != _active_connections.end())\n      {\n        _active_connections.erase(originating_peer_ptr);\n\n        if (inbound_endpoint && originating_peer_ptr->get_remote_endpoint())\n        {\n          fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint);\n          if (updated_peer_record)\n          {\n            updated_peer_record->last_seen_time = fc::time_point::now();\n            _potential_peer_db.update_entry(*updated_peer_record);\n          }\n        }\n      }\n\n      ilog(\"Remote peer ${endpoint} closed their connection to us\", (\"endpoint\", originating_peer->get_remote_endpoint()));\n      display_current_connections();\n      trigger_p2p_network_connect_loop();\n\n      // notify the node delegate so it can update the display\n      if( _active_connections.size() != _last_reported_number_of_connections )\n      {\n        _last_reported_number_of_connections = (uint32_t)_active_connections.size();\n        _delegate->connection_count_changed( _last_reported_number_of_connections );\n      }\n\n      // if we had delegated a firewall check to this peer, send it to another peer\n      if (originating_peer->firewall_check_state)\n      {\n        if (originating_peer->firewall_check_state->requesting_peer != node_id_t())\n        {\n          // it's a check we're doing for another node\n          firewall_check_state_data* firewall_check_state = originating_peer->firewall_check_state;\n          originating_peer->firewall_check_state = nullptr;\n          forward_firewall_check_to_next_available_peer(firewall_check_state);\n        }\n        else\n        {\n          // we were asking them to check whether we're firewalled.  we'll just let it\n          // go for now\n          delete originating_peer->firewall_check_state;\n        }\n      }\n\n      // if we had requested any sync or regular items from this peer that we haven't\n      // received yet, reschedule them to be fetched from another peer\n      if (!originating_peer->sync_items_requested_from_peer.empty())\n      {\n        for (auto sync_item : originating_peer->sync_items_requested_from_peer)\n          _active_sync_requests.erase(sync_item);\n        trigger_fetch_sync_items_loop();\n      }\n\n      if (!originating_peer->items_requested_from_peer.empty())\n      {\n        for (auto item_and_time : originating_peer->items_requested_from_peer)\n        {\n          if (is_item_in_any_peers_inventory(item_and_time.first))\n            _items_to_fetch.insert(prioritized_item_id(item_and_time.first, _items_to_fetch_sequence_counter++));\n        }\n        trigger_fetch_items_loop();\n      }\n\n      schedule_peer_for_deletion(originating_peer_ptr);\n    }\n\n    void node_impl::send_sync_block_to_node_delegate(const graphene::net::block_message& block_message_to_send)\n    {\n      dlog(\"in send_sync_block_to_node_delegate()\");\n      bool client_accepted_block = false;\n      bool discontinue_fetching_blocks_from_peer = false;\n\n      fc::oexception handle_message_exception;\n\n      try\n      {\n        std::vector<fc::uint160_t> contained_transaction_message_ids;\n        _delegate->handle_block(block_message_to_send, true, contained_transaction_message_ids);\n        ilog(\"Successfully pushed sync block ${num} (id:${id})\",\n             (\"num\", block_message_to_send.block.block_num())\n             (\"id\", block_message_to_send.block_id));\n        _most_recent_blocks_accepted.push_back(block_message_to_send.block_id);\n\n        client_accepted_block = true;\n      }\n      catch (const block_older_than_undo_history& e)\n      {\n        wlog(\"Failed to push sync block ${num} (id:${id}): block is on a fork older than our undo history would \"\n             \"allow us to switch to: ${e}\",\n             (\"num\", block_message_to_send.block.block_num())\n             (\"id\", block_message_to_send.block_id)\n             (\"e\", (fc::exception)e));\n        handle_message_exception = e;\n        discontinue_fetching_blocks_from_peer = true;\n      }\n      catch (const fc::canceled_exception&)\n      {\n        throw;\n      }\n      catch (const fc::exception& e)\n      {\n        auto block_num = block_message_to_send.block.block_num();\n        wlog(\"Failed to push sync block ${num} (id:${id}): client rejected sync block sent by peer: ${e}\",\n             (\"num\", block_num)\n             (\"id\", block_message_to_send.block_id)\n             (\"e\", e));\n        if( e.code() == block_timestamp_in_future_exception::code_enum::code_value )\n        {\n           handle_message_exception = block_timestamp_in_future_exception( FC_LOG_MESSAGE( warn, \"\",\n                (\"block_header\", static_cast<graphene::protocol::block_header>(block_message_to_send.block))\n                (\"block_num\", block_num)\n                (\"block_id\", block_message_to_send.block_id) ) );\n        }\n        else\n           handle_message_exception = e;\n      }\n\n      // build up lists for any potentially-blocking operations we need to do, then do them\n      // at the end of this function\n      std::set<peer_connection_ptr> peers_with_newly_empty_item_lists;\n      std::set<peer_connection_ptr> peers_we_need_to_sync_to;\n      std::map<peer_connection_ptr, std::pair<std::string, fc::oexception> > peers_to_disconnect; // map peer -> pair<reason_string, exception>\n\n      if( client_accepted_block )\n      {\n        --_total_number_of_unfetched_items;\n        dlog(\"sync: client accpted the block, we now have only ${count} items left to fetch before we're in sync\",\n              (\"count\", _total_number_of_unfetched_items));\n        bool is_fork_block = is_hard_fork_block(block_message_to_send.block.block_num());\n        for (const peer_connection_ptr& peer : _active_connections)\n        {\n          ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections\n          bool disconnecting_this_peer = false;\n          if (is_fork_block)\n          {\n            // we just pushed a hard fork block.  Find out if this peer is running a client\n            // that will be unable to process future blocks\n            if (peer->last_known_fork_block_number != 0)\n            {\n              uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(peer->last_known_fork_block_number);\n              if (next_fork_block_number != 0 &&\n                  next_fork_block_number <= block_message_to_send.block.block_num())\n              {\n                std::ostringstream disconnect_reason_stream;\n                disconnect_reason_stream << \"You need to upgrade your client due to hard fork at block \" << block_message_to_send.block.block_num();\n                peers_to_disconnect[peer] = std::make_pair(disconnect_reason_stream.str(),\n                                                           fc::oexception(fc::exception(FC_LOG_MESSAGE(error, \"You need to upgrade your client due to hard fork at block ${block_number}\",\n                                                                                                       (\"block_number\", block_message_to_send.block.block_num())))));\n#ifdef ENABLE_DEBUG_ULOGS\n                ulog(\"Disconnecting from peer during sync because their version is too old.  Their version date: ${date}\", (\"date\", peer->graphene_git_revision_unix_timestamp));\n#endif\n                disconnecting_this_peer = true;\n              }\n            }\n          }\n          if (!disconnecting_this_peer &&\n              peer->ids_of_items_to_get.empty() && peer->ids_of_items_being_processed.empty())\n          {\n            dlog( \"Cannot pop first element off peer ${peer}'s list, its list is empty\", (\"peer\", peer->get_remote_endpoint() ) );\n            // we don't know for sure that this peer has the item we just received.\n            // If peer is still syncing to us, we know they will ask us for\n            // sync item ids at least one more time and we'll notify them about\n            // the item then, so there's no need to do anything.  If we still need items\n            // from them, we'll be asking them for more items at some point, and\n            // that will clue them in that they are out of sync.  If we're fully in sync\n            // we need to kick off another round of synchronization with them so they can\n            // find out about the new item.\n            if (!peer->peer_needs_sync_items_from_us && !peer->we_need_sync_items_from_peer)\n            {\n              dlog(\"We will be restarting synchronization with peer ${peer}\", (\"peer\", peer->get_remote_endpoint()));\n              peers_we_need_to_sync_to.insert(peer);\n            }\n          }\n          else if (!disconnecting_this_peer)\n          {\n            auto items_being_processed_iter = peer->ids_of_items_being_processed.find(block_message_to_send.block_id);\n            if (items_being_processed_iter != peer->ids_of_items_being_processed.end())\n            {\n              peer->last_block_delegate_has_seen = block_message_to_send.block_id;\n              peer->last_block_time_delegate_has_seen = block_message_to_send.block.timestamp;\n\n              peer->ids_of_items_being_processed.erase(items_being_processed_iter);\n              dlog(\"Removed item from ${endpoint}'s list of items being processed, still processing ${len} blocks\",\n                   (\"endpoint\", peer->get_remote_endpoint())(\"len\", peer->ids_of_items_being_processed.size()));\n\n              // if we just received the last item in our list from this peer, we will want to\n              // send another request to find out if we are in sync, but we can't do this yet\n              // (we don't want to allow a fiber swap in the middle of popping items off the list)\n              if (peer->ids_of_items_to_get.empty() &&\n                  peer->number_of_unfetched_item_ids == 0 &&\n                  peer->ids_of_items_being_processed.empty())\n                peers_with_newly_empty_item_lists.insert(peer);\n\n              // in this case, we know the peer was offering us this exact item, no need to\n              // try to inform them of its existence\n            }\n          }\n        }\n      }\n      else\n      {\n        // invalid message received\n        for (const peer_connection_ptr& peer : _active_connections)\n        {\n          ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections\n\n          if (peer->ids_of_items_being_processed.find(block_message_to_send.block_id)\n                 != peer->ids_of_items_being_processed.end())\n          {\n            if (discontinue_fetching_blocks_from_peer)\n            {\n              wlog(\"inhibiting fetching sync blocks from peer ${endpoint} because it is on a fork that's too old\",\n                   (\"endpoint\", peer->get_remote_endpoint()));\n              peer->inhibit_fetching_sync_blocks = true;\n            }\n            else\n              peers_to_disconnect[peer] = std::make_pair(\n                    std::string(\"You offered us a block that we reject as invalid\"),\n                    fc::oexception(handle_message_exception));\n          }\n        }\n      }\n\n      for (auto& peer_to_disconnect : peers_to_disconnect)\n      {\n        const peer_connection_ptr& peer = peer_to_disconnect.first;\n        std::string reason_string;\n        fc::oexception reason_exception;\n        std::tie(reason_string, reason_exception) = peer_to_disconnect.second;\n        wlog(\"disconnecting client ${endpoint} because it offered us the rejected block\",\n             (\"endpoint\", peer->get_remote_endpoint()));\n        disconnect_from_peer(peer.get(), reason_string, true, reason_exception);\n      }\n      for (const peer_connection_ptr& peer : peers_with_newly_empty_item_lists)\n        fetch_next_batch_of_item_ids_from_peer(peer.get());\n\n      for (const peer_connection_ptr& peer : peers_we_need_to_sync_to)\n        start_synchronizing_with_peer(peer);\n\n      dlog(\"Leaving send_sync_block_to_node_delegate\");\n\n      if (// _suspend_fetching_sync_blocks && <-- you can use this if \"maximum_number_of_blocks_to_handle_at_one_time\" == \"maximum_number_of_sync_blocks_to_prefetch\"\n          !_node_is_shutting_down &&\n          (!_process_backlog_of_sync_blocks_done.valid() || _process_backlog_of_sync_blocks_done.ready()))\n        _process_backlog_of_sync_blocks_done = fc::async([=](){ process_backlog_of_sync_blocks(); },\n                                                         \"process_backlog_of_sync_blocks\");\n    }\n\n    void node_impl::process_backlog_of_sync_blocks()\n    {\n      VERIFY_CORRECT_THREAD();\n      // garbage-collect the list of async tasks here for lack of a better place\n      for (auto calls_iter = _handle_message_calls_in_progress.begin();\n            calls_iter != _handle_message_calls_in_progress.end();)\n      {\n        if (calls_iter->ready())\n          calls_iter = _handle_message_calls_in_progress.erase(calls_iter);\n        else\n          ++calls_iter;\n      }\n\n      dlog(\"in process_backlog_of_sync_blocks\");\n      if (_handle_message_calls_in_progress.size() >= _maximum_number_of_blocks_to_handle_at_one_time)\n      {\n        dlog(\"leaving process_backlog_of_sync_blocks because we're already processing too many blocks\");\n        return; // we will be rescheduled when the next block finishes its processing\n      }\n      dlog(\"currently ${count} blocks in the process of being handled\", (\"count\", _handle_message_calls_in_progress.size()));\n\n\n      if (_suspend_fetching_sync_blocks)\n      {\n        dlog(\"resuming processing sync block backlog because we only ${count} blocks in progress\",\n             (\"count\", _handle_message_calls_in_progress.size()));\n        _suspend_fetching_sync_blocks = false;\n      }\n\n\n      // when syncing with multiple peers, it's possible that we'll have hundreds of blocks ready to push\n      // to the client at once.  This can be slow, and we need to limit the number we push at any given\n      // time to allow network traffic to continue so we don't end up disconnecting from peers\n      //fc::time_point start_time = fc::time_point::now();\n      //fc::time_point when_we_should_yield = start_time + fc::seconds(1);\n\n      bool block_processed_this_iteration;\n      unsigned blocks_processed = 0;\n\n      std::set<peer_connection_ptr> peers_with_newly_empty_item_lists;\n      std::set<peer_connection_ptr> peers_we_need_to_sync_to;\n      std::map<peer_connection_ptr, fc::oexception> peers_with_rejected_block;\n\n      do\n      {\n        std::copy(std::make_move_iterator(_new_received_sync_items.begin()),\n                  std::make_move_iterator(_new_received_sync_items.end()),\n                  std::front_inserter(_received_sync_items));\n        _new_received_sync_items.clear();\n        dlog(\"currently ${count} sync items to consider\", (\"count\", _received_sync_items.size()));\n\n        block_processed_this_iteration = false;\n        for (auto received_block_iter = _received_sync_items.begin();\n             received_block_iter != _received_sync_items.end();\n             ++received_block_iter)\n        {\n\n          // find out if this block is the next block on the active chain or one of the forks\n          bool potential_first_block = false;\n          for (const peer_connection_ptr& peer : _active_connections)\n          {\n            ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections\n            if (!peer->ids_of_items_to_get.empty() &&\n                peer->ids_of_items_to_get.front() == received_block_iter->block_id)\n            {\n              potential_first_block = true;\n              peer->ids_of_items_to_get.pop_front();\n              peer->ids_of_items_being_processed.insert(received_block_iter->block_id);\n            }\n          }\n\n          // if it is, process it, remove it from all sync peers lists\n          if (potential_first_block)\n          {\n            // we can get into an interesting situation near the end of synchronization.  We can be in\n            // sync with one peer who is sending us the last block on the chain via a regular inventory\n            // message, while at the same time still be synchronizing with a peer who is sending us the\n            // block through the sync mechanism.  Further, we must request both blocks because\n            // we don't know they're the same (for the peer in normal operation, it has only told us the\n            // message id, for the peer in the sync case we only known the block_id).\n            if (std::find(_most_recent_blocks_accepted.begin(), _most_recent_blocks_accepted.end(),\n                          received_block_iter->block_id) == _most_recent_blocks_accepted.end())\n            {\n              graphene::net::block_message block_message_to_process = *received_block_iter;\n              _received_sync_items.erase(received_block_iter);\n              _handle_message_calls_in_progress.emplace_back(fc::async([this, block_message_to_process](){\n                send_sync_block_to_node_delegate(block_message_to_process);\n              }, \"send_sync_block_to_node_delegate\"));\n              ++blocks_processed;\n              block_processed_this_iteration = true;\n            }\n            else\n            {\n              dlog(\"Already received and accepted this block (presumably through normal inventory mechanism), treating it as accepted\");\n              std::vector< peer_connection_ptr > peers_needing_next_batch;\n              for (const peer_connection_ptr& peer : _active_connections)\n              {\n                auto items_being_processed_iter = peer->ids_of_items_being_processed.find(received_block_iter->block_id);\n                if (items_being_processed_iter != peer->ids_of_items_being_processed.end())\n                {\n                  peer->ids_of_items_being_processed.erase(items_being_processed_iter);\n                  dlog(\"Removed item from ${endpoint}'s list of items being processed, still processing ${len} blocks\",\n                       (\"endpoint\", peer->get_remote_endpoint())(\"len\", peer->ids_of_items_being_processed.size()));\n\n                  // if we just processed the last item in our list from this peer, we will want to\n                  // send another request to find out if we are now in sync (this is normally handled in\n                  // send_sync_block_to_node_delegate)\n                  if (peer->ids_of_items_to_get.empty() &&\n                      peer->number_of_unfetched_item_ids == 0 &&\n                      peer->ids_of_items_being_processed.empty())\n                  {\n                    dlog(\"We received last item in our list for peer ${endpoint}, setup to do a sync check\", (\"endpoint\", peer->get_remote_endpoint()));\n                    peers_needing_next_batch.push_back( peer );\n                  }\n                }\n              }\n              for( const peer_connection_ptr& peer : peers_needing_next_batch )\n                fetch_next_batch_of_item_ids_from_peer(peer.get());\n            }\n\n            break; // start iterating _received_sync_items from the beginning\n          } // end if potential_first_block\n        } // end for each block in _received_sync_items\n\n        if (_handle_message_calls_in_progress.size() >= _maximum_number_of_blocks_to_handle_at_one_time)\n        {\n          dlog(\"stopping processing sync block backlog because we have ${count} blocks in progress\",\n               (\"count\", _handle_message_calls_in_progress.size()));\n          //ulog(\"stopping processing sync block backlog because we have ${count} blocks in progress, total on hand: ${received}\",\n          //     (\"count\", _handle_message_calls_in_progress.size())(\"received\", _received_sync_items.size()));\n          if (_received_sync_items.size() >= _maximum_number_of_sync_blocks_to_prefetch)\n            _suspend_fetching_sync_blocks = true;\n          break;\n        }\n      } while (block_processed_this_iteration);\n\n      dlog(\"leaving process_backlog_of_sync_blocks, ${count} processed\", (\"count\", blocks_processed));\n\n      if (!_suspend_fetching_sync_blocks)\n        trigger_fetch_sync_items_loop();\n    }\n\n    void node_impl::trigger_process_backlog_of_sync_blocks()\n    {\n      if (!_node_is_shutting_down &&\n          (!_process_backlog_of_sync_blocks_done.valid() || _process_backlog_of_sync_blocks_done.ready()))\n        _process_backlog_of_sync_blocks_done = fc::async([=](){ process_backlog_of_sync_blocks(); }, \"process_backlog_of_sync_blocks\");\n    }\n\n    void node_impl::process_block_during_sync( peer_connection* originating_peer,\n                                               const graphene::net::block_message& block_message_to_process, const message_hash_type& message_hash )\n    {\n      VERIFY_CORRECT_THREAD();\n      dlog( \"received a sync block from peer ${endpoint}\", (\"endpoint\", originating_peer->get_remote_endpoint() ) );\n\n      // add it to the front of _received_sync_items, then process _received_sync_items to try to\n      // pass as many messages as possible to the client.\n      _new_received_sync_items.push_front( block_message_to_process );\n      trigger_process_backlog_of_sync_blocks();\n    }\n\n    void node_impl::process_block_during_normal_operation( peer_connection* originating_peer,\n                                                           const graphene::net::block_message& block_message_to_process,\n                                                           const message_hash_type& message_hash )\n    {\n      fc::time_point message_receive_time = fc::time_point::now();\n\n      dlog( \"received a block from peer ${endpoint}, passing it to client\", (\"endpoint\", originating_peer->get_remote_endpoint() ) );\n      std::set<peer_connection_ptr> peers_to_disconnect;\n      std::string disconnect_reason;\n      fc::oexception disconnect_exception;\n      fc::oexception restart_sync_exception;\n      try\n      {\n        // we can get into an intersting situation near the end of synchronization.  We can be in\n        // sync with one peer who is sending us the last block on the chain via a regular inventory\n        // message, while at the same time still be synchronizing with a peer who is sending us the\n        // block through the sync mechanism.  Further, we must request both blocks because\n        // we don't know they're the same (for the peer in normal operation, it has only told us the\n        // message id, for the peer in the sync case we only known the block_id).\n        fc::time_point message_validated_time;\n        if (std::find(_most_recent_blocks_accepted.begin(), _most_recent_blocks_accepted.end(),\n                      block_message_to_process.block_id) == _most_recent_blocks_accepted.end())\n        {\n          std::vector<fc::uint160_t> contained_transaction_message_ids;\n          _delegate->handle_block(block_message_to_process, false, contained_transaction_message_ids);\n          message_validated_time = fc::time_point::now();\n          ilog(\"Successfully pushed block ${num} (id:${id})\",\n                (\"num\", block_message_to_process.block.block_num())\n                (\"id\", block_message_to_process.block_id));\n          _most_recent_blocks_accepted.push_back(block_message_to_process.block_id);\n\n          bool new_transaction_discovered = false;\n          for (const item_hash_t& transaction_message_hash : contained_transaction_message_ids)\n          {\n            /*size_t items_erased =*/ _items_to_fetch.get<item_id_index>().erase(item_id(trx_message_type, transaction_message_hash));\n            // there are two ways we could behave here: we could either act as if we received\n            // the transaction outside the block and offer it to our peers, or we could just\n            // forget about it (we would still advertise this block to our peers so they should\n            // get the transaction through that mechanism).\n            // We take the second approach, bring in the next if block to try the first approach\n            //if (items_erased)\n            //{\n            //  new_transaction_discovered = true;\n            //  _new_inventory.insert(item_id(trx_message_type, transaction_message_hash));\n            //}\n          }\n          if (new_transaction_discovered)\n            trigger_advertise_inventory_loop();\n        }\n        else\n          dlog( \"Already received and accepted this block (presumably through sync mechanism), treating it as accepted\" );\n\n        dlog( \"client validated the block, advertising it to other peers\" );\n\n        item_id block_message_item_id(core_message_type_enum::block_message_type, message_hash);\n        uint32_t block_number = block_message_to_process.block.block_num();\n        fc::time_point_sec block_time = block_message_to_process.block.timestamp;\n\n        for (const peer_connection_ptr& peer : _active_connections)\n        {\n          ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections\n\n          auto iter = peer->inventory_peer_advertised_to_us.find(block_message_item_id);\n          if (iter != peer->inventory_peer_advertised_to_us.end())\n          {\n            // this peer offered us the item.  It will eventually expire from the peer's\n            // inventory_peer_advertised_to_us list after some time has passed (currently 2 minutes).\n            // For now, it will remain there, which will prevent us from offering the peer this\n            // block back when we rebroadcast the block below\n            peer->last_block_delegate_has_seen = block_message_to_process.block_id;\n            peer->last_block_time_delegate_has_seen = block_time;\n          }\n          peer->clear_old_inventory();\n        }\n        message_propagation_data propagation_data{message_receive_time, message_validated_time, originating_peer->node_id};\n        broadcast( block_message_to_process, propagation_data );\n        _message_cache.block_accepted();\n\n        if (is_hard_fork_block(block_number))\n        {\n          // we just pushed a hard fork block.  Find out if any of our peers are running clients\n          // that will be unable to process future blocks\n          for (const peer_connection_ptr& peer : _active_connections)\n          {\n            if (peer->last_known_fork_block_number != 0)\n            {\n              uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(peer->last_known_fork_block_number);\n              if (next_fork_block_number != 0 &&\n                  next_fork_block_number <= block_number)\n              {\n                peers_to_disconnect.insert(peer);\n#ifdef ENABLE_DEBUG_ULOGS\n                ulog(\"Disconnecting from peer because their version is too old.  Their version date: ${date}\", (\"date\", peer->graphene_git_revision_unix_timestamp));\n#endif\n              }\n            }\n          }\n          if (!peers_to_disconnect.empty())\n          {\n            std::ostringstream disconnect_reason_stream;\n            disconnect_reason_stream << \"You need to upgrade your client due to hard fork at block \" << block_number;\n            disconnect_reason = disconnect_reason_stream.str();\n            disconnect_exception = fc::exception(FC_LOG_MESSAGE(error, \"You need to upgrade your client due to hard fork at block ${block_number}\",\n                                                                (\"block_number\", block_number)));\n          }\n        }\n      }\n      catch (const fc::canceled_exception&)\n      {\n        throw;\n      }\n      catch (const unlinkable_block_exception& e) \n      {\n        restart_sync_exception = e;\n      }\n      catch (const fc::exception& e)\n      {\n        // client rejected the block.  Disconnect the client and any other clients that offered us this block\n        auto block_num = block_message_to_process.block.block_num();\n        wlog(\"Failed to push block ${num} (id:${id}), client rejected block sent by peer: ${e}\",\n              (\"num\", block_num)\n              (\"id\", block_message_to_process.block_id)\n              (\"e\",e));\n\n        if( e.code() == block_timestamp_in_future_exception::code_enum::code_value )\n        {\n           disconnect_exception = block_timestamp_in_future_exception( FC_LOG_MESSAGE( warn, \"\",\n                (\"block_header\", static_cast<graphene::protocol::block_header>(block_message_to_process.block))\n                (\"block_num\", block_num)\n                (\"block_id\", block_message_to_process.block_id) ) );\n        }\n        else\n           disconnect_exception = e;\n        disconnect_reason = \"You offered me a block that I have deemed to be invalid\";\n\n        peers_to_disconnect.insert( originating_peer->shared_from_this() );\n        for (const peer_connection_ptr& peer : _active_connections)\n          if (!peer->ids_of_items_to_get.empty() && peer->ids_of_items_to_get.front() == block_message_to_process.block_id)\n            peers_to_disconnect.insert(peer);\n      }\n\n      if (restart_sync_exception)\n      {\n        wlog(\"Peer ${peer} sent me a block that didn't link to our blockchain.  Restarting sync mode with them to get the missing block. \"\n             \"Error pushing block was: ${e}\",\n             (\"peer\", originating_peer->get_remote_endpoint())\n             (\"e\", *restart_sync_exception));\n        start_synchronizing_with_peer(originating_peer->shared_from_this());\n      }\n\n      for (const peer_connection_ptr& peer : peers_to_disconnect)\n      {\n        wlog(\"disconnecting client ${endpoint} because it offered us the rejected block\", (\"endpoint\", peer->get_remote_endpoint()));\n        disconnect_from_peer(peer.get(), disconnect_reason, true, *disconnect_exception);\n      }\n    }\n    void node_impl::process_block_message(peer_connection* originating_peer,\n                                          const message& message_to_process,\n                                          const message_hash_type& message_hash)\n    {\n      VERIFY_CORRECT_THREAD();\n      // find out whether we requested this item while we were synchronizing or during normal operation\n      // (it's possible that we request an item during normal operation and then get kicked into sync\n      // mode before we receive and process the item.  In that case, we should process the item as a normal\n      // item to avoid confusing the sync code)\n      graphene::net::block_message block_message_to_process(message_to_process.as<graphene::net::block_message>());\n      auto item_iter = originating_peer->items_requested_from_peer.find(item_id(graphene::net::block_message_type, message_hash));\n      if (item_iter != originating_peer->items_requested_from_peer.end())\n      {\n        originating_peer->items_requested_from_peer.erase(item_iter);\n        process_block_during_normal_operation(originating_peer, block_message_to_process, message_hash);\n        if (originating_peer->idle())\n          trigger_fetch_items_loop();\n        return;\n      }\n      else\n      {\n        // not during normal operation.  see if we requested it during sync\n        auto sync_item_iter = originating_peer->sync_items_requested_from_peer.find( block_message_to_process.block_id);\n        if (sync_item_iter != originating_peer->sync_items_requested_from_peer.end())\n        {\n          originating_peer->sync_items_requested_from_peer.erase(sync_item_iter);\n          // if exceptions are throw here after removing the sync item from the list (above),\n          // it could leave our sync in a stalled state.  Wrap a try/catch around the rest\n          // of the function so we can log if this ever happens.\n          try\n          {\n            originating_peer->last_sync_item_received_time = fc::time_point::now();\n            _active_sync_requests.erase(block_message_to_process.block_id);\n            process_block_during_sync(originating_peer, block_message_to_process, message_hash);\n            if (originating_peer->idle())\n            {\n              // we have finished fetching a batch of items, so we either need to grab another batch of items\n              // or we need to get another list of item ids.\n              if (originating_peer->number_of_unfetched_item_ids > 0 &&\n                  originating_peer->ids_of_items_to_get.size() < GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH)\n                fetch_next_batch_of_item_ids_from_peer(originating_peer);\n              else\n                trigger_fetch_sync_items_loop();\n            }\n            return;\n          }\n          catch (const fc::canceled_exception& e)\n          {\n            throw;\n          }\n          catch (const fc::exception& e)\n          {\n            elog(\"Caught unexpected exception: ${e}\", (\"e\", e));\n            assert(false && \"exceptions not expected here\");\n          }\n          catch (const std::exception& e)\n          {\n            elog(\"Caught unexpected exception: ${e}\", (\"e\", e.what()));\n            assert(false && \"exceptions not expected here\");\n          }\n          catch (...)\n          {\n            elog(\"Caught unexpected exception, could break sync operation\");\n          }\n        }\n      }\n\n      // if we get here, we didn't request the message, we must have a misbehaving peer\n      wlog(\"received a block ${block_id} I didn't ask for from peer ${endpoint}, disconnecting from peer\",\n           (\"endpoint\", originating_peer->get_remote_endpoint())\n           (\"block_id\", block_message_to_process.block_id));\n      fc::exception detailed_error(FC_LOG_MESSAGE(error, \"You sent me a block that I didn't ask for, block_id: ${block_id}\",\n                                                  (\"block_id\", block_message_to_process.block_id)\n                                                  (\"graphene_git_revision_sha\", originating_peer->graphene_git_revision_sha)\n                                                  (\"graphene_git_revision_unix_timestamp\", originating_peer->graphene_git_revision_unix_timestamp)\n                                                  (\"fc_git_revision_sha\", originating_peer->fc_git_revision_sha)\n                                                  (\"fc_git_revision_unix_timestamp\", originating_peer->fc_git_revision_unix_timestamp)));\n      disconnect_from_peer(originating_peer, \"You sent me a block that I didn't ask for\", true, detailed_error);\n    }\n\n    void node_impl::on_current_time_request_message(peer_connection* originating_peer,\n                                                    const current_time_request_message& current_time_request_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::time_point request_received_time(fc::time_point::now());\n      current_time_reply_message reply(current_time_request_message_received.request_sent_time,\n                                       request_received_time);\n      originating_peer->send_message(reply, offsetof(current_time_reply_message, reply_transmitted_time));\n    }\n\n    void node_impl::on_current_time_reply_message(peer_connection* originating_peer,\n                                                  const current_time_reply_message& current_time_reply_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::time_point reply_received_time = fc::time_point::now();\n      originating_peer->clock_offset = fc::microseconds(((current_time_reply_message_received.request_received_time - current_time_reply_message_received.request_sent_time) +\n                                                         (current_time_reply_message_received.reply_transmitted_time - reply_received_time)).count() / 2);\n      originating_peer->round_trip_delay = (reply_received_time - current_time_reply_message_received.request_sent_time) -\n                                           (current_time_reply_message_received.reply_transmitted_time - current_time_reply_message_received.request_received_time);\n    }\n\n    void node_impl::forward_firewall_check_to_next_available_peer(firewall_check_state_data* firewall_check_state)\n    {\n      for (const peer_connection_ptr& peer : _active_connections)\n      {\n        if (firewall_check_state->expected_node_id != peer->node_id && // it's not the node who is asking us to test\n            !peer->firewall_check_state && // the peer isn't already performing a check for another node\n            firewall_check_state->nodes_already_tested.find(peer->node_id) == firewall_check_state->nodes_already_tested.end() &&\n            peer->core_protocol_version >= 106)\n        {\n          wlog(\"forwarding firewall check for node ${to_check} to peer ${checker}\",\n               (\"to_check\", firewall_check_state->endpoint_to_test)\n               (\"checker\", peer->get_remote_endpoint()));\n          firewall_check_state->nodes_already_tested.insert(peer->node_id);\n          peer->firewall_check_state = firewall_check_state;\n          check_firewall_message check_request;\n          check_request.endpoint_to_check = firewall_check_state->endpoint_to_test;\n          check_request.node_id = firewall_check_state->expected_node_id;\n          peer->send_message(check_request);\n          return;\n        }\n      }\n      wlog(\"Unable to forward firewall check for node ${to_check} to any other peers, returning 'unable'\",\n           (\"to_check\", firewall_check_state->endpoint_to_test));\n\n      peer_connection_ptr originating_peer = get_peer_by_node_id(firewall_check_state->expected_node_id);\n      if (originating_peer)\n      {\n        check_firewall_reply_message reply;\n        reply.node_id = firewall_check_state->expected_node_id;\n        reply.endpoint_checked = firewall_check_state->endpoint_to_test;\n        reply.result = firewall_check_result::unable_to_check;\n        originating_peer->send_message(reply);\n      }\n      delete firewall_check_state;\n    }\n\n    void node_impl::on_check_firewall_message(peer_connection* originating_peer,\n                                              const check_firewall_message& check_firewall_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n\n      if (check_firewall_message_received.node_id == node_id_t() &&\n          check_firewall_message_received.endpoint_to_check == fc::ip::endpoint())\n      {\n        // originating_peer is asking us to test whether it is firewalled\n        // we're not going to try to connect back to the originating peer directly,\n        // instead, we're going to coordinate requests by asking some of our peers\n        // to try to connect to the originating peer, and relay the results back\n        wlog(\"Peer ${peer} wants us to check whether it is firewalled\", (\"peer\", originating_peer->get_remote_endpoint()));\n        firewall_check_state_data* firewall_check_state = new firewall_check_state_data;\n        // if they are using the same inbound and outbound port, try connecting to their outbound endpoint.\n        // if they are using a different inbound port, use their outbound address but the inbound port they reported\n        fc::ip::endpoint endpoint_to_check = originating_peer->get_socket().remote_endpoint();\n        if (originating_peer->inbound_port != originating_peer->outbound_port)\n          endpoint_to_check = fc::ip::endpoint(endpoint_to_check.get_address(), originating_peer->inbound_port);\n        firewall_check_state->endpoint_to_test = endpoint_to_check;\n        firewall_check_state->expected_node_id = originating_peer->node_id;\n        firewall_check_state->requesting_peer = originating_peer->node_id;\n\n        forward_firewall_check_to_next_available_peer(firewall_check_state);\n      }\n      else\n      {\n        // we're being asked to check another node\n        // first, find out if we're currently connected to that node.  If we are, we\n        // can't perform the test\n        if (is_already_connected_to_id(check_firewall_message_received.node_id) ||\n            is_connection_to_endpoint_in_progress(check_firewall_message_received.endpoint_to_check))\n        {\n          check_firewall_reply_message reply;\n          reply.node_id = check_firewall_message_received.node_id;\n          reply.endpoint_checked = check_firewall_message_received.endpoint_to_check;\n          reply.result = firewall_check_result::unable_to_check;\n        }\n        else\n        {\n          // we're not connected to them, so we need to set up a connection to them\n          // to test.\n          peer_connection_ptr peer_for_testing(peer_connection::make_shared(this));\n          peer_for_testing->firewall_check_state = new firewall_check_state_data;\n          peer_for_testing->firewall_check_state->endpoint_to_test = check_firewall_message_received.endpoint_to_check;\n          peer_for_testing->firewall_check_state->expected_node_id = check_firewall_message_received.node_id;\n          peer_for_testing->firewall_check_state->requesting_peer = originating_peer->node_id;\n          peer_for_testing->set_remote_endpoint(check_firewall_message_received.endpoint_to_check);\n          initiate_connect_to(peer_for_testing);\n        }\n      }\n    }\n\n    void node_impl::on_check_firewall_reply_message(peer_connection* originating_peer,\n                                                    const check_firewall_reply_message& check_firewall_reply_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n\n      if (originating_peer->firewall_check_state &&\n          originating_peer->firewall_check_state->requesting_peer != node_id_t())\n      {\n        // then this is a peer that is helping us check the firewalled state of one of our other peers\n        // and they're reporting back\n        // if they got a result, return it to the original peer.  if they were unable to check,\n        // we'll try another peer.\n        wlog(\"Peer ${reporter} reports firewall check status ${status} for ${peer}\",\n             (\"reporter\", originating_peer->get_remote_endpoint())\n             (\"status\", check_firewall_reply_message_received.result)\n             (\"peer\", check_firewall_reply_message_received.endpoint_checked));\n\n        if (check_firewall_reply_message_received.result == firewall_check_result::unable_to_connect ||\n            check_firewall_reply_message_received.result == firewall_check_result::connection_successful)\n        {\n          peer_connection_ptr original_peer = get_peer_by_node_id(originating_peer->firewall_check_state->requesting_peer);\n          if (original_peer)\n          {\n            if (check_firewall_reply_message_received.result == firewall_check_result::connection_successful)\n            {\n              // if we previously thought this peer was firewalled, mark them as not firewalled\n              if (original_peer->is_firewalled != firewalled_state::not_firewalled)\n              {\n\n                original_peer->is_firewalled = firewalled_state::not_firewalled;\n                // there should be no old entry if we thought they were firewalled, so just create a new one\n                fc::optional<fc::ip::endpoint> inbound_endpoint = originating_peer->get_endpoint_for_connecting();\n                if (inbound_endpoint)\n                {\n                  potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(*inbound_endpoint);\n                  updated_peer_record.last_seen_time = fc::time_point::now();\n                  _potential_peer_db.update_entry(updated_peer_record);\n                }\n              }\n            }\n            original_peer->send_message(check_firewall_reply_message_received);\n          }\n          delete originating_peer->firewall_check_state;\n          originating_peer->firewall_check_state = nullptr;\n        }\n        else\n        {\n          // they were unable to check for us, ask another peer\n          firewall_check_state_data* firewall_check_state = originating_peer->firewall_check_state;\n          originating_peer->firewall_check_state = nullptr;\n          forward_firewall_check_to_next_available_peer(firewall_check_state);\n        }\n      }\n      else if (originating_peer->firewall_check_state)\n      {\n        // this is a reply to a firewall check we initiated.\n        wlog(\"Firewall check we initiated has returned with result: ${result}, endpoint = ${endpoint}\",\n             (\"result\", check_firewall_reply_message_received.result)\n             (\"endpoint\", check_firewall_reply_message_received.endpoint_checked));\n        if (check_firewall_reply_message_received.result == firewall_check_result::connection_successful)\n        {\n          _is_firewalled = firewalled_state::not_firewalled;\n          _publicly_visible_listening_endpoint = check_firewall_reply_message_received.endpoint_checked;\n        }\n        else if (check_firewall_reply_message_received.result == firewall_check_result::unable_to_connect)\n        {\n          _is_firewalled = firewalled_state::firewalled;\n          _publicly_visible_listening_endpoint = fc::optional<fc::ip::endpoint>();\n        }\n        delete originating_peer->firewall_check_state;\n        originating_peer->firewall_check_state = nullptr;\n      }\n      else\n      {\n        wlog(\"Received a firewall check reply to a request I never sent\");\n      }\n\n    }\n\n    void node_impl::on_get_current_connections_request_message(peer_connection* originating_peer,\n                                                               const get_current_connections_request_message& get_current_connections_request_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n      get_current_connections_reply_message reply;\n\n      if (!_average_network_read_speed_minutes.empty())\n      {\n        reply.upload_rate_one_minute = _average_network_write_speed_minutes.back();\n        reply.download_rate_one_minute = _average_network_read_speed_minutes.back();\n\n        size_t minutes_to_average = std::min(_average_network_write_speed_minutes.size(), (size_t)15);\n        boost::circular_buffer<uint32_t>::iterator start_iter = _average_network_write_speed_minutes.end() - minutes_to_average;\n        reply.upload_rate_fifteen_minutes = std::accumulate(start_iter, _average_network_write_speed_minutes.end(), 0) / (uint32_t)minutes_to_average;\n        start_iter = _average_network_read_speed_minutes.end() - minutes_to_average;\n        reply.download_rate_fifteen_minutes = std::accumulate(start_iter, _average_network_read_speed_minutes.end(), 0) / (uint32_t)minutes_to_average;\n\n        minutes_to_average = std::min(_average_network_write_speed_minutes.size(), (size_t)60);\n        start_iter = _average_network_write_speed_minutes.end() - minutes_to_average;\n        reply.upload_rate_one_hour = std::accumulate(start_iter, _average_network_write_speed_minutes.end(), 0) / (uint32_t)minutes_to_average;\n        start_iter = _average_network_read_speed_minutes.end() - minutes_to_average;\n        reply.download_rate_one_hour = std::accumulate(start_iter, _average_network_read_speed_minutes.end(), 0) / (uint32_t)minutes_to_average;\n      }\n\n      fc::time_point now = fc::time_point::now();\n      for (const peer_connection_ptr& peer : _active_connections)\n      {\n        ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections\n\n        current_connection_data data_for_this_peer;\n        data_for_this_peer.connection_duration = now.sec_since_epoch() - peer->connection_initiation_time.sec_since_epoch();\n        if (peer->get_remote_endpoint()) // should always be set for anyone we're actively connected to\n          data_for_this_peer.remote_endpoint = *peer->get_remote_endpoint();\n        data_for_this_peer.clock_offset = peer->clock_offset;\n        data_for_this_peer.round_trip_delay = peer->round_trip_delay;\n        data_for_this_peer.node_id = peer->node_id;\n        data_for_this_peer.connection_direction = peer->direction;\n        data_for_this_peer.firewalled = peer->is_firewalled;\n        fc::mutable_variant_object user_data;\n        if (peer->graphene_git_revision_sha)\n          user_data[\"graphene_git_revision_sha\"] = *peer->graphene_git_revision_sha;\n        if (peer->graphene_git_revision_unix_timestamp)\n          user_data[\"graphene_git_revision_unix_timestamp\"] = *peer->graphene_git_revision_unix_timestamp;\n        if (peer->fc_git_revision_sha)\n          user_data[\"fc_git_revision_sha\"] = *peer->fc_git_revision_sha;\n        if (peer->fc_git_revision_unix_timestamp)\n          user_data[\"fc_git_revision_unix_timestamp\"] = *peer->fc_git_revision_unix_timestamp;\n        if (peer->platform)\n          user_data[\"platform\"] = *peer->platform;\n        if (peer->bitness)\n          user_data[\"bitness\"] = *peer->bitness;\n        user_data[\"user_agent\"] = peer->user_agent;\n\n        user_data[\"last_known_block_hash\"] = fc::variant( peer->last_block_delegate_has_seen, 1 );\n        user_data[\"last_known_block_number\"] = _delegate->get_block_number(peer->last_block_delegate_has_seen);\n        user_data[\"last_known_block_time\"] = peer->last_block_time_delegate_has_seen;\n\n        data_for_this_peer.user_data = user_data;\n        reply.current_connections.emplace_back(data_for_this_peer);\n      }\n      originating_peer->send_message(reply);\n    }\n\n    void node_impl::on_get_current_connections_reply_message(peer_connection* originating_peer,\n          const get_current_connections_reply_message& get_current_connections_reply_message_received)\n    {\n      VERIFY_CORRECT_THREAD();\n    }\n\n\n    // this handles any message we get that doesn't require any special processing.\n    // currently, this is any message other than block messages and p2p-specific\n    // messages.  (transaction messages would be handled here, for example)\n    // this just passes the message to the client, and does the bookkeeping\n    // related to requesting and rebroadcasting the message.\n    void node_impl::process_ordinary_message( peer_connection* originating_peer,\n                                              const message& message_to_process,\n                                              const message_hash_type& message_hash )\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::time_point message_receive_time = fc::time_point::now();\n\n      // only process it if we asked for it\n      auto iter = originating_peer->items_requested_from_peer.find(\n                        item_id(message_to_process.msg_type.value(), message_hash) );\n      if( iter == originating_peer->items_requested_from_peer.end() )\n      {\n        wlog( \"received a message I didn't ask for from peer ${endpoint}, disconnecting from peer\",\n             ( \"endpoint\", originating_peer->get_remote_endpoint() ) );\n        fc::exception detailed_error( FC_LOG_MESSAGE(error,\n                            \"You sent me a message that I didn't ask for, message_hash: ${message_hash}\",\n                            ( \"message_hash\", message_hash ) ) );\n        disconnect_from_peer( originating_peer, \"You sent me a message that I didn't request\", true, detailed_error );\n        return;\n      }\n      else\n      {\n        originating_peer->items_requested_from_peer.erase( iter );\n        if (originating_peer->idle())\n          trigger_fetch_items_loop();\n\n        // Next: have the delegate process the message\n        fc::time_point message_validated_time;\n        try\n        {\n          if (message_to_process.msg_type.value() == trx_message_type)\n          {\n            trx_message transaction_message_to_process = message_to_process.as<trx_message>();\n            dlog( \"passing message containing transaction ${trx} to client\",\n                  (\"trx\", transaction_message_to_process.trx.id()) );\n            _delegate->handle_transaction(transaction_message_to_process);\n          }\n          else\n            _delegate->handle_message( message_to_process );\n          message_validated_time = fc::time_point::now();\n        }\n        catch ( const fc::canceled_exception& )\n        {\n          throw;\n        }\n        catch ( const fc::exception& e )\n        {\n          switch( e.code() )\n          {\n          // log common exceptions in debug level\n          case graphene::chain::duplicate_transaction::code_enum::code_value :\n          case graphene::chain::limit_order_create_kill_unfilled::code_enum::code_value :\n          case graphene::chain::limit_order_create_market_not_whitelisted::code_enum::code_value :\n          case graphene::chain::limit_order_create_market_blacklisted::code_enum::code_value :\n          case graphene::chain::limit_order_create_selling_asset_unauthorized::code_enum::code_value :\n          case graphene::chain::limit_order_create_receiving_asset_unauthorized::code_enum::code_value :\n          case graphene::chain::limit_order_create_insufficient_balance::code_enum::code_value :\n          case graphene::chain::limit_order_cancel_nonexist_order::code_enum::code_value :\n          case graphene::chain::limit_order_cancel_owner_mismatch::code_enum::code_value :\n             dlog( \"client rejected message sent by peer ${peer}, ${e}\",\n                   (\"peer\", originating_peer->get_remote_endpoint() )(\"e\", e) );\n             break;\n          // log rarer exceptions in warn level\n          default:\n             wlog( \"client rejected message sent by peer ${peer}, ${e}\",\n                   (\"peer\", originating_peer->get_remote_endpoint() )(\"e\", e) );\n             break;\n          }\n          // record it so we don't try to fetch this item again\n          _recently_failed_items.insert( peer_connection::timestamped_item_id(\n                item_id( message_to_process.msg_type.value(), message_hash ), fc::time_point::now() ) );\n          return;\n        }\n\n        // finally, if the delegate validated the message, broadcast it to our other peers\n        message_propagation_data propagation_data { message_receive_time, message_validated_time,\n                                                    originating_peer->node_id };\n        broadcast( message_to_process, propagation_data );\n      }\n    }\n\n    void node_impl::start_synchronizing_with_peer( const peer_connection_ptr& peer )\n    {\n      VERIFY_CORRECT_THREAD();\n      peer->ids_of_items_to_get.clear();\n      peer->number_of_unfetched_item_ids = 0;\n      peer->we_need_sync_items_from_peer = true;\n      peer->last_block_delegate_has_seen = item_hash_t();\n      peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hash_t());\n      peer->inhibit_fetching_sync_blocks = false;\n      fetch_next_batch_of_item_ids_from_peer( peer.get() );\n    }\n\n    void node_impl::start_synchronizing()\n    {\n      for( const peer_connection_ptr& peer : _active_connections )\n        start_synchronizing_with_peer( peer );\n    }\n\n    void node_impl::new_peer_just_added( const peer_connection_ptr& peer )\n    {\n      VERIFY_CORRECT_THREAD();\n      peer->send_message(current_time_request_message(),\n                         offsetof(current_time_request_message, request_sent_time));\n      start_synchronizing_with_peer( peer );\n      if( _active_connections.size() != _last_reported_number_of_connections )\n      {\n        _last_reported_number_of_connections = (uint32_t)_active_connections.size();\n        _delegate->connection_count_changed( _last_reported_number_of_connections );\n      }\n    }\n\n    void node_impl::close()\n    {\n      VERIFY_CORRECT_THREAD();\n\n      try\n      {\n        _potential_peer_db.close();\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while closing P2P peer database, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while closing P2P peer database, ignoring\" );\n      }\n\n      // First, stop accepting incoming network connections\n      try\n      {\n        _tcp_server.close();\n        dlog(\"P2P TCP server closed\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while closing P2P TCP server, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while closing P2P TCP server, ignoring\" );\n      }\n\n      try\n      {\n        _accept_loop_complete.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"P2P accept loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating P2P accept loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating P2P accept loop, ignoring\" );\n      }\n\n      // terminate all of our long-running loops (these run continuously instead of rescheduling themselves)\n      try\n      {\n        _p2p_network_connect_loop_done.cancel(\"node_impl::close()\");\n        // cancel() is currently broken, so we need to wake up the task to allow it to finish\n        trigger_p2p_network_connect_loop();\n        _p2p_network_connect_loop_done.wait();\n        dlog(\"P2P connect loop terminated\");\n      }\n      catch ( const fc::canceled_exception& )\n      {\n        dlog(\"P2P connect loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating P2P connect loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating P2P connect loop, ignoring\" );\n      }\n\n      try\n      {\n        _process_backlog_of_sync_blocks_done.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"Process backlog of sync items task terminated\");\n      }\n      catch ( const fc::canceled_exception& )\n      {\n        dlog(\"Process backlog of sync items task terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Process backlog of sync items task, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Process backlog of sync items task, ignoring\" );\n      }\n\n      unsigned handle_message_call_count = 0;\n      while( true )\n      {\n        auto it = _handle_message_calls_in_progress.begin();\n        if( it == _handle_message_calls_in_progress.end() )\n           break;\n        if( it->ready() || it->error() || it->canceled() )\n        {\n           _handle_message_calls_in_progress.erase( it );\n           continue;\n        }\n        ++handle_message_call_count;\n        try\n        {\n          it->cancel_and_wait(\"node_impl::close()\");\n          dlog(\"handle_message call #${count} task terminated\", (\"count\", handle_message_call_count));\n        }\n        catch ( const fc::canceled_exception& )\n        {\n          dlog(\"handle_message call #${count} task terminated\", (\"count\", handle_message_call_count));\n        }\n        catch ( const fc::exception& e )\n        {\n          wlog(\"Exception thrown while terminating handle_message call #${count} task, ignoring: ${e}\", (\"e\", e)(\"count\", handle_message_call_count));\n        }\n        catch (...)\n        {\n          wlog(\"Exception thrown while terminating handle_message call #${count} task, ignoring\",(\"count\", handle_message_call_count));\n        }\n      }\n\n      try\n      {\n        _fetch_sync_items_loop_done.cancel(\"node_impl::close()\");\n        // cancel() is currently broken, so we need to wake up the task to allow it to finish\n        trigger_fetch_sync_items_loop();\n        _fetch_sync_items_loop_done.wait();\n        dlog(\"Fetch sync items loop terminated\");\n      }\n      catch ( const fc::canceled_exception& )\n      {\n        dlog(\"Fetch sync items loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Fetch sync items loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Fetch sync items loop, ignoring\" );\n      }\n\n      try\n      {\n        _fetch_item_loop_done.cancel(\"node_impl::close()\");\n        // cancel() is currently broken, so we need to wake up the task to allow it to finish\n        trigger_fetch_items_loop();\n        _fetch_item_loop_done.wait();\n        dlog(\"Fetch items loop terminated\");\n      }\n      catch ( const fc::canceled_exception& )\n      {\n        dlog(\"Fetch items loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Fetch items loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Fetch items loop, ignoring\" );\n      }\n\n      try\n      {\n        _advertise_inventory_loop_done.cancel(\"node_impl::close()\");\n        // cancel() is currently broken, so we need to wake up the task to allow it to finish\n        trigger_advertise_inventory_loop();\n        _advertise_inventory_loop_done.wait();\n        dlog(\"Advertise inventory loop terminated\");\n      }\n      catch ( const fc::canceled_exception& )\n      {\n        dlog(\"Advertise inventory loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Advertise inventory loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Advertise inventory loop, ignoring\" );\n      }\n\n\n      // Next, terminate our existing connections.  First, close all of the connections nicely.\n      // This will close the sockets and may result in calls to our \"on_connection_closing\"\n      // method to inform us that the connection really closed (or may not if we manage to cancel\n      // the read loop before it gets an EOF).\n      // operate off copies of the lists in case they change during iteration\n      std::list<peer_connection_ptr> all_peers;\n      boost::push_back(all_peers, _active_connections);\n      boost::push_back(all_peers, _handshaking_connections);\n      boost::push_back(all_peers, _closing_connections);\n\n      for (const peer_connection_ptr& peer : all_peers)\n      {\n        try\n        {\n          peer->destroy_connection();\n        }\n        catch ( const fc::exception& e )\n        {\n          wlog( \"Exception thrown while closing peer connection, ignoring: ${e}\", (\"e\", e) );\n        }\n        catch (...)\n        {\n          wlog( \"Exception thrown while closing peer connection, ignoring\" );\n        }\n      }\n\n      // and delete all of the peer_connection objects\n      _active_connections.clear();\n      _handshaking_connections.clear();\n      _closing_connections.clear();\n      all_peers.clear();\n\n      {\n#ifdef USE_PEERS_TO_DELETE_MUTEX\n        fc::scoped_lock<fc::mutex> lock(_peers_to_delete_mutex);\n#endif\n        try\n        {\n          _delayed_peer_deletion_task_done.cancel_and_wait(\"node_impl::close()\");\n          dlog(\"Delayed peer deletion task terminated\");\n        }\n        catch ( const fc::exception& e )\n        {\n          wlog( \"Exception thrown while terminating Delayed peer deletion task, ignoring: ${e}\", (\"e\", e) );\n        }\n        catch (...)\n        {\n          wlog( \"Exception thrown while terminating Delayed peer deletion task, ignoring\" );\n        }\n        _peers_to_delete.clear();\n      }\n\n      // Now that there are no more peers that can call methods on us, there should be no\n      // chance for one of our loops to be rescheduled, so we can safely terminate all of\n      // our loops now\n      try\n      {\n        _terminate_inactive_connections_loop_done.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"Terminate inactive connections loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Terminate inactive connections loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Terminate inactive connections loop, ignoring\" );\n      }\n\n      try\n      {\n        _fetch_updated_peer_lists_loop_done.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"Fetch updated peer lists loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Fetch updated peer lists loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Fetch updated peer lists loop, ignoring\" );\n      }\n\n      try\n      {\n        _update_seed_nodes_loop_done.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"Update seed nodes loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Update seed nodes loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Update seed nodes loop, ignoring\" );\n      }\n\n      try\n      {\n        _bandwidth_monitor_loop_done.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"Bandwidth monitor loop terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Bandwidth monitor loop, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Bandwidth monitor loop, ignoring\" );\n      }\n\n      try\n      {\n        _dump_node_status_task_done.cancel_and_wait(\"node_impl::close()\");\n        dlog(\"Dump node status task terminated\");\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"Exception thrown while terminating Dump node status task, ignoring: ${e}\", (\"e\", e) );\n      }\n      catch (...)\n      {\n        wlog( \"Exception thrown while terminating Dump node status task, ignoring\" );\n      }\n    } // node_impl::close()\n\n    void node_impl::accept_connection_task( peer_connection_ptr new_peer )\n    {\n      VERIFY_CORRECT_THREAD();\n      new_peer->accept_connection(); // this blocks until the secure connection is fully negotiated\n      send_hello_message(new_peer);\n    }\n\n    void node_impl::accept_loop()\n    {\n      VERIFY_CORRECT_THREAD();\n      while ( !_accept_loop_complete.canceled() )\n      {\n        peer_connection_ptr new_peer(peer_connection::make_shared(this));\n\n        try\n        {\n          _tcp_server.accept( new_peer->get_socket() );\n          ilog( \"accepted inbound connection from ${remote_endpoint}\", (\"remote_endpoint\", new_peer->get_socket().remote_endpoint() ) );\n          if (_node_is_shutting_down)\n            return;\n          new_peer->connection_initiation_time = fc::time_point::now();\n          _handshaking_connections.insert( new_peer );\n          _rate_limiter.add_tcp_socket( &new_peer->get_socket() );\n          std::weak_ptr<peer_connection> new_weak_peer(new_peer);\n          new_peer->accept_or_connect_task_done = fc::async( [this, new_weak_peer]() {\n            peer_connection_ptr new_peer(new_weak_peer.lock());\n            assert(new_peer);\n            if (!new_peer)\n              return;\n            accept_connection_task(new_peer);\n          }, \"accept_connection_task\" );\n\n          // limit the rate at which we accept connections to mitigate DOS attacks\n          fc::usleep( fc::milliseconds(10) );\n        } FC_CAPTURE_AND_LOG( (0) )\n      }\n    } // accept_loop()\n\n    void node_impl::send_hello_message(const peer_connection_ptr& peer)\n    {\n      VERIFY_CORRECT_THREAD();\n      peer->negotiation_status = peer_connection::connection_negotiation_status::hello_sent;\n\n      fc::sha256::encoder shared_secret_encoder;\n      fc::sha512 shared_secret = peer->get_shared_secret();\n      shared_secret_encoder.write(shared_secret.data(), sizeof(shared_secret));\n      fc::ecc::compact_signature signature = _node_configuration.private_key.sign_compact(shared_secret_encoder.result());\n\n      // in the hello messsage, we send three things:\n      //  ip address\n      //  outbound port\n      //  inbound port\n      // The peer we're connecting to will assume we're firewalled if the\n      // ip address and outbound port we send don't match the values it sees on its remote endpoint\n      //\n      // if we know that we're behind a NAT that will allow incoming connections because our firewall\n      // detection figured it out, send those values instead.\n\n      fc::ip::endpoint local_endpoint(peer->get_socket().local_endpoint());\n      uint16_t listening_port = _node_configuration.accept_incoming_connections ? _actual_listening_endpoint.port() : 0;\n\n      if (_is_firewalled == firewalled_state::not_firewalled &&\n          _publicly_visible_listening_endpoint)\n      {\n        local_endpoint = *_publicly_visible_listening_endpoint;\n        listening_port = _publicly_visible_listening_endpoint->port();\n      }\n\n      hello_message hello(_user_agent_string,\n                          core_protocol_version,\n                          local_endpoint.get_address(),\n                          listening_port,\n                          local_endpoint.port(),\n                          _node_public_key,\n                          signature,\n                          _chain_id,\n                          generate_hello_user_data());\n\n      peer->send_message(message(hello));\n    }\n\n    void node_impl::connect_to_task(peer_connection_ptr new_peer,\n                                    const fc::ip::endpoint& remote_endpoint)\n    {\n      VERIFY_CORRECT_THREAD();\n\n      if (!new_peer->performing_firewall_check())\n      {\n        // create or find the database entry for the new peer\n        // if we're connecting to them, we believe they're not firewalled\n        potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(remote_endpoint);\n        updated_peer_record.last_connection_disposition = last_connection_failed;\n        updated_peer_record.last_connection_attempt_time = fc::time_point::now();;\n        _potential_peer_db.update_entry(updated_peer_record);\n      }\n      else\n      {\n        wlog(\"connecting to peer ${peer} for firewall check\", (\"peer\", new_peer->get_remote_endpoint()));\n      }\n\n      fc::oexception connect_failed_exception;\n\n      try\n      {\n        new_peer->connect_to(remote_endpoint, _actual_listening_endpoint);  // blocks until the connection is established and secure connection is negotiated\n\n        // we connected to the peer.  guess they're not firewalled....\n        new_peer->is_firewalled = firewalled_state::not_firewalled;\n\n        // connection succeeded, we've started handshaking.  record that in our database\n        potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(remote_endpoint);\n        updated_peer_record.last_connection_disposition = last_connection_handshaking_failed;\n        updated_peer_record.number_of_successful_connection_attempts++;\n        updated_peer_record.last_seen_time = fc::time_point::now();\n        _potential_peer_db.update_entry(updated_peer_record);\n      }\n      catch (const fc::exception& except)\n      {\n        connect_failed_exception = except;\n      }\n\n      if (connect_failed_exception && !new_peer->performing_firewall_check())\n      {\n        // connection failed.  record that in our database\n        potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(remote_endpoint);\n        updated_peer_record.last_connection_disposition = last_connection_failed;\n        updated_peer_record.number_of_failed_connection_attempts++;\n        if (new_peer->connection_closed_error)\n          updated_peer_record.last_error = *new_peer->connection_closed_error;\n        else\n          updated_peer_record.last_error = *connect_failed_exception;\n        _potential_peer_db.update_entry(updated_peer_record);\n      }\n\n      if (new_peer->performing_firewall_check())\n      {\n        // we were connecting to test whether the node is firewalled, and we now know the result.\n        // send a message back to the requester\n        peer_connection_ptr requesting_peer = get_peer_by_node_id(new_peer->firewall_check_state->requesting_peer);\n        if (requesting_peer)\n        {\n          check_firewall_reply_message reply;\n          reply.endpoint_checked = new_peer->firewall_check_state->endpoint_to_test;\n          reply.node_id = new_peer->firewall_check_state->expected_node_id;\n          reply.result = connect_failed_exception ?\n                           firewall_check_result::unable_to_connect :\n                           firewall_check_result::connection_successful;\n          wlog(\"firewall check of ${peer_checked} ${success_or_failure}, sending reply to ${requester}\",\n                (\"peer_checked\", new_peer->get_remote_endpoint())\n                (\"success_or_failure\", connect_failed_exception ? \"failed\" : \"succeeded\" )\n                (\"requester\", requesting_peer->get_remote_endpoint()));\n\n          requesting_peer->send_message(reply);\n        }\n      }\n\n      if (connect_failed_exception || new_peer->performing_firewall_check())\n      {\n        // if the connection failed or if this connection was just intended to check\n        // whether the peer is firewalled, we want to disconnect now.\n        _handshaking_connections.erase(new_peer);\n        _terminating_connections.erase(new_peer);\n        assert(_active_connections.find(new_peer) == _active_connections.end());\n        _active_connections.erase(new_peer);\n        assert(_closing_connections.find(new_peer) == _closing_connections.end());\n        _closing_connections.erase(new_peer);\n\n        display_current_connections();\n        trigger_p2p_network_connect_loop();\n        schedule_peer_for_deletion(new_peer);\n\n        if (connect_failed_exception)\n          throw *connect_failed_exception;\n      }\n      else\n      {\n        // connection was successful and we want to stay connected\n        fc::ip::endpoint local_endpoint = new_peer->get_local_endpoint();\n        new_peer->inbound_address = local_endpoint.get_address();\n        new_peer->inbound_port = _node_configuration.accept_incoming_connections ? _actual_listening_endpoint.port() : 0;\n        new_peer->outbound_port = local_endpoint.port();\n\n        new_peer->our_state = peer_connection::our_connection_state::just_connected;\n        new_peer->their_state = peer_connection::their_connection_state::just_connected;\n        send_hello_message(new_peer);\n        dlog(\"Sent \\\"hello\\\" to peer ${peer}\", (\"peer\", new_peer->get_remote_endpoint()));\n      }\n    }\n\n    // methods implementing node's public interface\n    void node_impl::set_node_delegate(node_delegate* del, fc::thread* thread_for_delegate_calls)\n    {\n      VERIFY_CORRECT_THREAD();\n      _delegate.reset();\n      if (del)\n        _delegate.reset(new statistics_gathering_node_delegate_wrapper(del, thread_for_delegate_calls));\n      if( _delegate )\n        _chain_id = del->get_chain_id();\n    }\n\n    void node_impl::load_configuration( const fc::path& configuration_directory )\n    {\n      VERIFY_CORRECT_THREAD();\n      _node_configuration_directory = configuration_directory;\n      fc::path configuration_file_name( _node_configuration_directory / NODE_CONFIGURATION_FILENAME );\n      bool node_configuration_loaded = false;\n      if( fc::exists(configuration_file_name ) )\n      {\n        try\n        {\n          _node_configuration = fc::json::from_file( configuration_file_name ).as<detail::node_configuration>(GRAPHENE_NET_MAX_NESTED_OBJECTS);\n          ilog( \"Loaded configuration from file ${filename}\", (\"filename\", configuration_file_name ) );\n\n          if( _node_configuration.private_key == fc::ecc::private_key() )\n            _node_configuration.private_key = fc::ecc::private_key::generate();\n\n          node_configuration_loaded = true;\n        }\n        catch ( fc::parse_error_exception& parse_error )\n        {\n          elog( \"malformed node configuration file ${filename}: ${error}\",\n               ( \"filename\", configuration_file_name )(\"error\", parse_error.to_detail_string() ) );\n        }\n        catch ( fc::exception& except )\n        {\n          elog( \"unexpected exception while reading configuration file ${filename}: ${error}\",\n               ( \"filename\", configuration_file_name )(\"error\", except.to_detail_string() ) );\n        }\n      }\n\n      if( !node_configuration_loaded )\n      {\n        _node_configuration = detail::node_configuration();\n\n#ifdef GRAPHENE_TEST_NETWORK\n        uint32_t port = GRAPHENE_NET_TEST_P2P_PORT;\n#else\n        uint32_t port = GRAPHENE_NET_DEFAULT_P2P_PORT;\n#endif\n        _node_configuration.listen_endpoint.set_port( port );\n        _node_configuration.accept_incoming_connections = true;\n        _node_configuration.wait_if_endpoint_is_busy = false;\n\n        ilog( \"generating new private key for this node\" );\n        _node_configuration.private_key = fc::ecc::private_key::generate();\n      }\n\n      _node_public_key = _node_configuration.private_key.get_public_key().serialize();\n\n      fc::path potential_peer_database_file_name(_node_configuration_directory / POTENTIAL_PEER_DATABASE_FILENAME);\n      try\n      {\n        _potential_peer_db.open(potential_peer_database_file_name);\n\n        // push back the time on all peers loaded from the database so we will be able to retry them immediately\n        for (peer_database::iterator itr = _potential_peer_db.begin(); itr != _potential_peer_db.end(); ++itr)\n        {\n          potential_peer_record updated_peer_record = *itr;\n          updated_peer_record.last_connection_attempt_time = std::min<fc::time_point_sec>(updated_peer_record.last_connection_attempt_time,\n                                                                                          fc::time_point::now() - fc::seconds(_peer_connection_retry_timeout));\n          _potential_peer_db.update_entry(updated_peer_record);\n        }\n\n        trigger_p2p_network_connect_loop();\n      }\n      catch (fc::exception& except)\n      {\n        elog(\"unable to open peer database ${filename}: ${error}\",\n             (\"filename\", potential_peer_database_file_name)(\"error\", except.to_detail_string()));\n        throw;\n      }\n    }\n\n    void node_impl::listen_to_p2p_network()\n    {\n      VERIFY_CORRECT_THREAD();\n      if (!_node_configuration.accept_incoming_connections)\n      {\n        wlog(\"accept_incoming_connections is false, p2p network will not accept any incoming connections\");\n        return;\n      }\n\n      assert(_node_public_key != fc::ecc::public_key_data());\n\n      fc::ip::endpoint listen_endpoint = _node_configuration.listen_endpoint;\n      if( listen_endpoint.port() != 0 )\n      {\n        // if the user specified a port, we only want to bind to it if it's not already\n        // being used by another application.  During normal operation, we set the\n        // SO_REUSEADDR/SO_REUSEPORT flags so that we can bind outbound sockets to the\n        // same local endpoint as we're listening on here.  On some platforms, setting\n        // those flags will prevent us from detecting that other applications are\n        // listening on that port.  We'd like to detect that, so we'll set up a temporary\n        // tcp server without that flag to see if we can listen on that port.\n        bool first = true;\n        for( ;; )\n        {\n          bool listen_failed = false;\n\n          try\n          {\n            fc::tcp_server temporary_server;\n            if( listen_endpoint.get_address() != fc::ip::address() )\n              temporary_server.listen( listen_endpoint );\n            else\n              temporary_server.listen( listen_endpoint.port() );\n            break;\n          }\n          catch ( const fc::exception&)\n          {\n            listen_failed = true;\n          }\n\n          if (listen_failed)\n          {\n            if( _node_configuration.wait_if_endpoint_is_busy )\n            {\n              std::ostringstream error_message_stream;\n              if( first )\n              {\n                error_message_stream << \"Unable to listen for connections on port \" << listen_endpoint.port()\n                                     << \", retrying in a few seconds\\n\";\n                error_message_stream << \"You can wait for it to become available, or restart this program using\\n\";\n                error_message_stream << \"the --p2p-endpoint option to specify another port\\n\";\n                first = false;\n              }\n              else\n              {\n                error_message_stream << \"\\nStill waiting for port \" << listen_endpoint.port() << \" to become available\\n\";\n              }\n              std::string error_message = error_message_stream.str();\n              wlog(error_message);\n              std::cout << \"\\033[31m\" << error_message;  \n              _delegate->error_encountered( error_message, fc::oexception() );\n              fc::usleep( fc::seconds(5 ) );\n            }\n            else // don't wait, just find a random port\n            {\n              wlog( \"unable to bind on the requested endpoint ${endpoint}, which probably means that endpoint is already in use\",\n                   ( \"endpoint\", listen_endpoint ) );\n              listen_endpoint.set_port( 0 );\n            }\n          } // if (listen_failed)\n        } // for(;;)\n      } // if (listen_endpoint.port() != 0)\n      else // port is 0\n      {\n        // if they requested a random port, we'll just assume it's available\n        // (it may not be due to ip address, but we'll detect that in the next step)\n      }\n\n      _tcp_server.set_reuse_address();\n      try\n      {\n        if( listen_endpoint.get_address() != fc::ip::address() )\n          _tcp_server.listen( listen_endpoint );\n        else\n          _tcp_server.listen( listen_endpoint.port() );\n        _actual_listening_endpoint = _tcp_server.get_local_endpoint();\n        ilog( \"listening for connections on endpoint ${endpoint} (our first choice)\",\n              ( \"endpoint\", _actual_listening_endpoint ) );\n      }\n      catch ( fc::exception& e )\n      {\n        FC_RETHROW_EXCEPTION( e, error, \"unable to listen on ${endpoint}\", (\"endpoint\",listen_endpoint ) );\n      }\n    }\n\n    void node_impl::connect_to_p2p_network()\n    {\n      VERIFY_CORRECT_THREAD();\n      assert(_node_public_key != fc::ecc::public_key_data());\n\n      assert(!_accept_loop_complete.valid() &&\n             !_p2p_network_connect_loop_done.valid() &&\n             !_update_seed_nodes_loop_done.valid() &&\n             !_fetch_sync_items_loop_done.valid() &&\n             !_fetch_item_loop_done.valid() &&\n             !_advertise_inventory_loop_done.valid() &&\n             !_terminate_inactive_connections_loop_done.valid() &&\n             !_fetch_updated_peer_lists_loop_done.valid() &&\n             !_bandwidth_monitor_loop_done.valid() &&\n             !_dump_node_status_task_done.valid());\n      if (_node_configuration.accept_incoming_connections)\n        _accept_loop_complete = fc::async( [=](){ accept_loop(); }, \"accept_loop\");\n      _p2p_network_connect_loop_done = fc::async( [=]() { p2p_network_connect_loop(); }, \"p2p_network_connect_loop\" );\n      _fetch_sync_items_loop_done = fc::async( [=]() { fetch_sync_items_loop(); }, \"fetch_sync_items_loop\" );\n      _fetch_item_loop_done = fc::async( [=]() { fetch_items_loop(); }, \"fetch_items_loop\" );\n      _advertise_inventory_loop_done = fc::async( [=]() { advertise_inventory_loop(); }, \"advertise_inventory_loop\" );\n      _terminate_inactive_connections_loop_done = fc::async( [=]() { terminate_inactive_connections_loop(); }, \"terminate_inactive_connections_loop\" );\n      _fetch_updated_peer_lists_loop_done = fc::async([=](){ fetch_updated_peer_lists_loop(); }, \"fetch_updated_peer_lists_loop\");\n      _bandwidth_monitor_loop_done = fc::async([=](){ bandwidth_monitor_loop(); }, \"bandwidth_monitor_loop\");\n      _dump_node_status_task_done = fc::async([=](){ dump_node_status_task(); }, \"dump_node_status_task\");\n      schedule_next_update_seed_nodes_task();\n    }\n\n    void node_impl::add_node(const fc::ip::endpoint& ep)\n    {\n      VERIFY_CORRECT_THREAD();\n      // if we're connecting to them, we believe they're not firewalled\n      potential_peer_record updated_peer_record = _potential_peer_db.lookup_or_create_entry_for_endpoint(ep);\n\n      // if we've recently connected to this peer, reset the last_connection_attempt_time to allow\n      // us to immediately retry this peer\n      updated_peer_record.last_connection_attempt_time = std::min<fc::time_point_sec>(updated_peer_record.last_connection_attempt_time,\n                                                                                      fc::time_point::now() - fc::seconds(_peer_connection_retry_timeout));\n      _add_once_node_list.push_back(updated_peer_record);\n      _potential_peer_db.update_entry(updated_peer_record);\n      trigger_p2p_network_connect_loop();\n    }\n\n   void node_impl::add_seed_node(const std::string& endpoint_string)\n   {\n      VERIFY_CORRECT_THREAD();\n      _seed_nodes.insert( endpoint_string );\n      resolve_seed_node_and_add( endpoint_string );\n   }\n\n   void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string)\n   {\n      VERIFY_CORRECT_THREAD();\n      std::vector<fc::ip::endpoint> endpoints;\n      ilog(\"Resolving seed node ${endpoint}\", (\"endpoint\", endpoint_string));\n      try\n      {\n         endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string);\n      }\n      catch(...)\n      {\n         wlog( \"Unable to resolve endpoint during attempt to add seed node ${ep}\", (\"ep\", endpoint_string) );\n      }\n      for (const fc::ip::endpoint& endpoint : endpoints)\n      {\n         ilog(\"Adding seed node ${endpoint}\", (\"endpoint\", endpoint));\n         add_node(endpoint);\n      }\n   }\n\n    void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer)\n    {\n      new_peer->get_socket().open();\n      new_peer->get_socket().set_reuse_address();\n      new_peer->connection_initiation_time = fc::time_point::now();\n      _handshaking_connections.insert(new_peer);\n      _rate_limiter.add_tcp_socket(&new_peer->get_socket());\n\n      if (_node_is_shutting_down)\n        return;\n\n      std::weak_ptr<peer_connection> new_weak_peer(new_peer);\n      new_peer->accept_or_connect_task_done = fc::async([this, new_weak_peer](){\n        peer_connection_ptr new_peer(new_weak_peer.lock());\n        assert(new_peer);\n        if (!new_peer)\n          return;\n        connect_to_task(new_peer, *new_peer->get_remote_endpoint());\n      }, \"connect_to_task\");\n    }\n\n    void node_impl::connect_to_endpoint(const fc::ip::endpoint& remote_endpoint)\n    {\n      VERIFY_CORRECT_THREAD();\n      if (is_connection_to_endpoint_in_progress(remote_endpoint))\n        FC_THROW_EXCEPTION(already_connected_to_requested_peer, \"already connected to requested endpoint ${endpoint}\",\n                           (\"endpoint\", remote_endpoint));\n\n      dlog(\"node_impl::connect_to_endpoint(${endpoint})\", (\"endpoint\", remote_endpoint));\n      peer_connection_ptr new_peer(peer_connection::make_shared(this));\n      new_peer->set_remote_endpoint(remote_endpoint);\n      initiate_connect_to(new_peer);\n    }\n\n    peer_connection_ptr node_impl::get_connection_to_endpoint( const fc::ip::endpoint& remote_endpoint )\n    {\n      VERIFY_CORRECT_THREAD();\n      for( const peer_connection_ptr& active_peer : _active_connections )\n      {\n        fc::optional<fc::ip::endpoint> endpoint_for_this_peer( active_peer->get_remote_endpoint() );\n        if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint )\n          return active_peer;\n      }\n      for( const peer_connection_ptr& handshaking_peer : _handshaking_connections )\n      {\n        fc::optional<fc::ip::endpoint> endpoint_for_this_peer( handshaking_peer->get_remote_endpoint() );\n        if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint )\n          return handshaking_peer;\n      }\n      return peer_connection_ptr();\n    }\n\n    bool node_impl::is_connection_to_endpoint_in_progress( const fc::ip::endpoint& remote_endpoint )\n    {\n      VERIFY_CORRECT_THREAD();\n      return get_connection_to_endpoint( remote_endpoint ) != peer_connection_ptr();\n    }\n\n    void node_impl::move_peer_to_active_list(const peer_connection_ptr& peer)\n    {\n      VERIFY_CORRECT_THREAD();\n      _active_connections.insert(peer);\n      _handshaking_connections.erase(peer);\n      _closing_connections.erase(peer);\n      _terminating_connections.erase(peer);\n    }\n\n    void node_impl::move_peer_to_closing_list(const peer_connection_ptr& peer)\n    {\n      VERIFY_CORRECT_THREAD();\n      _active_connections.erase(peer);\n      _handshaking_connections.erase(peer);\n      _closing_connections.insert(peer);\n      _terminating_connections.erase(peer);\n    }\n\n    void node_impl::move_peer_to_terminating_list(const peer_connection_ptr& peer)\n    {\n      VERIFY_CORRECT_THREAD();\n      _active_connections.erase(peer);\n      _handshaking_connections.erase(peer);\n      _closing_connections.erase(peer);\n      _terminating_connections.insert(peer);\n    }\n\n    void node_impl::dump_node_status()\n    {\n      VERIFY_CORRECT_THREAD();\n      ilog( \"----------------- PEER STATUS UPDATE --------------------\" );\n      ilog( \" number of peers: ${active} active, ${handshaking}, ${closing} closing.  attempting to maintain ${desired} - ${maximum} peers\",\n           ( \"active\", _active_connections.size() )(\"handshaking\", _handshaking_connections.size() )(\"closing\",_closing_connections.size() )\n           ( \"desired\", _desired_number_of_connections )(\"maximum\", _maximum_number_of_connections ) );\n      for( const peer_connection_ptr& peer : _active_connections )\n      {\n        ilog( \"       active peer ${endpoint} peer_is_in_sync_with_us:${in_sync_with_us} we_are_in_sync_with_peer:${in_sync_with_them}\",\n             ( \"endpoint\", peer->get_remote_endpoint() )\n             ( \"in_sync_with_us\", !peer->peer_needs_sync_items_from_us )(\"in_sync_with_them\", !peer->we_need_sync_items_from_peer ) );\n        if( peer->we_need_sync_items_from_peer )\n          ilog( \"              above peer has ${count} sync items we might need\", (\"count\", peer->ids_of_items_to_get.size() ) );\n        if (peer->inhibit_fetching_sync_blocks)\n          ilog( \"              we are not fetching sync blocks from the above peer (inhibit_fetching_sync_blocks == true)\" );\n\n      }\n      for( const peer_connection_ptr& peer : _handshaking_connections )\n      {\n        ilog( \"  handshaking peer ${endpoint} in state ours(${our_state}) theirs(${their_state})\",\n             ( \"endpoint\", peer->get_remote_endpoint() )(\"our_state\", peer->our_state )(\"their_state\", peer->their_state ) );\n      }\n\n      ilog( \"--------- MEMORY USAGE ------------\" );\n      ilog( \"node._active_sync_requests size: ${size}\", (\"size\", _active_sync_requests.size() ) );\n      ilog( \"node._received_sync_items size: ${size}\", (\"size\", _received_sync_items.size() ) );\n      ilog( \"node._new_received_sync_items size: ${size}\", (\"size\", _new_received_sync_items.size() ) );\n      ilog( \"node._items_to_fetch size: ${size}\", (\"size\", _items_to_fetch.size() ) );\n      ilog( \"node._new_inventory size: ${size}\", (\"size\", _new_inventory.size() ) );\n      ilog( \"node._message_cache size: ${size}\", (\"size\", _message_cache.size() ) );\n      for( const peer_connection_ptr& peer : _active_connections )\n      {\n        ilog( \"  peer ${endpoint}\", (\"endpoint\", peer->get_remote_endpoint() ) );\n        ilog( \"    peer.ids_of_items_to_get size: ${size}\", (\"size\", peer->ids_of_items_to_get.size() ) );\n        ilog( \"    peer.inventory_peer_advertised_to_us size: ${size}\", (\"size\", peer->inventory_peer_advertised_to_us.size() ) );\n        ilog( \"    peer.inventory_advertised_to_peer size: ${size}\", (\"size\", peer->inventory_advertised_to_peer.size() ) );\n        ilog( \"    peer.items_requested_from_peer size: ${size}\", (\"size\", peer->items_requested_from_peer.size() ) );\n        ilog( \"    peer.sync_items_requested_from_peer size: ${size}\", (\"size\", peer->sync_items_requested_from_peer.size() ) );\n      }\n      ilog( \"--------- END MEMORY USAGE ------------\" );\n    }\n\n    void node_impl::disconnect_from_peer( peer_connection* peer_to_disconnect,\n                                          const std::string& reason_for_disconnect,\n                                          bool caused_by_error /* = false */,\n                                          const fc::oexception& error /* = fc::oexception() */ )\n    {\n      VERIFY_CORRECT_THREAD();\n      move_peer_to_closing_list(peer_to_disconnect->shared_from_this());\n\n      if (peer_to_disconnect->they_have_requested_close)\n      {\n        // the peer has already told us that it's ready to close the connection, so just close the connection\n        peer_to_disconnect->close_connection();\n      }\n      else\n      {\n        // we're the first to try to want to close the connection\n        fc::optional<fc::ip::endpoint> inbound_endpoint = peer_to_disconnect->get_endpoint_for_connecting();\n        if (inbound_endpoint)\n        {\n          fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint);\n          if (updated_peer_record)\n          {\n            updated_peer_record->last_seen_time = fc::time_point::now();\n            if (error)\n              updated_peer_record->last_error = error;\n            else\n              updated_peer_record->last_error = fc::exception(FC_LOG_MESSAGE(info, reason_for_disconnect.c_str()));\n            _potential_peer_db.update_entry(*updated_peer_record);\n          }\n        }\n        peer_to_disconnect->we_have_requested_close = true;\n        peer_to_disconnect->connection_closed_time = fc::time_point::now();\n\n        closing_connection_message closing_message( reason_for_disconnect, caused_by_error, error );\n        peer_to_disconnect->send_message( closing_message );\n      }\n\n      // notify the user.  This will be useful in testing, but we might want to remove it later.\n      // It makes good sense to notify the user if other nodes think she is behaving badly, but\n      // if we're just detecting and dissconnecting other badly-behaving nodes, they don't really care.\n      if (caused_by_error)\n      {\n        std::ostringstream error_message;\n        error_message << \"I am disconnecting peer \" << fc::variant( peer_to_disconnect->get_remote_endpoint(), GRAPHENE_NET_MAX_NESTED_OBJECTS ).as_string() <<\n                         \" for reason: \" << reason_for_disconnect;\n        _delegate->error_encountered(error_message.str(), fc::oexception());\n        dlog(error_message.str());\n      }\n      else\n        dlog(\"Disconnecting from ${peer} for ${reason}\", (\"peer\",peer_to_disconnect->get_remote_endpoint()) (\"reason\",reason_for_disconnect));\n    }\n\n    void node_impl::listen_on_endpoint( const fc::ip::endpoint& ep, bool wait_if_not_available )\n    {\n      VERIFY_CORRECT_THREAD();\n      _node_configuration.listen_endpoint = ep;\n      _node_configuration.wait_if_endpoint_is_busy = wait_if_not_available;\n      save_node_configuration();\n    }\n\n    void node_impl::accept_incoming_connections(bool accept)\n    {\n      VERIFY_CORRECT_THREAD();\n      _node_configuration.accept_incoming_connections = accept;\n      save_node_configuration();\n    }\n\n    void node_impl::listen_on_port( uint16_t port, bool wait_if_not_available )\n    {\n      VERIFY_CORRECT_THREAD();\n      _node_configuration.listen_endpoint = fc::ip::endpoint( fc::ip::address(), port );\n      _node_configuration.wait_if_endpoint_is_busy = wait_if_not_available;\n      save_node_configuration();\n    }\n\n    fc::ip::endpoint node_impl::get_actual_listening_endpoint() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _actual_listening_endpoint;\n    }\n\n    std::vector<peer_status> node_impl::get_connected_peers() const\n    {\n      VERIFY_CORRECT_THREAD();\n      std::vector<peer_status> statuses;\n      for (const peer_connection_ptr& peer : _active_connections)\n      {\n        ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections\n\n        peer_status this_peer_status;\n        this_peer_status.version = 0;\n        fc::optional<fc::ip::endpoint> endpoint = peer->get_remote_endpoint();\n        if (endpoint)\n          this_peer_status.host = *endpoint;\n        fc::mutable_variant_object peer_details;\n        peer_details[\"addr\"] = endpoint ? (std::string)*endpoint : std::string();\n        peer_details[\"addrlocal\"] = (std::string)peer->get_local_endpoint();\n        peer_details[\"services\"] = \"00000001\";\n        peer_details[\"lastsend\"] = peer->get_last_message_sent_time().sec_since_epoch();\n        peer_details[\"lastrecv\"] = peer->get_last_message_received_time().sec_since_epoch();\n        peer_details[\"bytessent\"] = peer->get_total_bytes_sent();\n        peer_details[\"bytesrecv\"] = peer->get_total_bytes_received();\n        peer_details[\"conntime\"] = peer->get_connection_time();\n        peer_details[\"pingtime\"] = \"\";\n        peer_details[\"pingwait\"] = \"\";\n        peer_details[\"version\"] = \"\";\n        peer_details[\"subver\"] = peer->user_agent;\n        peer_details[\"inbound\"] = peer->direction == peer_connection_direction::inbound;\n        peer_details[\"firewall_status\"] = fc::variant( peer->is_firewalled, 1 );\n        peer_details[\"startingheight\"] = \"\";\n        peer_details[\"banscore\"] = \"\";\n        peer_details[\"syncnode\"] = \"\";\n\n        if (peer->fc_git_revision_sha)\n        {\n          std::string revision_string = *peer->fc_git_revision_sha;\n          if (*peer->fc_git_revision_sha == fc::git_revision_sha)\n            revision_string += \" (same as ours)\";\n          else\n            revision_string += \" (different from ours)\";\n          peer_details[\"fc_git_revision_sha\"] = revision_string;\n\n        }\n        if (peer->fc_git_revision_unix_timestamp)\n        {\n          peer_details[\"fc_git_revision_unix_timestamp\"] = *peer->fc_git_revision_unix_timestamp;\n          std::string age_string = fc::get_approximate_relative_time_string( *peer->fc_git_revision_unix_timestamp);\n          if (*peer->fc_git_revision_unix_timestamp == fc::time_point_sec(fc::git_revision_unix_timestamp))\n            age_string += \" (same as ours)\";\n          else if (*peer->fc_git_revision_unix_timestamp > fc::time_point_sec(fc::git_revision_unix_timestamp))\n            age_string += \" (newer than ours)\";\n          else\n            age_string += \" (older than ours)\";\n          peer_details[\"fc_git_revision_age\"] = age_string;\n        }\n\n        if (peer->platform)\n          peer_details[\"platform\"] = *peer->platform;\n\n        // provide these for debugging\n        // warning: these are just approximations, if the peer is \"downstream\" of us, they may\n        // have received blocks from other peers that we are unaware of\n        peer_details[\"current_head_block\"] = fc::variant( peer->last_block_delegate_has_seen, 1 );\n        peer_details[\"current_head_block_number\"] = _delegate->get_block_number(peer->last_block_delegate_has_seen);\n        peer_details[\"current_head_block_time\"] = peer->last_block_time_delegate_has_seen;\n\n        this_peer_status.info = peer_details;\n        statuses.push_back(this_peer_status);\n      }\n      return statuses;\n    }\n\n    uint32_t node_impl::get_connection_count() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return (uint32_t)_active_connections.size();\n    }\n\n    void node_impl::broadcast( const message& item_to_broadcast, const message_propagation_data& propagation_data )\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::uint160_t hash_of_message_contents;\n      if( item_to_broadcast.msg_type.value() == graphene::net::block_message_type )\n      {\n        graphene::net::block_message block_message_to_broadcast = item_to_broadcast.as<graphene::net::block_message>();\n        hash_of_message_contents = block_message_to_broadcast.block_id; // for debugging\n        _most_recent_blocks_accepted.push_back( block_message_to_broadcast.block_id );\n      }\n      else if( item_to_broadcast.msg_type.value() == graphene::net::trx_message_type )\n      {\n        graphene::net::trx_message transaction_message_to_broadcast = item_to_broadcast.as<graphene::net::trx_message>();\n        hash_of_message_contents = transaction_message_to_broadcast.trx.id(); // for debugging\n        dlog( \"broadcasting trx: ${trx}\", (\"trx\", transaction_message_to_broadcast) );\n      }\n      message_hash_type hash_of_item_to_broadcast = item_to_broadcast.id();\n\n      _message_cache.cache_message( item_to_broadcast, hash_of_item_to_broadcast, propagation_data, hash_of_message_contents );\n      _new_inventory.insert( item_id(item_to_broadcast.msg_type.value(), hash_of_item_to_broadcast ) );\n      trigger_advertise_inventory_loop();\n    }\n\n    void node_impl::broadcast( const message& item_to_broadcast )\n    {\n      VERIFY_CORRECT_THREAD();\n      // this version is called directly from the client\n      message_propagation_data propagation_data{fc::time_point::now(), fc::time_point::now(), _node_id};\n      broadcast( item_to_broadcast, propagation_data );\n    }\n\n    void node_impl::sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers)\n    {\n      VERIFY_CORRECT_THREAD();\n      _most_recent_blocks_accepted.clear();\n      _sync_item_type = current_head_block.item_type;\n      _most_recent_blocks_accepted.push_back(current_head_block.item_hash);\n      _hard_fork_block_numbers = hard_fork_block_numbers;\n    }\n\n    bool node_impl::is_connected() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return !_active_connections.empty();\n    }\n\n    std::vector<potential_peer_record> node_impl::get_potential_peers() const\n    {\n      VERIFY_CORRECT_THREAD();\n      std::vector<potential_peer_record> result;\n      // use explicit iterators here, for some reason the mac compiler can't used ranged-based for loops here\n      for (peer_database::iterator itr = _potential_peer_db.begin(); itr != _potential_peer_db.end(); ++itr)\n        result.push_back(*itr);\n      return result;\n    }\n\n    void node_impl::set_advanced_node_parameters(const fc::variant_object& params)\n    {\n      VERIFY_CORRECT_THREAD();\n      if (params.contains(\"peer_connection_retry_timeout\"))\n        _peer_connection_retry_timeout = params[\"peer_connection_retry_timeout\"].as<uint32_t>(1);\n      if (params.contains(\"desired_number_of_connections\"))\n        _desired_number_of_connections = params[\"desired_number_of_connections\"].as<uint32_t>(1);\n      if (params.contains(\"maximum_number_of_connections\"))\n        _maximum_number_of_connections = params[\"maximum_number_of_connections\"].as<uint32_t>(1);\n      if (params.contains(\"maximum_number_of_blocks_to_handle_at_one_time\"))\n        _maximum_number_of_blocks_to_handle_at_one_time = params[\"maximum_number_of_blocks_to_handle_at_one_time\"].as<uint32_t>(1);\n      if (params.contains(\"maximum_number_of_sync_blocks_to_prefetch\"))\n        _maximum_number_of_sync_blocks_to_prefetch = params[\"maximum_number_of_sync_blocks_to_prefetch\"].as<uint32_t>(1);\n      if (params.contains(\"maximum_blocks_per_peer_during_syncing\"))\n        _maximum_blocks_per_peer_during_syncing = params[\"maximum_blocks_per_peer_during_syncing\"].as<uint32_t>(1);\n\n      _desired_number_of_connections = std::min(_desired_number_of_connections, _maximum_number_of_connections);\n\n      while (_active_connections.size() > _maximum_number_of_connections)\n        disconnect_from_peer(_active_connections.begin()->get(),\n                             \"I have too many connections open\");\n      trigger_p2p_network_connect_loop();\n    }\n\n    fc::variant_object node_impl::get_advanced_node_parameters()\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::mutable_variant_object result;\n      result[\"peer_connection_retry_timeout\"] = _peer_connection_retry_timeout;\n      result[\"desired_number_of_connections\"] = _desired_number_of_connections;\n      result[\"maximum_number_of_connections\"] = _maximum_number_of_connections;\n      result[\"maximum_number_of_blocks_to_handle_at_one_time\"] = _maximum_number_of_blocks_to_handle_at_one_time;\n      result[\"maximum_number_of_sync_blocks_to_prefetch\"] = _maximum_number_of_sync_blocks_to_prefetch;\n      result[\"maximum_blocks_per_peer_during_syncing\"] = _maximum_blocks_per_peer_during_syncing;\n      return result;\n    }\n\n    message_propagation_data node_impl::get_transaction_propagation_data( const graphene::net::transaction_id_type& transaction_id )\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_cache.get_message_propagation_data( transaction_id );\n    }\n\n    message_propagation_data node_impl::get_block_propagation_data( const graphene::net::block_id_type& block_id )\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_cache.get_message_propagation_data( block_id );\n    }\n\n    node_id_t node_impl::get_node_id() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _node_id;\n    }\n    void node_impl::set_allowed_peers(const std::vector<node_id_t>& allowed_peers)\n    {\n      VERIFY_CORRECT_THREAD();\n#ifdef ENABLE_P2P_DEBUGGING_API\n      _allowed_peers.clear();\n      _allowed_peers.insert(allowed_peers.begin(), allowed_peers.end());\n      std::list<peer_connection_ptr> peers_to_disconnect;\n      if (!_allowed_peers.empty())\n        for (const peer_connection_ptr& peer : _active_connections)\n          if (_allowed_peers.find(peer->node_id) == _allowed_peers.end())\n            peers_to_disconnect.push_back(peer);\n      for (const peer_connection_ptr& peer : peers_to_disconnect)\n        disconnect_from_peer(peer.get(), \"My allowed_peers list has changed, and you're no longer allowed.  Bye.\");\n#endif // ENABLE_P2P_DEBUGGING_API\n    }\n    void node_impl::clear_peer_database()\n    {\n      VERIFY_CORRECT_THREAD();\n      _potential_peer_db.clear();\n    }\n\n    void node_impl::set_total_bandwidth_limit( uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second )\n    {\n      VERIFY_CORRECT_THREAD();\n      _rate_limiter.set_upload_limit( upload_bytes_per_second );\n      _rate_limiter.set_download_limit( download_bytes_per_second );\n    }\n\n    void node_impl::disable_peer_advertising()\n    {\n      VERIFY_CORRECT_THREAD();\n      _peer_advertising_disabled = true;\n    }\n\n    fc::variant_object node_impl::get_call_statistics() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _delegate->get_call_statistics();\n    }\n\n    fc::variant_object node_impl::network_get_info() const\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::mutable_variant_object info;\n      info[\"listening_on\"] = _actual_listening_endpoint;\n      info[\"node_public_key\"] = fc::variant( _node_public_key, 1 );\n      info[\"node_id\"] = fc::variant( _node_id, 1 );\n      info[\"firewalled\"] = fc::variant( _is_firewalled, 1 );\n      return info;\n    }\n    fc::variant_object node_impl::network_get_usage_stats() const\n    {\n      VERIFY_CORRECT_THREAD();\n      std::vector<uint32_t> network_usage_by_second;\n      network_usage_by_second.reserve(_average_network_read_speed_seconds.size());\n      std::transform(_average_network_read_speed_seconds.begin(), _average_network_read_speed_seconds.end(),\n                     _average_network_write_speed_seconds.begin(),\n                     std::back_inserter(network_usage_by_second),\n                     std::plus<uint32_t>());\n\n      std::vector<uint32_t> network_usage_by_minute;\n      network_usage_by_minute.reserve(_average_network_read_speed_minutes.size());\n      std::transform(_average_network_read_speed_minutes.begin(), _average_network_read_speed_minutes.end(),\n                     _average_network_write_speed_minutes.begin(),\n                     std::back_inserter(network_usage_by_minute),\n                     std::plus<uint32_t>());\n\n      std::vector<uint32_t> network_usage_by_hour;\n      network_usage_by_hour.reserve(_average_network_read_speed_hours.size());\n      std::transform(_average_network_read_speed_hours.begin(), _average_network_read_speed_hours.end(),\n                     _average_network_write_speed_hours.begin(),\n                     std::back_inserter(network_usage_by_hour),\n                     std::plus<uint32_t>());\n\n      fc::mutable_variant_object result;\n      result[\"usage_by_second\"] = fc::variant( network_usage_by_second, 2 );\n      result[\"usage_by_minute\"] = fc::variant( network_usage_by_minute, 2 );\n      result[\"usage_by_hour\"]   = fc::variant( network_usage_by_hour, 2 );\n      return result;\n    }\n\n    bool node_impl::is_hard_fork_block(uint32_t block_number) const\n    {\n      return std::binary_search(_hard_fork_block_numbers.begin(), _hard_fork_block_numbers.end(), block_number);\n    }\n    uint32_t node_impl::get_next_known_hard_fork_block_number(uint32_t block_number) const\n    {\n      auto iter = std::upper_bound(_hard_fork_block_numbers.begin(), _hard_fork_block_numbers.end(),\n                                   block_number);\n      return iter != _hard_fork_block_numbers.end() ? *iter : 0;\n    }\n\n  }  // end namespace detail\n\n\n\n  /////////////////////////////////////////////////////////////////////////////////////////////////////////////\n  // implement node functions, they call the matching function in to detail::node_impl in the correct thread //\n\n#ifdef P2P_IN_DEDICATED_THREAD\n# define INVOKE_IN_IMPL(method_name, ...) \\\n    return my->_thread->async([&](){ return my->method_name(__VA_ARGS__); }, \"thread invoke for method \" BOOST_PP_STRINGIZE(method_name)).wait()\n#else\n# define INVOKE_IN_IMPL(method_name, ...) \\\n    return my->method_name(__VA_ARGS__)\n#endif // P2P_IN_DEDICATED_THREAD\n\n  node::node(const std::string& user_agent) :\n    my(new detail::node_impl(user_agent))\n  {\n  }\n\n  node::~node()\n  {\n  }\n\n  void node::set_node_delegate( node_delegate* del )\n  {\n    fc::thread* delegate_thread = &fc::thread::current();\n    INVOKE_IN_IMPL(set_node_delegate, del, delegate_thread);\n  }\n\n  void node::load_configuration( const fc::path& configuration_directory )\n  {\n    INVOKE_IN_IMPL(load_configuration, configuration_directory);\n  }\n\n  void node::listen_to_p2p_network()\n  {\n    INVOKE_IN_IMPL(listen_to_p2p_network);\n  }\n\n  void node::connect_to_p2p_network()\n  {\n    INVOKE_IN_IMPL(connect_to_p2p_network);\n  }\n\n  void node::add_node( const fc::ip::endpoint& ep )\n  {\n    INVOKE_IN_IMPL(add_node, ep);\n  }\n\n  void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint )\n  {\n    INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint);\n  }\n\n  void node::listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available)\n  {\n    INVOKE_IN_IMPL(listen_on_endpoint, ep, wait_if_not_available);\n  }\n\n  void node::accept_incoming_connections(bool accept)\n  {\n    INVOKE_IN_IMPL(accept_incoming_connections, accept);\n  }\n\n  void node::listen_on_port( uint16_t port, bool wait_if_not_available )\n  {\n    INVOKE_IN_IMPL(listen_on_port, port, wait_if_not_available);\n  }\n\n  fc::ip::endpoint node::get_actual_listening_endpoint() const\n  {\n    INVOKE_IN_IMPL(get_actual_listening_endpoint);\n  }\n\n  std::vector<peer_status> node::get_connected_peers() const\n  {\n    INVOKE_IN_IMPL(get_connected_peers);\n  }\n\n  uint32_t node::get_connection_count() const\n  {\n    INVOKE_IN_IMPL(get_connection_count);\n  }\n\n  void node::broadcast( const message& msg )\n  {\n    INVOKE_IN_IMPL(broadcast, msg);\n  }\n\n  void node::sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers)\n  {\n    INVOKE_IN_IMPL(sync_from, current_head_block, hard_fork_block_numbers);\n  }\n\n  bool node::is_connected() const\n  {\n    INVOKE_IN_IMPL(is_connected);\n  }\n\n  std::vector<potential_peer_record> node::get_potential_peers()const\n  {\n    INVOKE_IN_IMPL(get_potential_peers);\n  }\n\n  void node::set_advanced_node_parameters( const fc::variant_object& params )\n  {\n    INVOKE_IN_IMPL(set_advanced_node_parameters, params);\n  }\n\n  fc::variant_object node::get_advanced_node_parameters()\n  {\n    INVOKE_IN_IMPL(get_advanced_node_parameters);\n  }\n\n  message_propagation_data node::get_transaction_propagation_data( const graphene::net::transaction_id_type& transaction_id )\n  {\n    INVOKE_IN_IMPL(get_transaction_propagation_data, transaction_id);\n  }\n\n  message_propagation_data node::get_block_propagation_data( const graphene::net::block_id_type& block_id )\n  {\n    INVOKE_IN_IMPL(get_block_propagation_data, block_id);\n  }\n\n  node_id_t node::get_node_id() const\n  {\n    INVOKE_IN_IMPL(get_node_id);\n  }\n\n  void node::set_allowed_peers( const std::vector<node_id_t>& allowed_peers )\n  {\n    INVOKE_IN_IMPL(set_allowed_peers, allowed_peers);\n  }\n\n  void node::clear_peer_database()\n  {\n    INVOKE_IN_IMPL(clear_peer_database);\n  }\n\n  void node::set_total_bandwidth_limit(uint32_t upload_bytes_per_second,\n                                       uint32_t download_bytes_per_second)\n  {\n    INVOKE_IN_IMPL(set_total_bandwidth_limit, upload_bytes_per_second, download_bytes_per_second);\n  }\n\n  void node::disable_peer_advertising()\n  {\n    INVOKE_IN_IMPL(disable_peer_advertising);\n  }\n\n  fc::variant_object node::get_call_statistics() const\n  {\n    INVOKE_IN_IMPL(get_call_statistics);\n  }\n\n  fc::variant_object node::network_get_info() const\n  {\n    INVOKE_IN_IMPL(network_get_info);\n  }\n\n  fc::variant_object node::network_get_usage_stats() const\n  {\n    INVOKE_IN_IMPL(network_get_usage_stats);\n  }\n\n  void node::close()\n  {\n    INVOKE_IN_IMPL(close);\n  }\n\n  struct simulated_network::node_info\n  {\n    node_delegate* delegate;\n    fc::future<void> message_sender_task_done;\n    std::queue<message> messages_to_deliver;\n    node_info(node_delegate* delegate) : delegate(delegate) {}\n  };\n\n  simulated_network::~simulated_network()\n  {\n    for( node_info* network_node_info : network_nodes )\n    {\n      network_node_info->message_sender_task_done.cancel_and_wait(\"~simulated_network()\");\n      delete network_node_info;\n    }\n  }\n\n  void simulated_network::message_sender(node_info* destination_node)\n  {\n    while (!destination_node->messages_to_deliver.empty())\n    {\n      try\n      {\n        const message& message_to_deliver = destination_node->messages_to_deliver.front();\n        if (message_to_deliver.msg_type.value() == trx_message_type)\n          destination_node->delegate->handle_transaction(message_to_deliver.as<trx_message>());\n        else if (message_to_deliver.msg_type.value() == block_message_type)\n        {\n          std::vector<fc::uint160_t> contained_transaction_message_ids;\n          destination_node->delegate->handle_block(message_to_deliver.as<block_message>(), false, contained_transaction_message_ids);\n        }\n        else\n          destination_node->delegate->handle_message(message_to_deliver);\n      }\n      catch ( const fc::exception& e )\n      {\n        elog( \"${r}\", (\"r\",e) );\n      }\n      destination_node->messages_to_deliver.pop();\n    }\n  }\n\n  void simulated_network::broadcast( const message& item_to_broadcast  )\n  {\n    for (node_info* network_node_info : network_nodes)\n    {\n      network_node_info->messages_to_deliver.emplace(item_to_broadcast);\n      if (!network_node_info->message_sender_task_done.valid() || network_node_info->message_sender_task_done.ready())\n        network_node_info->message_sender_task_done = fc::async([=](){ message_sender(network_node_info); }, \"simulated_network_sender\");\n    }\n  }\n\n  void simulated_network::add_node_delegate( node_delegate* node_delegate_to_add )\n  {\n    network_nodes.push_back(new node_info(node_delegate_to_add));\n  }\n\n  namespace detail\n  {\n#define ROLLING_WINDOW_SIZE 1000\n#define INITIALIZE_ACCUMULATOR(r, data, method_name) \\\n      , BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator))(boost::accumulators::tag::rolling_window::window_size = ROLLING_WINDOW_SIZE) \\\n      , BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_before_accumulator))(boost::accumulators::tag::rolling_window::window_size = ROLLING_WINDOW_SIZE) \\\n      , BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_after_accumulator))(boost::accumulators::tag::rolling_window::window_size = ROLLING_WINDOW_SIZE)\n\n\n    statistics_gathering_node_delegate_wrapper::statistics_gathering_node_delegate_wrapper(node_delegate* delegate, fc::thread* thread_for_delegate_calls) :\n      _node_delegate(delegate),\n      _thread(thread_for_delegate_calls)\n      BOOST_PP_SEQ_FOR_EACH(INITIALIZE_ACCUMULATOR, unused, NODE_DELEGATE_METHOD_NAMES)\n    {}\n#undef INITIALIZE_ACCUMULATOR\n\n    fc::variant_object statistics_gathering_node_delegate_wrapper::get_call_statistics()\n    {\n      fc::mutable_variant_object statistics;\n      std::ostringstream note;\n      note << \"All times are in microseconds, mean is the average of the last \" << ROLLING_WINDOW_SIZE << \" call times\";\n      statistics[\"_note\"] = note.str();\n\n#define ADD_STATISTICS_FOR_METHOD(r, data, method_name) \\\n      fc::mutable_variant_object BOOST_PP_CAT(method_name, _stats); \\\n      BOOST_PP_CAT(method_name, _stats)[\"min\"] = boost::accumulators::min(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"mean\"] = boost::accumulators::rolling_mean(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"max\"] = boost::accumulators::max(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"sum\"] = boost::accumulators::sum(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_before_min\"] = boost::accumulators::min(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_before_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_before_mean\"] = boost::accumulators::rolling_mean(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_before_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_before_max\"] = boost::accumulators::max(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_before_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_before_sum\"] = boost::accumulators::sum(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_before_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_after_min\"] = boost::accumulators::min(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_after_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_after_mean\"] = boost::accumulators::rolling_mean(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_after_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_after_max\"] = boost::accumulators::max(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_after_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"delay_after_sum\"] = boost::accumulators::sum(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_after_accumulator))); \\\n      BOOST_PP_CAT(method_name, _stats)[\"count\"] = boost::accumulators::count(BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator))); \\\n      statistics[BOOST_PP_STRINGIZE(method_name)] = BOOST_PP_CAT(method_name, _stats);\n\n      BOOST_PP_SEQ_FOR_EACH(ADD_STATISTICS_FOR_METHOD, unused, NODE_DELEGATE_METHOD_NAMES)\n#undef ADD_STATISTICS_FOR_METHOD\n\n      return statistics;\n    }\n\n// define VERBOSE_NODE_DELEGATE_LOGGING to log whenever the node delegate throws exceptions\n//#define VERBOSE_NODE_DELEGATE_LOGGING\n#ifdef VERBOSE_NODE_DELEGATE_LOGGING\n#  define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \\\n    try \\\n    { \\\n      std::shared_ptr<call_statistics_collector> statistics_collector = std::make_shared<call_statistics_collector>( \\\n                                                     #method_name, \\\n                                                     &_ ## method_name ## _execution_accumulator, \\\n                                                     &_ ## method_name ## _delay_before_accumulator, \\\n                                                     &_ ## method_name ## _delay_after_accumulator); \\\n      if (_thread->is_current()) \\\n      { \\\n        call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \\\n        return _node_delegate->method_name(__VA_ARGS__); \\\n      } \\\n      else \\\n        return _thread->async([&, statistics_collector](){ \\\n          call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \\\n          return _node_delegate->method_name(__VA_ARGS__); \\\n        }, \"invoke \" BOOST_STRINGIZE(method_name)).wait(); \\\n    } \\\n    catch (const fc::exception& e) \\\n    { \\\n      dlog(\"node_delegate threw fc::exception: ${e}\", (\"e\", e)); \\\n      throw; \\\n    } \\\n    catch (const std::exception& e) \\\n    { \\\n      dlog(\"node_delegate threw std::exception: ${e}\", (\"e\", e.what())); \\\n      throw; \\\n    } \\\n    catch (...) \\\n    { \\\n      dlog(\"node_delegate threw unrecognized exception\"); \\\n      throw; \\\n    }\n#else\n#  define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \\\n    std::shared_ptr<call_statistics_collector> statistics_collector = std::make_shared<call_statistics_collector>( \\\n                                                   #method_name, \\\n                                                   &_ ## method_name ## _execution_accumulator, \\\n                                                   &_ ## method_name ## _delay_before_accumulator, \\\n                                                   &_ ## method_name ## _delay_after_accumulator); \\\n    if (_thread->is_current()) \\\n    { \\\n      call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \\\n      return _node_delegate->method_name(__VA_ARGS__); \\\n    } \\\n    else \\\n      return _thread->async([&, statistics_collector](){ \\\n        call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \\\n        return _node_delegate->method_name(__VA_ARGS__); \\\n      }, \"invoke \" BOOST_STRINGIZE(method_name)).wait()\n#endif\n\n    bool statistics_gathering_node_delegate_wrapper::has_item( const net::item_id& id )\n    {\n      INVOKE_AND_COLLECT_STATISTICS(has_item, id);\n    }\n\n    void statistics_gathering_node_delegate_wrapper::handle_message( const message& message_to_handle )\n    {\n      INVOKE_AND_COLLECT_STATISTICS(handle_message, message_to_handle);\n    }\n\n    bool statistics_gathering_node_delegate_wrapper::handle_block( const graphene::net::block_message& block_message, bool sync_mode, std::vector<fc::uint160_t>& contained_transaction_message_ids)\n    {\n      INVOKE_AND_COLLECT_STATISTICS(handle_block, block_message, sync_mode, contained_transaction_message_ids);\n    }\n\n    void statistics_gathering_node_delegate_wrapper::handle_transaction( const graphene::net::trx_message& transaction_message )\n    {\n      INVOKE_AND_COLLECT_STATISTICS(handle_transaction, transaction_message);\n    }\n\n    std::vector<item_hash_t> statistics_gathering_node_delegate_wrapper::get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,\n                                                                                       uint32_t& remaining_item_count,\n                                                                                       uint32_t limit /* = 2000 */)\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_block_ids, blockchain_synopsis, remaining_item_count, limit);\n    }\n\n    message statistics_gathering_node_delegate_wrapper::get_item( const item_id& id )\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_item, id);\n    }\n\n    chain_id_type statistics_gathering_node_delegate_wrapper::get_chain_id() const\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_chain_id);\n    }\n\n    std::vector<item_hash_t> statistics_gathering_node_delegate_wrapper::get_blockchain_synopsis(const item_hash_t& reference_point, uint32_t number_of_blocks_after_reference_point)\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_blockchain_synopsis, reference_point, number_of_blocks_after_reference_point);\n    }\n\n    void statistics_gathering_node_delegate_wrapper::sync_status( uint32_t item_type, uint32_t item_count )\n    {\n      INVOKE_AND_COLLECT_STATISTICS(sync_status, item_type, item_count);\n    }\n\n    void statistics_gathering_node_delegate_wrapper::connection_count_changed( uint32_t c )\n    {\n      INVOKE_AND_COLLECT_STATISTICS(connection_count_changed, c);\n    }\n\n    uint32_t statistics_gathering_node_delegate_wrapper::get_block_number(const item_hash_t& block_id)\n    {\n      // this function doesn't need to block,\n      ASSERT_TASK_NOT_PREEMPTED();\n      return _node_delegate->get_block_number(block_id);\n    }\n\n    fc::time_point_sec statistics_gathering_node_delegate_wrapper::get_block_time(const item_hash_t& block_id)\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_block_time, block_id);\n    }\n\n    item_hash_t statistics_gathering_node_delegate_wrapper::get_head_block_id() const\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_head_block_id);\n    }\n\n    uint32_t statistics_gathering_node_delegate_wrapper::estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const\n    {\n      INVOKE_AND_COLLECT_STATISTICS(estimate_last_known_fork_from_git_revision_timestamp, unix_timestamp);\n    }\n\n    void statistics_gathering_node_delegate_wrapper::error_encountered(const std::string& message, const fc::oexception& error)\n    {\n      INVOKE_AND_COLLECT_STATISTICS(error_encountered, message, error);\n    }\n\n    uint8_t statistics_gathering_node_delegate_wrapper::get_current_block_interval_in_seconds() const\n    {\n      INVOKE_AND_COLLECT_STATISTICS(get_current_block_interval_in_seconds);\n    }\n\n#undef INVOKE_AND_COLLECT_STATISTICS\n\n  } // end namespace detail\n\n   // TODO move this function to impl class\n   std::vector<fc::ip::endpoint> node::resolve_string_to_ip_endpoints(const std::string& in)\n   {\n      try\n      {\n         std::string::size_type colon_pos = in.find(':');\n         if (colon_pos == std::string::npos)\n            FC_THROW(\"Missing required port number in endpoint string \\\"${endpoint_string}\\\"\",\n                  (\"endpoint_string\", in));\n         std::string port_string = in.substr(colon_pos + 1);\n         try\n         {\n            uint16_t port = boost::lexical_cast<uint16_t>(port_string);\n\n            std::string hostname = in.substr(0, colon_pos);\n            std::vector<fc::ip::endpoint> endpoints = fc::resolve(hostname, port);\n            if (endpoints.empty())\n               FC_THROW_EXCEPTION( fc::unknown_host_exception,\n                     \"The host name can not be resolved: ${hostname}\",\n                     (\"hostname\", hostname) );\n            return endpoints;\n         }\n         catch (const boost::bad_lexical_cast&)\n         {\n            FC_THROW(\"Bad port: ${port}\", (\"port\", port_string));\n         }\n      }\n      FC_CAPTURE_AND_RETHROW((in))\n   }\n\n   void node::add_seed_nodes(std::vector<std::string> seeds)\n   {\n      for(const std::string& endpoint_string : seeds )\n      {\n         try {\n            add_seed_node(endpoint_string);\n         } catch( const fc::exception& e ) {\n            wlog( \"caught exception ${e} while adding seed node ${endpoint}\",\n                  (\"e\", e.to_detail_string())(\"endpoint\", endpoint_string) );\n         }\n      }\n   }\n\n   void node::add_seed_node(const std::string& in)\n   {\n      INVOKE_IN_IMPL(add_seed_node, in);\n   }\n\n} } // end namespace graphene::net\n"
  },
  {
    "path": "libraries/net/node_impl.hxx",
    "content": "#pragma once\n#include <memory>\n#include <fc/thread/thread.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/network/tcp_socket.hpp>\n#include <graphene/chain/config.hpp>\n#include <graphene/protocol/types.hpp>\n#include <graphene/net/node.hpp>\n#include <graphene/net/core_messages.hpp>\n#include <graphene/net/peer_connection.hpp>\n\nnamespace graphene { namespace net { namespace detail {\n\n// when requesting items from peers, we want to prioritize any blocks before\n// transactions, but otherwise request items in the order we heard about them\nstruct prioritized_item_id\n{\n  item_id  item;\n  unsigned sequence_number;\n  fc::time_point timestamp; // the time we last heard about this item in an inventory message\n\n  prioritized_item_id(const item_id& item, unsigned sequence_number) :\n    item(item),\n    sequence_number(sequence_number),\n    timestamp(fc::time_point::now())\n  {}\n  bool operator<(const prioritized_item_id& rhs) const\n  {\n    static_assert(graphene::net::block_message_type > graphene::net::trx_message_type,\n                  \"block_message_type must be greater than trx_message_type for prioritized_item_ids to sort correctly\");\n    if (item.item_type != rhs.item.item_type)\n      return item.item_type > rhs.item.item_type;\n    return (signed)(rhs.sequence_number - sequence_number) > 0;\n  }\n};\n\nclass statistics_gathering_node_delegate_wrapper : public node_delegate\n{\nprivate:\n  node_delegate *_node_delegate;\n  fc::thread *_thread;\n\n  typedef boost::accumulators::accumulator_set<int64_t, boost::accumulators::stats<boost::accumulators::tag::min,\n                                                                                   boost::accumulators::tag::rolling_mean,\n                                                                                   boost::accumulators::tag::max,\n                                                                                   boost::accumulators::tag::sum,\n                                                                                   boost::accumulators::tag::count> > call_stats_accumulator;\n#define NODE_DELEGATE_METHOD_NAMES (has_item) \\\n                               (handle_message) \\\n                               (handle_block) \\\n                               (handle_transaction) \\\n                               (get_block_ids) \\\n                               (get_item) \\\n                               (get_chain_id) \\\n                               (get_blockchain_synopsis) \\\n                               (sync_status) \\\n                               (connection_count_changed) \\\n                               (get_block_number) \\\n                               (get_block_time) \\\n                               (get_head_block_id) \\\n                               (estimate_last_known_fork_from_git_revision_timestamp) \\\n                               (error_encountered) \\\n                               (get_current_block_interval_in_seconds)\n\n\n\n#define DECLARE_ACCUMULATOR(r, data, method_name) \\\n      mutable call_stats_accumulator BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _execution_accumulator)); \\\n      mutable call_stats_accumulator BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_before_accumulator)); \\\n      mutable call_stats_accumulator BOOST_PP_CAT(_, BOOST_PP_CAT(method_name, _delay_after_accumulator));\n      BOOST_PP_SEQ_FOR_EACH(DECLARE_ACCUMULATOR, unused, NODE_DELEGATE_METHOD_NAMES)\n#undef DECLARE_ACCUMULATOR\n\n      class call_statistics_collector\n      {\n      private:\n        fc::time_point _call_requested_time;\n        fc::time_point _begin_execution_time;\n        fc::time_point _execution_completed_time;\n        const char* _method_name;\n        call_stats_accumulator* _execution_accumulator;\n        call_stats_accumulator* _delay_before_accumulator;\n        call_stats_accumulator* _delay_after_accumulator;\n      public:\n        class actual_execution_measurement_helper\n        {\n          std::shared_ptr<call_statistics_collector> _collector;\n        public:\n          actual_execution_measurement_helper(std::shared_ptr<call_statistics_collector> collector) :\n            _collector(collector)\n          {\n            _collector->starting_execution();\n          }\n          ~actual_execution_measurement_helper()\n          {\n            _collector->execution_completed();\n          }\n        };\n        call_statistics_collector(const char* method_name,\n                                  call_stats_accumulator* execution_accumulator,\n                                  call_stats_accumulator* delay_before_accumulator,\n                                  call_stats_accumulator* delay_after_accumulator) :\n          _call_requested_time(fc::time_point::now()),\n          _method_name(method_name),\n          _execution_accumulator(execution_accumulator),\n          _delay_before_accumulator(delay_before_accumulator),\n          _delay_after_accumulator(delay_after_accumulator)\n        {}\n        ~call_statistics_collector()\n        {\n          fc::time_point end_time(fc::time_point::now());\n          fc::microseconds actual_execution_time(_execution_completed_time - _begin_execution_time);\n          fc::microseconds delay_before(_begin_execution_time - _call_requested_time);\n          fc::microseconds delay_after(end_time - _execution_completed_time);\n          fc::microseconds total_duration(actual_execution_time + delay_before + delay_after);\n          (*_execution_accumulator)(actual_execution_time.count());\n          (*_delay_before_accumulator)(delay_before.count());\n          (*_delay_after_accumulator)(delay_after.count());\n          if (total_duration > fc::milliseconds(500))\n          {\n            ilog(\"Call to method node_delegate::${method} took ${total_duration}us, longer than our target maximum of 500ms\",\n                 (\"method\", _method_name)\n                 (\"total_duration\", total_duration.count()));\n            ilog(\"Actual execution took ${execution_duration}us, with a ${delegate_delay}us delay before the delegate thread started \"\n                 \"executing the method, and a ${p2p_delay}us delay after it finished before the p2p thread started processing the response\",\n                 (\"execution_duration\", actual_execution_time)\n                 (\"delegate_delay\", delay_before)\n                 (\"p2p_delay\", delay_after));\n          }\n        }\n        void starting_execution()\n        {\n          _begin_execution_time = fc::time_point::now();\n        }\n        void execution_completed()\n        {\n          _execution_completed_time = fc::time_point::now();\n        }\n      };\n    public:\n      statistics_gathering_node_delegate_wrapper(node_delegate* delegate, fc::thread* thread_for_delegate_calls);\n\n      fc::variant_object get_call_statistics();\n\n      bool has_item( const graphene::net::item_id& id ) override;\n      void handle_message( const message& ) override;\n      bool handle_block( const graphene::net::block_message& block_message, bool sync_mode, std::vector<fc::uint160_t>& contained_transaction_message_ids ) override;\n      void handle_transaction( const graphene::net::trx_message& transaction_message ) override;\n      std::vector<item_hash_t> get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,\n                                             uint32_t& remaining_item_count,\n                                             uint32_t limit = 2000) override;\n      message get_item( const item_id& id ) override;\n      graphene::protocol::chain_id_type get_chain_id() const override;\n      std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,\n                                                       uint32_t number_of_blocks_after_reference_point) override;\n      void     sync_status( uint32_t item_type, uint32_t item_count ) override;\n      void     connection_count_changed( uint32_t c ) override;\n      uint32_t get_block_number(const item_hash_t& block_id) override;\n      fc::time_point_sec get_block_time(const item_hash_t& block_id) override;\n      item_hash_t get_head_block_id() const override;\n      uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const override;\n      void error_encountered(const std::string& message, const fc::oexception& error) override;\n      uint8_t get_current_block_interval_in_seconds() const override;\n    };\n\nclass node_impl : public peer_connection_delegate\n{\n    public:\n#ifdef P2P_IN_DEDICATED_THREAD\n      std::shared_ptr<fc::thread> _thread;\n#endif // P2P_IN_DEDICATED_THREAD\n      std::unique_ptr<statistics_gathering_node_delegate_wrapper> _delegate;\n      fc::sha256           _chain_id;\n\n#define NODE_CONFIGURATION_FILENAME      \"node_config.json\"\n#define POTENTIAL_PEER_DATABASE_FILENAME \"peers.json\"\n      fc::path             _node_configuration_directory;\n      node_configuration   _node_configuration;\n\n      /// stores the endpoint we're listening on.  This will be the same as\n      // _node_configuration.listen_endpoint, unless that endpoint was already\n      // in use.\n      fc::ip::endpoint     _actual_listening_endpoint;\n\n      /// we determine whether we're firewalled by asking other nodes.  Store the result here:\n      firewalled_state     _is_firewalled;\n      /// if we're behind NAT, our listening endpoint address will appear different to the rest of the world.  store it here.\n      fc::optional<fc::ip::endpoint> _publicly_visible_listening_endpoint;\n      fc::time_point       _last_firewall_check_message_sent;\n\n      /// used by the task that manages connecting to peers\n      // @{\n      std::list<potential_peer_record> _add_once_node_list; /// list of peers we want to connect to as soon as possible\n\n      peer_database             _potential_peer_db;\n      fc::promise<void>::ptr    _retrigger_connect_loop_promise;\n      bool                      _potential_peer_database_updated;\n      fc::future<void>          _p2p_network_connect_loop_done;\n      // @}\n\n      /// used by the task that fetches sync items during synchronization\n      // @{\n      fc::promise<void>::ptr    _retrigger_fetch_sync_items_loop_promise;\n      bool                      _sync_items_to_fetch_updated;\n      fc::future<void>          _fetch_sync_items_loop_done;\n\n      typedef std::unordered_map<graphene::net::block_id_type, fc::time_point> active_sync_requests_map;\n\n      active_sync_requests_map              _active_sync_requests; /// list of sync blocks we've asked for from peers but have not yet received\n      std::list<graphene::net::block_message> _new_received_sync_items; /// list of sync blocks we've just received but haven't yet tried to process\n      std::list<graphene::net::block_message> _received_sync_items; /// list of sync blocks we've received, but can't yet process because we are still missing blocks that come earlier in the chain\n      // @}\n\n      fc::future<void> _process_backlog_of_sync_blocks_done;\n      bool _suspend_fetching_sync_blocks;\n\n      /// used by the task that fetches items during normal operation\n      // @{\n      fc::promise<void>::ptr _retrigger_fetch_item_loop_promise;\n      bool                   _items_to_fetch_updated;\n      fc::future<void>       _fetch_item_loop_done;\n\n      struct item_id_index{};\n      typedef boost::multi_index_container<prioritized_item_id,\n                                           boost::multi_index::indexed_by<boost::multi_index::ordered_unique<boost::multi_index::identity<prioritized_item_id> >,\n                                                                          boost::multi_index::hashed_unique<boost::multi_index::tag<item_id_index>,\n                                                                                                            boost::multi_index::member<prioritized_item_id, item_id, &prioritized_item_id::item>,\n                                                                                                            std::hash<item_id> > >\n                                           > items_to_fetch_set_type;\n      unsigned _items_to_fetch_sequence_counter;\n      items_to_fetch_set_type _items_to_fetch; /// list of items we know another peer has and we want\n      peer_connection::timestamped_items_set_type _recently_failed_items; /// list of transactions we've recently pushed and had rejected by the delegate\n      // @}\n\n      /// used by the task that advertises inventory during normal operation\n      // @{\n      fc::promise<void>::ptr        _retrigger_advertise_inventory_loop_promise;\n      fc::future<void>              _advertise_inventory_loop_done;\n      std::unordered_set<item_id>   _new_inventory; /// list of items we have received but not yet advertised to our peers\n      // @}\n\n      fc::future<void>     _terminate_inactive_connections_loop_done;\n      uint8_t _recent_block_interval_in_seconds; // a cached copy of the block interval, to avoid a thread hop to the blockchain to get the current value\n\n      std::string          _user_agent_string;\n      /** _node_public_key is a key automatically generated when the client is first run, stored in\n       * node_config.json.  It doesn't really have much of a purpose yet, there was just some thought\n       * that we might someday have a use for nodes having a private key (sent in hello messages)\n       */\n      node_id_t            _node_public_key;\n      /**\n       * _node_id is a random number generated each time the client is launched, used to prevent us\n       * from connecting to the same client multiple times (sent in hello messages).\n       * Since this was introduced after the hello_message was finalized, this is sent in the\n       * user_data field.\n       * While this shares the same underlying type as a public key, it is really just a random\n       * number.\n       */\n      node_id_t            _node_id;\n\n      /** if we have less than `_desired_number_of_connections`, we will try to connect with more nodes */\n      uint32_t             _desired_number_of_connections;\n      /** if we have _maximum_number_of_connections or more, we will refuse any inbound connections */\n      uint32_t             _maximum_number_of_connections;\n      /** retry connections to peers that have failed or rejected us this often, in seconds */\n      uint32_t              _peer_connection_retry_timeout;\n      /** how many seconds of inactivity are permitted before disconnecting a peer */\n      uint32_t              _peer_inactivity_timeout;\n\n      fc::tcp_server       _tcp_server;\n      fc::future<void>     _accept_loop_complete;\n\n      /** Stores all connections which have not yet finished key exchange or are still sending initial handshaking messages\n       * back and forth (not yet ready to initiate syncing) */\n      std::unordered_set<graphene::net::peer_connection_ptr>                     _handshaking_connections;\n      /** stores fully established connections we're either syncing with or in normal operation with */\n      std::unordered_set<graphene::net::peer_connection_ptr>                     _active_connections;\n      /** stores connections we've closed (sent closing message, not actually closed), but are still waiting for the remote end to close before we delete them */\n      std::unordered_set<graphene::net::peer_connection_ptr>                     _closing_connections;\n      /** stores connections we've closed, but are still waiting for the OS to notify us that the socket is really closed */\n      std::unordered_set<graphene::net::peer_connection_ptr>                     _terminating_connections;\n\n      boost::circular_buffer<item_hash_t> _most_recent_blocks_accepted; // the /n/ most recent blocks we've accepted (currently tuned to the max number of connections)\n\n      uint32_t _sync_item_type;\n      uint32_t _total_number_of_unfetched_items; /// the number of items we still need to fetch while syncing\n      std::vector<uint32_t> _hard_fork_block_numbers; /// list of all block numbers where there are hard forks\n\n      blockchain_tied_message_cache _message_cache; /// cache message we have received and might be required to provide to other peers via inventory requests\n\n      fc::rate_limiting_group _rate_limiter;\n\n      uint32_t _last_reported_number_of_connections; // number of connections last reported to the client (to avoid sending duplicate messages)\n\n      bool _peer_advertising_disabled;\n\n      fc::future<void> _fetch_updated_peer_lists_loop_done;\n\n      boost::circular_buffer<uint32_t> _average_network_read_speed_seconds;\n      boost::circular_buffer<uint32_t> _average_network_write_speed_seconds;\n      boost::circular_buffer<uint32_t> _average_network_read_speed_minutes;\n      boost::circular_buffer<uint32_t> _average_network_write_speed_minutes;\n      boost::circular_buffer<uint32_t> _average_network_read_speed_hours;\n      boost::circular_buffer<uint32_t> _average_network_write_speed_hours;\n      unsigned _average_network_usage_second_counter;\n      unsigned _average_network_usage_minute_counter;\n\n      fc::time_point_sec _bandwidth_monitor_last_update_time;\n      fc::future<void> _bandwidth_monitor_loop_done;\n\n      fc::future<void> _dump_node_status_task_done;\n\n      /* We have two alternate paths through the schedule_peer_for_deletion code -- one that\n       * uses a mutex to prevent one fiber from adding items to the queue while another is deleting\n       * items from it, and one that doesn't.  The one that doesn't is simpler and more efficient\n       * code, but we're keeping around the version that uses the mutex because it crashes, and\n       * this crash probably indicates a bug in our underlying threading code that needs\n       * fixing.  To produce the bug, define USE_PEERS_TO_DELETE_MUTEX and then connect up\n       * to the network and set your desired/max connection counts high\n       */\n//#define USE_PEERS_TO_DELETE_MUTEX 1\n#ifdef USE_PEERS_TO_DELETE_MUTEX\n      fc::mutex _peers_to_delete_mutex;\n#endif\n      std::list<peer_connection_ptr> _peers_to_delete;\n      fc::future<void> _delayed_peer_deletion_task_done;\n\n#ifdef ENABLE_P2P_DEBUGGING_API\n      std::set<node_id_t> _allowed_peers;\n#endif // ENABLE_P2P_DEBUGGING_API\n\n      bool _node_is_shutting_down; // set to true when we begin our destructor, used to prevent us from starting new tasks while we're shutting down\n\n      unsigned _maximum_number_of_blocks_to_handle_at_one_time;\n      unsigned _maximum_number_of_sync_blocks_to_prefetch;\n      unsigned _maximum_blocks_per_peer_during_syncing;\n\n      std::list<fc::future<void> > _handle_message_calls_in_progress;\n\n      /// used by the task that checks whether addresses of seed nodes have been updated\n      // @{\n      boost::container::flat_set<std::string> _seed_nodes;\n      fc::future<void> _update_seed_nodes_loop_done;\n      void update_seed_nodes_task();\n      void schedule_next_update_seed_nodes_task();\n      // @}\n\n      node_impl(const std::string& user_agent);\n      virtual ~node_impl();\n\n      void save_node_configuration();\n\n      void p2p_network_connect_loop();\n      void trigger_p2p_network_connect_loop();\n\n      bool have_already_received_sync_item( const item_hash_t& item_hash );\n      void request_sync_item_from_peer( const peer_connection_ptr& peer, const item_hash_t& item_to_request );\n      void request_sync_items_from_peer( const peer_connection_ptr& peer, const std::vector<item_hash_t>& items_to_request );\n      void fetch_sync_items_loop();\n      void trigger_fetch_sync_items_loop();\n\n      bool is_item_in_any_peers_inventory(const item_id& item) const;\n      void fetch_items_loop();\n      void trigger_fetch_items_loop();\n\n      void advertise_inventory_loop();\n      void trigger_advertise_inventory_loop();\n\n      void terminate_inactive_connections_loop();\n\n      void fetch_updated_peer_lists_loop();\n      void update_bandwidth_data(uint32_t bytes_read_this_second, uint32_t bytes_written_this_second);\n      void bandwidth_monitor_loop();\n      void dump_node_status_task();\n\n      bool is_accepting_new_connections();\n      bool is_wanting_new_connections();\n      uint32_t get_number_of_connections();\n      peer_connection_ptr get_peer_by_node_id(const node_id_t& id);\n\n      bool is_already_connected_to_id(const node_id_t& node_id);\n      bool merge_address_info_with_potential_peer_database( const std::vector<address_info> addresses );\n      void display_current_connections();\n      uint32_t calculate_unsynced_block_count_from_all_peers();\n      std::vector<item_hash_t> create_blockchain_synopsis_for_peer( const peer_connection* peer );\n      void fetch_next_batch_of_item_ids_from_peer( peer_connection* peer, bool reset_fork_tracking_data_for_peer = false );\n\n      fc::variant_object generate_hello_user_data();\n      void parse_hello_user_data_for_peer( peer_connection* originating_peer, const fc::variant_object& user_data );\n\n      void on_message( peer_connection* originating_peer,\n                       const message& received_message ) override;\n\n      void on_hello_message( peer_connection* originating_peer,\n                             const hello_message& hello_message_received );\n\n      void on_connection_accepted_message( peer_connection* originating_peer,\n                                           const connection_accepted_message& connection_accepted_message_received );\n\n      void on_connection_rejected_message( peer_connection* originating_peer,\n                                           const connection_rejected_message& connection_rejected_message_received );\n\n      void on_address_request_message( peer_connection* originating_peer,\n                                       const address_request_message& address_request_message_received );\n\n      void on_address_message( peer_connection* originating_peer,\n                               const address_message& address_message_received );\n\n      void on_fetch_blockchain_item_ids_message( peer_connection* originating_peer,\n                                                 const fetch_blockchain_item_ids_message& fetch_blockchain_item_ids_message_received );\n\n      void on_blockchain_item_ids_inventory_message( peer_connection* originating_peer,\n                                                     const blockchain_item_ids_inventory_message& blockchain_item_ids_inventory_message_received );\n\n      void on_fetch_items_message( peer_connection* originating_peer,\n                                   const fetch_items_message& fetch_items_message_received );\n\n      void on_item_not_available_message( peer_connection* originating_peer,\n                                          const item_not_available_message& item_not_available_message_received );\n\n      void on_item_ids_inventory_message( peer_connection* originating_peer,\n                                          const item_ids_inventory_message& item_ids_inventory_message_received );\n\n      void on_closing_connection_message( peer_connection* originating_peer,\n                                          const closing_connection_message& closing_connection_message_received );\n\n      void on_current_time_request_message( peer_connection* originating_peer,\n                                            const current_time_request_message& current_time_request_message_received );\n\n      void on_current_time_reply_message( peer_connection* originating_peer,\n                                          const current_time_reply_message& current_time_reply_message_received );\n\n      void forward_firewall_check_to_next_available_peer(firewall_check_state_data* firewall_check_state);\n\n      void on_check_firewall_message(peer_connection* originating_peer,\n                                     const check_firewall_message& check_firewall_message_received);\n\n      void on_check_firewall_reply_message(peer_connection* originating_peer,\n                                           const check_firewall_reply_message& check_firewall_reply_message_received);\n\n      void on_get_current_connections_request_message(peer_connection* originating_peer,\n                                                      const get_current_connections_request_message& get_current_connections_request_message_received);\n\n      void on_get_current_connections_reply_message(peer_connection* originating_peer,\n                                                    const get_current_connections_reply_message& get_current_connections_reply_message_received);\n\n      void on_connection_closed(peer_connection* originating_peer) override;\n\n      void send_sync_block_to_node_delegate(const graphene::net::block_message& block_message_to_send);\n      void process_backlog_of_sync_blocks();\n      void trigger_process_backlog_of_sync_blocks();\n      void process_block_during_sync(peer_connection* originating_peer, const graphene::net::block_message& block_message, const message_hash_type& message_hash);\n      void process_block_during_normal_operation(peer_connection* originating_peer, const graphene::net::block_message& block_message, const message_hash_type& message_hash);\n      void process_block_message(peer_connection* originating_peer, const message& message_to_process, const message_hash_type& message_hash);\n\n      void process_ordinary_message(peer_connection* originating_peer, const message& message_to_process, const message_hash_type& message_hash);\n\n      void start_synchronizing();\n      void start_synchronizing_with_peer(const peer_connection_ptr& peer);\n\n      void new_peer_just_added(const peer_connection_ptr& peer); /// called after a peer finishes handshaking, kicks off syncing\n\n      void close();\n\n      void accept_connection_task(peer_connection_ptr new_peer);\n      void accept_loop();\n      void send_hello_message(const peer_connection_ptr& peer);\n      void connect_to_task(peer_connection_ptr new_peer, const fc::ip::endpoint& remote_endpoint);\n      bool is_connection_to_endpoint_in_progress(const fc::ip::endpoint& remote_endpoint);\n\n      void move_peer_to_active_list(const peer_connection_ptr& peer);\n      void move_peer_to_closing_list(const peer_connection_ptr& peer);\n      void move_peer_to_terminating_list(const peer_connection_ptr& peer);\n\n      peer_connection_ptr get_connection_to_endpoint( const fc::ip::endpoint& remote_endpoint );\n\n      void dump_node_status();\n\n      void delayed_peer_deletion_task();\n      void schedule_peer_for_deletion(const peer_connection_ptr& peer_to_delete);\n\n      void disconnect_from_peer( peer_connection* originating_peer,\n                               const std::string& reason_for_disconnect,\n                                bool caused_by_error = false,\n                               const fc::oexception& additional_data = fc::oexception() );\n\n      // methods implementing node's public interface\n      void set_node_delegate(node_delegate* del, fc::thread* thread_for_delegate_calls);\n      void load_configuration( const fc::path& configuration_directory );\n      void listen_to_p2p_network();\n      void connect_to_p2p_network();\n      void add_node( const fc::ip::endpoint& ep );\n      void add_seed_node( const std::string& seed_string );\n      void resolve_seed_node_and_add( const std::string& seed_string );\n      void initiate_connect_to(const peer_connection_ptr& peer);\n      void connect_to_endpoint(const fc::ip::endpoint& ep);\n      void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available);\n      void accept_incoming_connections(bool accept);\n      void listen_on_port( uint16_t port, bool wait_if_not_available );\n\n      fc::ip::endpoint         get_actual_listening_endpoint() const;\n      std::vector<peer_status> get_connected_peers() const;\n      uint32_t                 get_connection_count() const;\n\n      void broadcast(const message& item_to_broadcast, const message_propagation_data& propagation_data);\n      void broadcast(const message& item_to_broadcast);\n      void sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers);\n      bool is_connected() const;\n      std::vector<potential_peer_record> get_potential_peers() const;\n      void set_advanced_node_parameters( const fc::variant_object& params );\n\n      fc::variant_object         get_advanced_node_parameters();\n      message_propagation_data   get_transaction_propagation_data( const graphene::net::transaction_id_type& transaction_id );\n      message_propagation_data   get_block_propagation_data( const graphene::net::block_id_type& block_id );\n\n      node_id_t                  get_node_id() const;\n      void                       set_allowed_peers( const std::vector<node_id_t>& allowed_peers );\n      void                       clear_peer_database();\n      void                       set_total_bandwidth_limit( uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second );\n      void                       disable_peer_advertising();\n      fc::variant_object         get_call_statistics() const;\n      message                    get_message_for_item(const item_id& item) override;\n\n      fc::variant_object         network_get_info() const;\n      fc::variant_object         network_get_usage_stats() const;\n\n      bool is_hard_fork_block(uint32_t block_number) const;\n      uint32_t get_next_known_hard_fork_block_number(uint32_t block_number) const;\n    }; // end class node_impl\n\n}}} // end of namespace graphene::net::detail\n"
  },
  {
    "path": "libraries/net/peer_connection.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/net/peer_connection.hpp>\n#include <graphene/net/exceptions.hpp>\n#include <graphene/net/config.hpp>\n#include <graphene/chain/config.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/thread/thread.hpp>\n\n#include <boost/scope_exit.hpp>\n\n#ifdef DEFAULT_LOGGER\n# undef DEFAULT_LOGGER\n#endif\n#define DEFAULT_LOGGER \"p2p\"\n\n#ifndef NDEBUG\n# define VERIFY_CORRECT_THREAD() assert(_thread->is_current())\n#else\n# define VERIFY_CORRECT_THREAD() do {} while (0)\n#endif\n\nnamespace graphene { namespace net\n  {\n    message peer_connection::real_queued_message::get_message(peer_connection_delegate*)\n    {\n      if (message_send_time_field_offset != (size_t)-1)\n      {\n        // patch the current time into the message.  Since this operates on the packed version of the structure,\n        // it won't work for anything after a variable-length field\n        std::vector<char> packed_current_time = fc::raw::pack(fc::time_point::now());\n        assert(message_send_time_field_offset + packed_current_time.size() <= message_to_send.data.size());\n        memcpy(message_to_send.data.data() + message_send_time_field_offset,\n               packed_current_time.data(), packed_current_time.size());\n      }\n      return message_to_send;\n    }\n    size_t peer_connection::real_queued_message::get_size_in_queue()\n    {\n      return message_to_send.data.size();\n    }\n    message peer_connection::virtual_queued_message::get_message(peer_connection_delegate* node)\n    {\n      return node->get_message_for_item(item_to_send);\n    }\n\n    size_t peer_connection::virtual_queued_message::get_size_in_queue()\n    {\n      return sizeof(item_id);\n    }\n\n    peer_connection::peer_connection(peer_connection_delegate* delegate) :\n      _node(delegate),\n      _message_connection(this),\n      _total_queued_messages_size(0),\n      direction(peer_connection_direction::unknown),\n      is_firewalled(firewalled_state::unknown),\n      our_state(our_connection_state::disconnected),\n      they_have_requested_close(false),\n      their_state(their_connection_state::disconnected),\n      we_have_requested_close(false),\n      negotiation_status(connection_negotiation_status::disconnected),\n      number_of_unfetched_item_ids(0),\n      peer_needs_sync_items_from_us(true),\n      we_need_sync_items_from_peer(true),\n      inhibit_fetching_sync_blocks(false),\n      transaction_fetching_inhibited_until(fc::time_point::min()),\n      last_known_fork_block_number(0),\n      firewall_check_state(nullptr),\n#ifndef NDEBUG\n      _thread(&fc::thread::current()),\n      _send_message_queue_tasks_running(0),\n#endif\n      _currently_handling_message(false)\n    {\n    }\n\n    peer_connection_ptr peer_connection::make_shared(peer_connection_delegate* delegate)\n    {\n      // The lifetime of peer_connection objects is managed by shared_ptrs in node.  The peer_connection\n      // is responsible for notifying the node when it should be deleted, and the process of deleting it\n      // cleans up the peer connection's asynchronous tasks which are responsible for notifying the node\n      // when it should be deleted.\n      // To ease this vicious cycle, we slightly delay the execution of the destructor until the\n      // current task yields.  In the (not uncommon) case where it is the task executing\n      // connect_to or read_loop, this allows the task to finish before the destructor is forced\n      // to cancel it.\n      return peer_connection_ptr(new peer_connection(delegate));\n      //, [](peer_connection* peer_to_delete){ fc::async([peer_to_delete](){delete peer_to_delete;}); });\n    }\n\n    void peer_connection::destroy()\n    {\n      VERIFY_CORRECT_THREAD();\n\n#if 0 // this gets too verbose\n#ifndef NDEBUG\n      struct scope_logger {\n        fc::optional<fc::ip::endpoint> endpoint;\n        scope_logger(const fc::optional<fc::ip::endpoint>& endpoint) : endpoint(endpoint) { dlog(\"entering peer_connection::destroy() for peer ${endpoint}\", (\"endpoint\", endpoint)); }\n        ~scope_logger() { dlog(\"leaving peer_connection::destroy() for peer ${endpoint}\", (\"endpoint\", endpoint)); }\n      } send_message_scope_logger(get_remote_endpoint());\n#endif\n#endif\n\n      try\n      {\n        dlog(\"calling close_connection()\");\n        close_connection();\n        dlog(\"close_connection completed normally\");\n      }\n      catch ( const fc::canceled_exception& )\n      {\n        assert(false && \"the task that deletes peers should not be canceled because it will prevent us from cleaning up correctly\");\n      }\n      catch ( ... )\n      {\n        dlog(\"close_connection threw\");\n      }\n\n      try\n      {\n        dlog(\"canceling _send_queued_messages task\");\n        _send_queued_messages_done.cancel_and_wait(__FUNCTION__);\n        dlog(\"cancel_and_wait completed normally\");\n      }\n      catch( const fc::exception& e )\n      {\n        wlog(\"Unexpected exception from peer_connection's send_queued_messages_task : ${e}\", (\"e\", e));\n      }\n      catch( ... )\n      {\n        wlog(\"Unexpected exception from peer_connection's send_queued_messages_task\");\n      }\n\n      try\n      {\n        dlog(\"canceling accept_or_connect_task\");\n        accept_or_connect_task_done.cancel_and_wait(__FUNCTION__);\n        dlog(\"accept_or_connect_task completed normally\");\n      }\n      catch( const fc::exception& e )\n      {\n        wlog(\"Unexpected exception from peer_connection's accept_or_connect_task : ${e}\", (\"e\", e));\n      }\n      catch( ... )\n      {\n        wlog(\"Unexpected exception from peer_connection's accept_or_connect_task\");\n      }\n\n      _message_connection.destroy_connection(); // shut down the read loop\n    }\n\n    peer_connection::~peer_connection()\n    {\n      VERIFY_CORRECT_THREAD();\n      destroy();\n    }\n\n    fc::tcp_socket& peer_connection::get_socket()\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_socket();\n    }\n\n    void peer_connection::accept_connection()\n    {\n      VERIFY_CORRECT_THREAD();\n\n      struct scope_logger {\n        scope_logger() { dlog(\"entering peer_connection::accept_connection()\"); }\n        ~scope_logger() { dlog(\"leaving peer_connection::accept_connection()\"); }\n      } accept_connection_scope_logger;\n\n      try\n      {\n        assert( our_state == our_connection_state::disconnected &&\n                their_state == their_connection_state::disconnected );\n        direction = peer_connection_direction::inbound;\n        negotiation_status = connection_negotiation_status::accepting;\n        _message_connection.accept();           // perform key exchange\n        negotiation_status = connection_negotiation_status::accepted;\n        _remote_endpoint = _message_connection.get_socket().remote_endpoint();\n\n        // firewall-detecting info is pretty useless for inbound connections, but initialize\n        // it the best we can\n        fc::ip::endpoint local_endpoint = _message_connection.get_socket().local_endpoint();\n        inbound_address = local_endpoint.get_address();\n        inbound_port = local_endpoint.port();\n        outbound_port = inbound_port;\n\n        their_state = their_connection_state::just_connected;\n        our_state = our_connection_state::just_connected;\n        ilog( \"established inbound connection from ${remote_endpoint}, sending hello\", (\"remote_endpoint\", _message_connection.get_socket().remote_endpoint() ) );\n      }\n      catch ( const fc::exception& e )\n      {\n        wlog( \"error accepting connection ${e}\", (\"e\", e.to_detail_string() ) );\n        throw;\n      }\n    }\n\n    void peer_connection::connect_to( const fc::ip::endpoint& remote_endpoint, fc::optional<fc::ip::endpoint> local_endpoint )\n    {\n      VERIFY_CORRECT_THREAD();\n      try\n      {\n        assert( our_state == our_connection_state::disconnected &&\n                their_state == their_connection_state::disconnected );\n        direction = peer_connection_direction::outbound;\n\n        _remote_endpoint = remote_endpoint;\n        if( local_endpoint )\n        {\n          // the caller wants us to bind the local side of this socket to a specific ip/port\n          // This depends on the ip/port being unused, and on being able to set the\n          // SO_REUSEADDR/SO_REUSEPORT flags, and either of these might fail, so we need to\n          // detect if this fails.\n          try\n          {\n            _message_connection.bind( *local_endpoint );\n          }\n          catch ( const fc::canceled_exception& )\n          {\n            throw;\n          }\n          catch ( const fc::exception& except )\n          {\n            wlog( \"Failed to bind to desired local endpoint ${endpoint}, will connect using an OS-selected endpoint: ${except}\", (\"endpoint\", *local_endpoint )(\"except\", except ) );\n          }\n        }\n        negotiation_status = connection_negotiation_status::connecting;\n        _message_connection.connect_to( remote_endpoint );\n        negotiation_status = connection_negotiation_status::connected;\n        their_state = their_connection_state::just_connected;\n        our_state = our_connection_state::just_connected;\n        ilog( \"established outbound connection to ${remote_endpoint}\", (\"remote_endpoint\", remote_endpoint ) );\n      }\n      catch ( fc::exception& e )\n      {\n        wlog( \"error connecting to peer ${remote_endpoint}: ${e}\", (\"remote_endpoint\", remote_endpoint )(\"e\", e.to_detail_string() ) );\n        throw;\n      }\n    } // connect_to()\n\n    void peer_connection::on_message( message_oriented_connection* originating_connection, const message& received_message )\n    {\n      VERIFY_CORRECT_THREAD();\n      _currently_handling_message = true;\n      BOOST_SCOPE_EXIT(this_) {\n        this_->_currently_handling_message = false;\n      } BOOST_SCOPE_EXIT_END\n      _node->on_message( this, received_message );\n    }\n\n    void peer_connection::on_connection_closed( message_oriented_connection* originating_connection )\n    {\n      VERIFY_CORRECT_THREAD();\n      negotiation_status = connection_negotiation_status::closed;\n      _node->on_connection_closed( this );\n    }\n\n    void peer_connection::send_queued_messages_task()\n    {\n      VERIFY_CORRECT_THREAD();\n#ifndef NDEBUG\n      struct counter {\n        unsigned& _send_message_queue_tasks_counter;\n        counter(unsigned& var) : _send_message_queue_tasks_counter(var) { /* dlog(\"entering peer_connection::send_queued_messages_task()\"); */ assert(_send_message_queue_tasks_counter == 0); ++_send_message_queue_tasks_counter; }\n        ~counter() { assert(_send_message_queue_tasks_counter == 1); --_send_message_queue_tasks_counter; /* dlog(\"leaving peer_connection::send_queued_messages_task()\"); */ }\n      } concurrent_invocation_counter(_send_message_queue_tasks_running);\n#endif\n      while (!_queued_messages.empty())\n      {\n        _queued_messages.front()->transmission_start_time = fc::time_point::now();\n        message message_to_send = _queued_messages.front()->get_message(_node);\n        try\n        {\n          //dlog(\"peer_connection::send_queued_messages_task() calling message_oriented_connection::send_message() \"\n          //     \"to send message of type ${type} for peer ${endpoint}\",\n          //     (\"type\", message_to_send.msg_type)(\"endpoint\", get_remote_endpoint()));\n          _message_connection.send_message(message_to_send);\n          //dlog(\"peer_connection::send_queued_messages_task()'s call to message_oriented_connection::send_message() completed normally for peer ${endpoint}\",\n          //     (\"endpoint\", get_remote_endpoint()));\n        }\n        catch (const fc::canceled_exception&)\n        {\n          dlog(\"message_oriented_connection::send_message() was canceled, rethrowing canceled_exception\");\n          throw;\n        }\n        catch (const fc::exception& send_error)\n        {\n          wlog(\"Error sending message: ${exception}.  Closing connection.\", (\"exception\", send_error));\n          try\n          {\n            close_connection();\n          }\n          catch (const fc::exception& close_error)\n          {\n            wlog(\"Caught error while closing connection: ${exception}\", (\"exception\", close_error));\n          }\n          return;\n        }\n        catch (const std::exception& e)\n        {\n          wlog(\"message_oriented_exception::send_message() threw a std::exception(): ${what}\", (\"what\", e.what()));\n        }\n        catch (...)\n        {\n          wlog(\"message_oriented_exception::send_message() threw an unhandled exception\");\n        }\n        _queued_messages.front()->transmission_finish_time = fc::time_point::now();\n        _total_queued_messages_size -= _queued_messages.front()->get_size_in_queue();\n        _queued_messages.pop();\n      }\n      //dlog(\"leaving peer_connection::send_queued_messages_task() due to queue exhaustion\");\n    }\n\n    void peer_connection::send_queueable_message(std::unique_ptr<queued_message>&& message_to_send)\n    {\n      VERIFY_CORRECT_THREAD();\n      _total_queued_messages_size += message_to_send->get_size_in_queue();\n      _queued_messages.emplace(std::move(message_to_send));\n      if (_total_queued_messages_size > GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES)\n      {\n        wlog(\"send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)\",\n             (\"max\", GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES)(\"current\", _total_queued_messages_size));\n        try\n        {\n          close_connection();\n        }\n        catch (const fc::exception& e)\n        {\n          wlog(\"Caught error while closing connection: ${exception}\", (\"exception\", e));\n        }\n        return;\n      }\n\n      if( _send_queued_messages_done.valid() && _send_queued_messages_done.canceled() )\n        FC_THROW_EXCEPTION(fc::exception, \"Attempting to send a message on a connection that is being shut down\");\n\n      if (!_send_queued_messages_done.valid() || _send_queued_messages_done.ready())\n      {\n        //dlog(\"peer_connection::send_message() is firing up send_queued_message_task\");\n        _send_queued_messages_done = fc::async([this](){ send_queued_messages_task(); }, \"send_queued_messages_task\");\n      }\n      //else\n      //  dlog(\"peer_connection::send_message() doesn't need to fire up send_queued_message_task, it's already running\");\n    }\n\n    void peer_connection::send_message(const message& message_to_send, size_t message_send_time_field_offset)\n    {\n      VERIFY_CORRECT_THREAD();\n      //dlog(\"peer_connection::send_message() enqueueing message of type ${type} for peer ${endpoint}\",\n      //     (\"type\", message_to_send.msg_type)(\"endpoint\", get_remote_endpoint()));\n      std::unique_ptr<queued_message> message_to_enqueue(new real_queued_message(message_to_send, message_send_time_field_offset));\n      send_queueable_message(std::move(message_to_enqueue));\n    }\n\n    void peer_connection::send_item(const item_id& item_to_send)\n    {\n      VERIFY_CORRECT_THREAD();\n      //dlog(\"peer_connection::send_item() enqueueing message of type ${type} for peer ${endpoint}\",\n      //     (\"type\", item_to_send.item_type)(\"endpoint\", get_remote_endpoint()));\n      std::unique_ptr<queued_message> message_to_enqueue(new virtual_queued_message(item_to_send));\n      send_queueable_message(std::move(message_to_enqueue));\n    }\n\n    void peer_connection::close_connection()\n    {\n      VERIFY_CORRECT_THREAD();\n      negotiation_status = connection_negotiation_status::closing;\n      if (connection_terminated_time != fc::time_point::min())\n        connection_terminated_time = fc::time_point::now();\n      _message_connection.close_connection();\n    }\n\n    void peer_connection::destroy_connection()\n    {\n      VERIFY_CORRECT_THREAD();\n      negotiation_status = connection_negotiation_status::closing;\n      destroy();\n    }\n\n    uint64_t peer_connection::get_total_bytes_sent() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_total_bytes_sent();\n    }\n\n    uint64_t peer_connection::get_total_bytes_received() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_total_bytes_received();\n    }\n\n    fc::time_point peer_connection::get_last_message_sent_time() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_last_message_sent_time();\n    }\n\n    fc::time_point peer_connection::get_last_message_received_time() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_last_message_received_time();\n    }\n\n    fc::optional<fc::ip::endpoint> peer_connection::get_remote_endpoint()\n    {\n      VERIFY_CORRECT_THREAD();\n      return _remote_endpoint;\n    }\n    fc::ip::endpoint peer_connection::get_local_endpoint()\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_socket().local_endpoint();\n    }\n\n    void peer_connection::set_remote_endpoint( fc::optional<fc::ip::endpoint> new_remote_endpoint )\n    {\n      VERIFY_CORRECT_THREAD();\n      _remote_endpoint = new_remote_endpoint;\n    }\n\n    bool peer_connection::busy() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return !items_requested_from_peer.empty() || !sync_items_requested_from_peer.empty() || item_ids_requested_from_peer;\n    }\n\n    bool peer_connection::idle() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return !busy();\n    }\n\n    bool peer_connection::is_currently_handling_message() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _currently_handling_message;\n    }\n\n    bool peer_connection::is_transaction_fetching_inhibited() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return transaction_fetching_inhibited_until > fc::time_point::now();\n    }\n\n    fc::sha512 peer_connection::get_shared_secret() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return _message_connection.get_shared_secret();\n    }\n\n    void peer_connection::clear_old_inventory()\n    {\n      VERIFY_CORRECT_THREAD();\n      fc::time_point_sec oldest_inventory_to_keep(fc::time_point::now() - fc::minutes(GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES));\n\n      // expire old items from inventory_advertised_to_peer\n      auto oldest_inventory_to_keep_iter = inventory_advertised_to_peer.get<timestamp_index>().lower_bound(oldest_inventory_to_keep);\n      auto begin_iter = inventory_advertised_to_peer.get<timestamp_index>().begin();\n      unsigned number_of_elements_advertised_to_peer_to_discard = std::distance(begin_iter, oldest_inventory_to_keep_iter);\n      inventory_advertised_to_peer.get<timestamp_index>().erase(begin_iter, oldest_inventory_to_keep_iter);\n\n      // also expire items from inventory_peer_advertised_to_us\n      oldest_inventory_to_keep_iter = inventory_peer_advertised_to_us.get<timestamp_index>().lower_bound(oldest_inventory_to_keep);\n      begin_iter = inventory_peer_advertised_to_us.get<timestamp_index>().begin();\n      unsigned number_of_elements_peer_advertised_to_discard = std::distance(begin_iter, oldest_inventory_to_keep_iter);\n      inventory_peer_advertised_to_us.get<timestamp_index>().erase(begin_iter, oldest_inventory_to_keep_iter);\n      dlog(\"Expiring old inventory for peer ${peer}: removing ${to_peer} items advertised to peer (${remain_to_peer} left), and ${to_us} advertised to us (${remain_to_us} left)\",\n           (\"peer\", get_remote_endpoint())\n           (\"to_peer\", number_of_elements_advertised_to_peer_to_discard)(\"remain_to_peer\", inventory_advertised_to_peer.size())\n           (\"to_us\", number_of_elements_peer_advertised_to_discard)(\"remain_to_us\", inventory_peer_advertised_to_us.size()));\n    }\n\n    // we have a higher limit for blocks than transactions so we will still fetch blocks even when transactions are throttled\n    bool peer_connection::is_inventory_advertised_to_us_list_full_for_transactions() const\n    {\n      VERIFY_CORRECT_THREAD();\n      return inventory_peer_advertised_to_us.size() > GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES * GRAPHENE_NET_MAX_TRX_PER_SECOND * 60;\n    }\n\n    bool peer_connection::is_inventory_advertised_to_us_list_full() const\n    {\n      VERIFY_CORRECT_THREAD();\n      // allow the total inventory size to be the maximum number of transactions we'll store in the inventory (above)\n      // plus the maximum number of blocks that would be generated in GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES (plus one,\n      // to give us some wiggle room)\n      return inventory_peer_advertised_to_us.size() >\n        GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES * GRAPHENE_NET_MAX_TRX_PER_SECOND * 60 +\n        (GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES + 1) * 60 / GRAPHENE_MIN_BLOCK_INTERVAL;\n    }\n\n    bool peer_connection::performing_firewall_check() const\n    {\n      return firewall_check_state && firewall_check_state->requesting_peer != node_id_t();\n    }\n\n    fc::optional<fc::ip::endpoint> peer_connection::get_endpoint_for_connecting() const\n    {\n      if (inbound_port)\n        return fc::ip::endpoint(inbound_address, inbound_port);\n      return fc::optional<fc::ip::endpoint>();\n    }\n\n} } // end namespace graphene::net\n"
  },
  {
    "path": "libraries/net/peer_database.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n#include <boost/multi_index/tag.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/io/raw_variant.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/io/json.hpp>\n\n#include <graphene/net/peer_database.hpp>\n#include <graphene/net/config.hpp>\n\nnamespace graphene { namespace net {\n  namespace detail\n  {\n    using namespace boost::multi_index;\n\n    class peer_database_impl\n    {\n    public:\n      struct last_seen_time_index {};\n      struct endpoint_index {};\n      typedef boost::multi_index_container<potential_peer_record, \n                                           indexed_by<ordered_non_unique<tag<last_seen_time_index>, \n                                                                         member<potential_peer_record, \n                                                                                fc::time_point_sec, \n                                                                                &potential_peer_record::last_seen_time>,\n                                                                         std::greater<fc::time_point_sec> >,\n                                                      hashed_unique<tag<endpoint_index>, \n                                                                    member<potential_peer_record, \n                                                                           fc::ip::endpoint, \n                                                                           &potential_peer_record::endpoint>, \n                                                                    std::hash<fc::ip::endpoint> > > > potential_peer_set;\n\n    private:\n      potential_peer_set     _potential_peer_set;\n      fc::path _peer_database_filename;\n\n    public:\n      void open(const fc::path& databaseFilename);\n      void close();\n      void clear();\n      void erase(const fc::ip::endpoint& endpointToErase);\n      void update_entry(const potential_peer_record& updatedRecord);\n      potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);\n      fc::optional<potential_peer_record> lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);\n\n      peer_database::iterator begin() const;\n      peer_database::iterator end() const;\n      size_t size() const;\n    };\n\n    class peer_database_iterator_impl\n    {\n    public:\n      typedef peer_database_impl::potential_peer_set::index<peer_database_impl::last_seen_time_index>::type::iterator last_seen_time_index_iterator;\n      last_seen_time_index_iterator _iterator;\n      explicit peer_database_iterator_impl(const last_seen_time_index_iterator& iterator) :\n        _iterator(iterator)\n      {}\n    };\n    peer_database_iterator::peer_database_iterator( const peer_database_iterator& c ) :\n      boost::iterator_facade<peer_database_iterator, const potential_peer_record, boost::forward_traversal_tag>(c){}\n\n    void peer_database_impl::open(const fc::path& peer_database_filename)\n    {\n      _peer_database_filename = peer_database_filename;\n      if (fc::exists(_peer_database_filename))\n      {\n        try\n        {\n          std::vector<potential_peer_record> peer_records = fc::json::from_file(_peer_database_filename).as<std::vector<potential_peer_record> >( GRAPHENE_NET_MAX_NESTED_OBJECTS );\n          std::copy(peer_records.begin(), peer_records.end(), std::inserter(_potential_peer_set, _potential_peer_set.end()));\n          if (_potential_peer_set.size() > MAXIMUM_PEERDB_SIZE)\n          {\n            // prune database to a reasonable size\n            auto iter = _potential_peer_set.begin();\n            std::advance(iter, MAXIMUM_PEERDB_SIZE);\n            _potential_peer_set.erase(iter, _potential_peer_set.end());\n          }\n        }\n        catch (const fc::exception& e)\n        {\n          elog(\"error opening peer database file ${peer_database_filename}, starting with a clean database\", \n               (\"peer_database_filename\", _peer_database_filename));\n        }\n      }\n    }\n\n    void peer_database_impl::close()\n    {\n      std::vector<potential_peer_record> peer_records;\n      peer_records.reserve(_potential_peer_set.size());\n      std::copy(_potential_peer_set.begin(), _potential_peer_set.end(), std::back_inserter(peer_records));\n\n      try\n      {\n        fc::path peer_database_filename_dir = _peer_database_filename.parent_path();\n        if (!fc::exists(peer_database_filename_dir))\n          fc::create_directories(peer_database_filename_dir);\n        fc::json::save_to_file( peer_records, _peer_database_filename, GRAPHENE_NET_MAX_NESTED_OBJECTS );\n      }\n      catch (const fc::exception& e)\n      {\n        elog(\"error saving peer database to file ${peer_database_filename}\", \n             (\"peer_database_filename\", _peer_database_filename));\n      }\n      _potential_peer_set.clear();\n    }\n\n    void peer_database_impl::clear()\n    {\n      _potential_peer_set.clear();\n    }\n\n    void peer_database_impl::erase(const fc::ip::endpoint& endpointToErase)\n    {\n      auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToErase);\n      if (iter != _potential_peer_set.get<endpoint_index>().end())\n        _potential_peer_set.get<endpoint_index>().erase(iter);\n    }\n\n    void peer_database_impl::update_entry(const potential_peer_record& updatedRecord)\n    {\n      auto iter = _potential_peer_set.get<endpoint_index>().find(updatedRecord.endpoint);\n      if (iter != _potential_peer_set.get<endpoint_index>().end())\n        _potential_peer_set.get<endpoint_index>().modify(iter, [&updatedRecord](potential_peer_record& record) { record = updatedRecord; });\n      else\n        _potential_peer_set.get<endpoint_index>().insert(updatedRecord);\n    }\n\n    potential_peer_record peer_database_impl::lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup)\n    {\n      auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToLookup);\n      if (iter != _potential_peer_set.get<endpoint_index>().end())\n        return *iter;\n      return potential_peer_record(endpointToLookup);\n    }\n\n    fc::optional<potential_peer_record> peer_database_impl::lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup)\n    {\n      auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToLookup);\n      if (iter != _potential_peer_set.get<endpoint_index>().end())\n        return *iter;\n      return fc::optional<potential_peer_record>();\n    }\n\n    peer_database::iterator peer_database_impl::begin() const\n    {\n      return peer_database::iterator(new peer_database_iterator_impl(_potential_peer_set.get<last_seen_time_index>().begin()));\n    }\n\n    peer_database::iterator peer_database_impl::end() const\n    {\n      return peer_database::iterator(new peer_database_iterator_impl(_potential_peer_set.get<last_seen_time_index>().end()));\n    }\n\n    size_t peer_database_impl::size() const\n    {\n      return _potential_peer_set.size();\n    }\n\n    peer_database_iterator::peer_database_iterator()\n    {\n    }\n\n    peer_database_iterator::~peer_database_iterator()\n    {\n    }\n\n    peer_database_iterator::peer_database_iterator(peer_database_iterator_impl* impl) :\n      my(impl)\n    {\n    }\n\n    void peer_database_iterator::increment()\n    {\n      ++my->_iterator;\n    }\n\n    bool peer_database_iterator::equal(const peer_database_iterator& other) const\n    {\n      return my->_iterator == other.my->_iterator;\n    }\n\n    const potential_peer_record& peer_database_iterator::dereference() const\n    {\n      return *my->_iterator;\n    }\n\n  } // end namespace detail\n\n  peer_database::peer_database() :\n    my(new detail::peer_database_impl)\n  {\n  }\n\n  peer_database::~peer_database()\n  {}\n\n  void peer_database::open(const fc::path& databaseFilename)\n  {\n    my->open(databaseFilename);\n  }\n\n  void peer_database::close()\n  {\n    my->close();\n  }\n\n  void peer_database::clear()\n  {\n    my->clear();\n  }\n\n  void peer_database::erase(const fc::ip::endpoint& endpointToErase)\n  {\n    my->erase(endpointToErase);\n  }\n\n  void peer_database::update_entry(const potential_peer_record& updatedRecord)\n  {\n    my->update_entry(updatedRecord);\n  }\n\n  potential_peer_record peer_database::lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup)\n  {\n    return my->lookup_or_create_entry_for_endpoint(endpointToLookup);\n  }\n\n  fc::optional<potential_peer_record> peer_database::lookup_entry_for_endpoint(const fc::ip::endpoint& endpoint_to_lookup)\n  {\n    return my->lookup_entry_for_endpoint(endpoint_to_lookup);\n  }\n\n  peer_database::iterator peer_database::begin() const\n  {\n    return my->begin();\n  }\n\n  peer_database::iterator peer_database::end() const\n  {\n    return my->end();\n  }\n\n  size_t peer_database::size() const\n  {\n    return my->size();\n  }\n\n} } // end namespace graphene::net\n\nFC_REFLECT_ENUM( graphene::net::potential_peer_last_connection_disposition,\n                 (never_attempted_to_connect)\n                 (last_connection_failed)(last_connection_rejected)\n                 (last_connection_handshaking_failed)(last_connection_succeeded) )\nFC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::potential_peer_record, BOOST_PP_SEQ_NIL,\n                                (endpoint)(last_seen_time)(last_connection_disposition)\n                                (last_connection_attempt_time)(number_of_successful_connection_attempts)\n                                (number_of_failed_connection_attempts)(last_error) )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::net::potential_peer_record)\n"
  },
  {
    "path": "libraries/net/stcp_socket.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <assert.h>\n\n#include <algorithm>\n\n#include <fc/crypto/hex.hpp>\n#include <fc/crypto/aes.hpp>\n#include <fc/crypto/city.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/network/ip.hpp>\n#include <fc/exception/exception.hpp>\n\n#include <graphene/net/stcp_socket.hpp>\n\nnamespace graphene { namespace net {\n\nstcp_socket::stcp_socket()\n//:_buf_len(0)\n#ifndef NDEBUG\n   : _read_buffer_in_use(false),\n     _write_buffer_in_use(false)\n#endif\n{\n}\nstcp_socket::~stcp_socket()\n{\n}\n\nvoid stcp_socket::do_key_exchange()\n{\n  _priv_key = fc::ecc::private_key::generate();\n  fc::ecc::public_key pub = _priv_key.get_public_key();\n  fc::ecc::public_key_data s = pub.serialize();\n  std::shared_ptr<char> serialized_key_buffer(new char[sizeof(fc::ecc::public_key_data)], [](char* p){ delete[] p; });\n  memcpy(serialized_key_buffer.get(), (char*)&s, sizeof(fc::ecc::public_key_data));\n  _sock.write( serialized_key_buffer, sizeof(fc::ecc::public_key_data) );\n  _sock.read( serialized_key_buffer, sizeof(fc::ecc::public_key_data) );\n  fc::ecc::public_key_data rpub;\n  memcpy((char*)&rpub, serialized_key_buffer.get(), sizeof(fc::ecc::public_key_data));\n\n  _shared_secret = _priv_key.get_shared_secret( rpub );\n//    ilog(\"shared secret ${s}\", (\"s\", shared_secret) );\n  _send_aes.init( fc::sha256::hash( (char*)&_shared_secret, sizeof(_shared_secret) ), \n                  fc::city_hash_crc_128((char*)&_shared_secret,sizeof(_shared_secret) ) );\n  _recv_aes.init( fc::sha256::hash( (char*)&_shared_secret, sizeof(_shared_secret) ), \n                  fc::city_hash_crc_128((char*)&_shared_secret,sizeof(_shared_secret) ) );\n}\n\n\nvoid stcp_socket::connect_to( const fc::ip::endpoint& remote_endpoint )\n{\n  _sock.connect_to( remote_endpoint );\n  do_key_exchange();\n}\n\nvoid stcp_socket::bind( const fc::ip::endpoint& local_endpoint )\n{\n  _sock.bind(local_endpoint);\n}\n\n/**\n *   This method must read at least 16 bytes at a time from\n *   the underlying TCP socket so that it can decrypt them. It\n *   will buffer any left-over.\n */\nsize_t stcp_socket::readsome( char* buffer, size_t len )\n{ try {\n    assert( len > 0 && (len % 16) == 0 );\n\n#ifndef NDEBUG\n    // This code was written with the assumption that you'd only be making one call to readsome \n    // at a time so it reuses _read_buffer.  If you really need to make concurrent calls to \n    // readsome(), you'll need to prevent reusing _read_buffer here\n    struct check_buffer_in_use {\n      bool& _buffer_in_use;\n      check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; }\n      ~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; }\n    } buffer_in_use_checker(_read_buffer_in_use);\n#endif\n\n    const size_t read_buffer_length = 4096;\n    if (!_read_buffer)\n      _read_buffer.reset(new char[read_buffer_length], [](char* p){ delete[] p; });\n\n    len = std::min<size_t>(read_buffer_length, len);\n\n    size_t s = _sock.readsome( _read_buffer, len, 0 );\n    if( s % 16 ) \n    {\n      _sock.read(_read_buffer, 16 - (s%16), s);\n      s += 16-(s%16);\n    }\n    _recv_aes.decode( _read_buffer.get(), s, buffer );\n    return s;\n} FC_RETHROW_EXCEPTIONS( warn, \"\", (\"len\",len) ) }\n\nsize_t stcp_socket::readsome( const std::shared_ptr<char>& buf, size_t len, size_t offset ) \n{\n  return readsome(buf.get() + offset, len);\n}\n\nbool stcp_socket::eof()const\n{\n  return _sock.eof();\n}\n\nsize_t stcp_socket::writesome( const char* buffer, size_t len )\n{ try {\n    assert( len > 0 && (len % 16) == 0 );\n\n#ifndef NDEBUG\n    // This code was written with the assumption that you'd only be making one call to writesome\n    // at a time so it reuses _write_buffer.  If you really need to make concurrent calls to \n    // writesome(), you'll need to prevent reusing _write_buffer here\n    struct check_buffer_in_use {\n      bool& _buffer_in_use;\n      check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; }\n      ~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; }\n    } buffer_in_use_checker(_write_buffer_in_use);\n#endif\n\n    const std::size_t write_buffer_length = 4096;\n    if (!_write_buffer)\n      _write_buffer.reset(new char[write_buffer_length], [](char* p){ delete[] p; });\n    len = std::min<size_t>(write_buffer_length, len);\n    memset(_write_buffer.get(), 0, len); // just in case aes.encode screws up\n    /**\n     * every sizeof(crypt_buf) bytes the aes channel\n     * has an error and doesn't decrypt properly...  disable\n     * for now because we are going to upgrade to something\n     * better.\n     */\n    uint32_t ciphertext_len = _send_aes.encode( buffer, len, _write_buffer.get() );\n    assert(ciphertext_len == len);\n    _sock.write( _write_buffer, ciphertext_len );\n    return ciphertext_len;\n} FC_RETHROW_EXCEPTIONS( warn, \"\", (\"len\",len) ) }\n\nsize_t stcp_socket::writesome( const std::shared_ptr<const char>& buf, size_t len, size_t offset )\n{\n  return writesome(buf.get() + offset, len);\n}\n\nvoid stcp_socket::flush()\n{\n  _sock.flush();\n}\n\n\nvoid stcp_socket::close()\n{\n  try \n  {\n    _sock.close();\n  }FC_RETHROW_EXCEPTIONS( warn, \"error closing stcp socket\" );\n}\n\nvoid stcp_socket::accept()\n{\n  do_key_exchange();\n}\n\n\n}} // namespace graphene::net\n\n"
  },
  {
    "path": "libraries/plugins/CMakeLists.txt",
    "content": "add_subdirectory( witness )\nadd_subdirectory( account_history )\nadd_subdirectory( elasticsearch )\nadd_subdirectory( market_history )\nadd_subdirectory( grouped_orders )\nadd_subdirectory( delayed_node )\nadd_subdirectory( debug_witness )\nadd_subdirectory( snapshot )\nadd_subdirectory( es_objects )\nadd_subdirectory( api_helper_indexes )\nadd_subdirectory( custom_operations )\n"
  },
  {
    "path": "libraries/plugins/README.md",
    "content": "# BitShares Plugins\n\nThe bitshares plugins are a collection of tools that brings new functionality without the need of modifications in the consensus and more sensitive areas of the bitshares-core.\n\nThe main source of I/O of the bitshares blockchain is the API. Plugins are a more powerful alternative to build more complex developments for when the current API is not enough.\n\nPlugins are optional to run by node operator according to their needs. However, all plugins here will be compiled. There are plans for optional build of plugins at: [Issue 533](https://github.com/bitshares/bitshares-core/issues/533)\n\n# Available Plugins\n\nFolder                             | Name                     | Description                                                                 | Category       | Status        | SpaceID     \n-----------------------------------|--------------------------|-----------------------------------------------------------------------------|----------------|---------------|--------------|\n[account_history](account_history) | Account History          | Save account history data                                                   | History        | Stable        | 4\n[api_helper_indexes](api_helper_indexes) | API Helper Indexes | Provides some helper indexes used by various API calls                                                 | Database API   | Stable        | \n[custom_operations](custom_operations) | Custom Operations    | Store and retrieve account catalogs of key=>value data using custom operations | Additional data   | Experimental        | 7\n[debug_witness](debug_witness)     | Debug Witness            | Run \"what-if\" tests                                                         | Debug          | Stable        |\n[delayed_node](delayed_node)       | Delayed Node             | Avoid forks by running a several times confirmed and delayed blockchain     | Business       | Stable        |\n[elasticsearch](elasticsearch)     | ElasticSearch Operations | Save account history data into elasticsearch database                       | History        | Experimental  | 6\n[es_objects](es_objects)           | ElasticSearch Objects    | Save selected objects into elasticsearch database                           | History        | Experimental  |\n[grouped_orders](grouped_orders)   | Grouped Orders           | Expose api to create a grouped order book of bitshares markets              | Market data    | Experimental  |\n[market_history](market_history)   | Market History           | Save market history data                                                    | Market data    | Stable        | 5\n[snapshot](snapshot)               | Snapshot                 | Get a json of all objects in blockchain at a specificed time or block       | Debug          | Stable        | \n[witness](witness)                 | Witness                  | Generate and sign blocks                                                    | Block producer | Stable        | \n"
  },
  {
    "path": "libraries/plugins/account_history/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/account_history/*.hpp\")\n\nadd_library( graphene_account_history \n             account_history_plugin.cpp\n           )\n\ntarget_link_libraries( graphene_account_history graphene_chain graphene_app )\ntarget_include_directories( graphene_account_history\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties( account_history_plugin.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_account_history\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/account_history\" )\n\n"
  },
  {
    "path": "libraries/plugins/account_history/account_history_plugin.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/account_history/account_history_plugin.hpp>\n\n#include <graphene/chain/impacted.hpp>\n\n#include <graphene/chain/account_evaluator.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/config.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/transaction_evaluation_state.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/thread/thread.hpp>\n\nnamespace graphene { namespace account_history {\n\nnamespace detail\n{\n\n\nclass account_history_plugin_impl\n{\n   public:\n      account_history_plugin_impl(account_history_plugin& _plugin)\n         : _self( _plugin )\n      { }\n      virtual ~account_history_plugin_impl();\n\n\n      /** this method is called as a callback after a block is applied\n       * and will process/index all operations that were applied in the block.\n       */\n      void update_account_histories( const signed_block& b );\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      account_history_plugin& _self;\n      flat_set<account_id_type> _tracked_accounts;\n      bool _partial_operations = false;\n      primary_index< operation_history_index >* _oho_index;\n      uint64_t _max_ops_per_account = -1;\n   private:\n      /** add one history record, then check and remove the earliest history record */\n      void add_account_history( const account_id_type account_id, const operation_history_id_type op_id );\n\n};\n\naccount_history_plugin_impl::~account_history_plugin_impl()\n{\n   return;\n}\n\nvoid account_history_plugin_impl::update_account_histories( const signed_block& b )\n{\n   graphene::chain::database& db = database();\n   const vector<optional< operation_history_object > >& hist = db.get_applied_operations();\n   bool is_first = true;\n   auto skip_oho_id = [&is_first,&db,this]() {\n      if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo\n      {\n         db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );\n         is_first = false;\n      }\n      else\n         _oho_index->use_next_id();\n   };\n\n   for( const optional< operation_history_object >& o_op : hist )\n   {\n      optional<operation_history_object> oho;\n\n      auto create_oho = [&]() {\n         is_first = false;\n         return optional<operation_history_object>( db.create<operation_history_object>( [&]( operation_history_object& h )\n         {\n            if( o_op.valid() )\n            {\n               h.op           = o_op->op;\n               h.result       = o_op->result;\n               h.block_num    = o_op->block_num;\n               h.trx_in_block = o_op->trx_in_block;\n               h.op_in_trx    = o_op->op_in_trx;\n               h.virtual_op   = o_op->virtual_op;\n            }\n         } ) );\n      };\n\n      if( !o_op.valid() || ( _max_ops_per_account == 0 && _partial_operations ) )\n      {\n         // Note: the 2nd and 3rd checks above are for better performance, when the db is not clean,\n         //       they will break consistency of account_stats.total_ops and removed_ops and most_recent_op\n         skip_oho_id();\n         continue;\n      }\n      else if( !_partial_operations )\n         // add to the operation history index\n         oho = create_oho();\n\n      const operation_history_object& op = *o_op;\n\n      // get the set of accounts this operation applies to\n      flat_set<account_id_type> impacted;\n      vector<authority> other;\n      // fee payer is added here\n      operation_get_required_authorities( op.op, impacted, impacted, other,\n                                          MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );\n\n      if( op.op.is_type< account_create_operation >() )\n         impacted.insert( op.result.get<object_id_type>() );\n      else\n         operation_get_impacted_accounts( op.op, impacted,\n                                          MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );\n\n      for( auto& a : other )\n         for( auto& item : a.account_auths )\n            impacted.insert( item.first );\n\n      // be here, either _max_ops_per_account > 0, or _partial_operations == false, or both\n      // if _partial_operations == false, oho should have been created above\n      // so the only case should be checked here is:\n      //    whether need to create oho if _max_ops_per_account > 0 and _partial_operations == true\n\n      // for each operation this account applies to that is in the config link it into the history\n      if( _tracked_accounts.size() == 0 ) // tracking all accounts\n      {\n         // if tracking all accounts, when impacted is not empty (although it will always be),\n         //    still need to create oho if _max_ops_per_account > 0 and _partial_operations == true\n         //    so always need to create oho if not done\n         if (!impacted.empty() && !oho.valid()) { oho = create_oho(); }\n\n         if( _max_ops_per_account > 0 )\n         {\n            // Note: the check above is for better performance, when the db is not clean,\n            //       it breaks consistency of account_stats.total_ops and removed_ops and most_recent_op,\n            //       but it ensures it's safe to remove old entries in add_account_history(...)\n            for( auto& account_id : impacted )\n            {\n               // we don't do index_account_keys here anymore, because\n               // that indexing now happens in observers' post_evaluate()\n\n               // add history\n               add_account_history( account_id, oho->id );\n            }\n         }\n      }\n      else // tracking a subset of accounts\n      {\n         // whether need to create oho if _max_ops_per_account > 0 and _partial_operations == true ?\n         // the answer: only need to create oho if a tracked account is impacted and need to save history\n\n         if( _max_ops_per_account > 0 )\n         {\n            // Note: the check above is for better performance, when the db is not clean,\n            //       it breaks consistency of account_stats.total_ops and removed_ops and most_recent_op,\n            //       but it ensures it's safe to remove old entries in add_account_history(...)\n            for( auto account_id : _tracked_accounts )\n            {\n               if( impacted.find( account_id ) != impacted.end() )\n               {\n                  if (!oho.valid()) { oho = create_oho(); }\n                  // add history\n                  add_account_history( account_id, oho->id );\n               }\n            }\n         }\n      }\n      if (_partial_operations && ! oho.valid())\n         skip_oho_id();\n   }\n}\n\nvoid account_history_plugin_impl::add_account_history( const account_id_type account_id, const operation_history_id_type op_id )\n{\n   graphene::chain::database& db = database();\n   const auto& stats_obj = account_id(db).statistics(db);\n   // add new entry\n   const auto& ath = db.create<account_transaction_history_object>( [&]( account_transaction_history_object& obj ){\n       obj.operation_id = op_id;\n       obj.account = account_id;\n       obj.sequence = stats_obj.total_ops + 1;\n       obj.next = stats_obj.most_recent_op;\n   });\n   db.modify( stats_obj, [&]( account_statistics_object& obj ){\n       obj.most_recent_op = ath.id;\n       obj.total_ops = ath.sequence;\n   });\n   // remove the earliest account history entry if too many\n   // _max_ops_per_account is guaranteed to be non-zero outside\n   if( stats_obj.total_ops - stats_obj.removed_ops > _max_ops_per_account )\n   {\n      // look for the earliest entry\n      const auto& his_idx = db.get_index_type<account_transaction_history_index>();\n      const auto& by_seq_idx = his_idx.indices().get<by_seq>();\n      auto itr = by_seq_idx.lower_bound( boost::make_tuple( account_id, 0 ) );\n      // make sure don't remove the one just added\n      if( itr != by_seq_idx.end() && itr->account == account_id && itr->id != ath.id )\n      {\n         // if found, remove the entry, and adjust account stats object\n         const auto remove_op_id = itr->operation_id;\n         const auto itr_remove = itr;\n         ++itr;\n         db.remove( *itr_remove );\n         db.modify( stats_obj, [&]( account_statistics_object& obj ){\n             obj.removed_ops = obj.removed_ops + 1;\n         });\n         // modify previous node's next pointer\n         // this should be always true, but just have a check here\n         if( itr != by_seq_idx.end() && itr->account == account_id )\n         {\n            db.modify( *itr, [&]( account_transaction_history_object& obj ){\n               obj.next = account_transaction_history_id_type();\n            });\n         }\n         // else need to modify the head pointer, but it shouldn't be true\n\n         // remove the operation history entry (1.11.x) if configured and no reference left\n         if( _partial_operations )\n         {\n            // check for references\n            const auto& by_opid_idx = his_idx.indices().get<by_opid>();\n            if( by_opid_idx.find( remove_op_id ) == by_opid_idx.end() )\n            {\n               // if no reference, remove\n               db.remove( remove_op_id(db) );\n            }\n         }\n      }\n   }\n}\n\n} // end namespace detail\n\n\n\n\n\n\naccount_history_plugin::account_history_plugin() :\n   my( new detail::account_history_plugin_impl(*this) )\n{\n}\n\naccount_history_plugin::~account_history_plugin()\n{\n}\n\nstd::string account_history_plugin::plugin_name()const\n{\n   return \"account_history\";\n}\n\nvoid account_history_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"track-account\", boost::program_options::value<std::vector<std::string>>()->composing()->multitoken(), \"Account ID to track history for (may specify multiple times)\")\n         (\"partial-operations\", boost::program_options::value<bool>(), \"Keep only those operations in memory that are related to account history tracking\")\n         (\"max-ops-per-account\", boost::program_options::value<uint64_t>(), \"Maximum number of operations per account will be kept in memory\")\n         ;\n   cfg.add(cli);\n}\n\nvoid account_history_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{\n   database().applied_block.connect( [&]( const signed_block& b){ my->update_account_histories(b); } );\n   my->_oho_index = database().add_index< primary_index< operation_history_index > >();\n   database().add_index< primary_index< account_transaction_history_index > >();\n\n   LOAD_VALUE_SET(options, \"track-account\", my->_tracked_accounts, graphene::chain::account_id_type);\n   if (options.count(\"partial-operations\")) {\n       my->_partial_operations = options[\"partial-operations\"].as<bool>();\n   }\n   if (options.count(\"max-ops-per-account\")) {\n       my->_max_ops_per_account = options[\"max-ops-per-account\"].as<uint64_t>();\n   }\n}\n\nvoid account_history_plugin::plugin_startup()\n{\n}\n\nflat_set<account_id_type> account_history_plugin::tracked_accounts() const\n{\n   return my->_tracked_accounts;\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/operation_history_object.hpp>\n\n#include <fc/thread/future.hpp>\n\nnamespace graphene { namespace account_history {\n   using namespace chain;\n   //using namespace graphene::db;\n   //using boost::multi_index_container;\n   //using namespace boost::multi_index;\n\n//\n// Plugins should #define their SPACE_ID's so plugins with\n// conflicting SPACE_ID assignments can be compiled into the\n// same binary (by simply re-assigning some of the conflicting #defined\n// SPACE_ID's in a build script).\n//\n// Assignment of SPACE_ID's cannot be done at run-time because\n// various template automagic depends on them being known at compile\n// time.\n//\n#ifndef ACCOUNT_HISTORY_SPACE_ID\n#define ACCOUNT_HISTORY_SPACE_ID 4\n#endif\n\nenum account_history_object_type\n{\n   key_account_object_type = 0\n};\n\n\nnamespace detail\n{\n    class account_history_plugin_impl;\n}\n\nclass account_history_plugin : public graphene::app::plugin\n{\n   public:\n      account_history_plugin();\n      virtual ~account_history_plugin();\n\n      std::string plugin_name()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      flat_set<account_id_type> tracked_accounts()const;\n\n      friend class detail::account_history_plugin_impl;\n      std::unique_ptr<detail::account_history_plugin_impl> my;\n};\n\n} } //graphene::account_history\n"
  },
  {
    "path": "libraries/plugins/api_helper_indexes/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/api_helper_indexes/*.hpp\")\n\nadd_library( graphene_api_helper_indexes\n        api_helper_indexes.cpp\n           )\n\ntarget_link_libraries( graphene_api_helper_indexes graphene_chain graphene_app )\ntarget_include_directories( graphene_api_helper_indexes\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties(api_helper_indexes.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_api_helper_indexes\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/api_helper_indexes\" )\n"
  },
  {
    "path": "libraries/plugins/api_helper_indexes/api_helper_indexes.cpp",
    "content": "/*\n * Copyright (c) 2018 api_helper_indexes and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/api_helper_indexes/api_helper_indexes.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\nnamespace graphene { namespace api_helper_indexes {\n\nvoid amount_in_collateral_index::object_inserted( const object& objct )\n{ try {\n   const call_order_object& o = static_cast<const call_order_object&>( objct );\n\n   {\n      auto itr = in_collateral.find( o.collateral_type() );\n      if( itr == in_collateral.end() )\n         in_collateral[o.collateral_type()] = o.collateral;\n      else\n         itr->second += o.collateral;\n   }\n\n   {\n      auto itr = backing_collateral.find( o.debt_type() );\n      if( itr == backing_collateral.end() )\n         backing_collateral[o.debt_type()] = o.collateral;\n      else\n         itr->second += o.collateral;\n   }\n\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid amount_in_collateral_index::object_removed( const object& objct )\n{ try {\n   const call_order_object& o = static_cast<const call_order_object&>( objct );\n\n   {\n      auto itr = in_collateral.find( o.collateral_type() );\n      if( itr != in_collateral.end() ) // should always be true\n         itr->second -= o.collateral;\n   }\n\n   {\n      auto itr = backing_collateral.find( o.debt_type() );\n      if( itr != backing_collateral.end() ) // should always be true\n         itr->second -= o.collateral;\n   }\n\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid amount_in_collateral_index::about_to_modify( const object& objct )\n{ try {\n   object_removed( objct );\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid amount_in_collateral_index::object_modified( const object& objct )\n{ try {\n   object_inserted( objct );\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nshare_type amount_in_collateral_index::get_amount_in_collateral( const asset_id_type& asset )const\n{ try {\n   auto itr = in_collateral.find( asset );\n   if( itr == in_collateral.end() ) return 0;\n   return itr->second;\n} FC_CAPTURE_AND_RETHROW( (asset) ); }\n\nshare_type amount_in_collateral_index::get_backing_collateral( const asset_id_type& asset )const\n{ try {\n   auto itr = backing_collateral.find( asset );\n   if( itr == backing_collateral.end() ) return 0;\n   return itr->second;\n} FC_CAPTURE_AND_RETHROW( (asset) ); }\n\nnamespace detail\n{\n\nclass api_helper_indexes_impl\n{\n   public:\n      api_helper_indexes_impl(api_helper_indexes& _plugin)\n         : _self( _plugin )\n      {  }\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      api_helper_indexes& _self;\n\n   private:\n\n};\n\n} // end namespace detail\n\napi_helper_indexes::api_helper_indexes() :\n   my( new detail::api_helper_indexes_impl(*this) )\n{\n}\n\napi_helper_indexes::~api_helper_indexes()\n{\n}\n\nstd::string api_helper_indexes::plugin_name()const\n{\n   return \"api_helper_indexes\";\n}\nstd::string api_helper_indexes::plugin_description()const\n{\n   return \"Provides some helper indexes used by various API calls\";\n}\n\nvoid api_helper_indexes::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n}\n\nvoid api_helper_indexes::plugin_initialize(const boost::program_options::variables_map& options)\n{\n}\n\nvoid api_helper_indexes::plugin_startup()\n{\n   ilog(\"api_helper_indexes: plugin_startup() begin\");\n   amount_in_collateral = database().add_secondary_index< primary_index<call_order_index>, amount_in_collateral_index >();\n   for( const auto& call : database().get_index_type<call_order_index>().indices() )\n      amount_in_collateral->object_inserted( call );\n\n   auto& account_members = *database().add_secondary_index< primary_index<account_index>, account_member_index >();\n   for( const auto& account : database().get_index_type< account_index >().indices() )\n      account_members.object_inserted( account );\n\n   auto& approvals = *database().add_secondary_index< primary_index<proposal_index>, required_approval_index >();\n   for( const auto& proposal : database().get_index_type< proposal_index >().indices() )\n      approvals.object_inserted( proposal );\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/api_helper_indexes/include/graphene/api_helper_indexes/api_helper_indexes.hpp",
    "content": "/*\n * Copyright (c) 2018 api_helper_indexes and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace api_helper_indexes {\nusing namespace chain;\n\n/**\n *  @brief This secondary index tracks how much of each asset is locked up as collateral for MPAs, and how much\n *         collateral is backing an MPA in total.\n */\nclass amount_in_collateral_index : public secondary_index\n{\n   public:\n      virtual void object_inserted( const object& obj ) override;\n      virtual void object_removed( const object& obj ) override;\n      virtual void about_to_modify( const object& before ) override;\n      virtual void object_modified( const object& after ) override;\n\n      share_type get_amount_in_collateral( const asset_id_type& asset )const;\n      share_type get_backing_collateral( const asset_id_type& asset )const;\n\n   private:\n      flat_map<asset_id_type, share_type> in_collateral;\n      flat_map<asset_id_type, share_type> backing_collateral;\n};\n\nnamespace detail\n{\n    class api_helper_indexes_impl;\n}\n\nclass api_helper_indexes : public graphene::app::plugin\n{\n   public:\n      api_helper_indexes();\n      virtual ~api_helper_indexes();\n\n      std::string plugin_name()const override;\n      std::string plugin_description()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      friend class detail::api_helper_indexes_impl;\n      std::unique_ptr<detail::api_helper_indexes_impl> my;\n\n   private:\n      amount_in_collateral_index* amount_in_collateral = nullptr;\n};\n\n} } //graphene::template\n"
  },
  {
    "path": "libraries/plugins/custom_operations/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/custom_operations/*.hpp\")\n\nadd_library( graphene_custom_operations\n        custom_operations_plugin.cpp\n        custom_operations.cpp\n        custom_evaluators.cpp\n           )\n\ntarget_link_libraries( graphene_custom_operations graphene_chain graphene_app )\ntarget_include_directories( graphene_custom_operations\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties(custom_operations_plugin.cpp custom_operations.cpp custom_evaluators.cpp\n          PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_custom_operations\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/custom_operations\" )\n"
  },
  {
    "path": "libraries/plugins/custom_operations/custom_evaluators.cpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n#include <graphene/custom_operations/custom_objects.hpp>\n#include <graphene/custom_operations/custom_evaluators.hpp>\n\nnamespace graphene { namespace custom_operations {\n\ncustom_generic_evaluator::custom_generic_evaluator(database& db, const account_id_type account)\n{\n   _db = &db;\n   _account = account;\n}\n\nvector<object_id_type> custom_generic_evaluator::do_apply(const account_storage_map& op)\n{\n   const auto &index = _db->get_index_type<account_storage_index>().indices().get<by_account_catalog_key>();\n   vector<object_id_type> results;\n   results.reserve( op.key_values.size() );\n\n   if (op.remove)\n   {\n      for(auto const& row: op.key_values) {\n         auto itr = index.find(make_tuple(_account, op.catalog, row.first));\n         if(itr != index.end()) {\n            results.push_back(itr->id);\n            _db->remove(*itr);\n         }\n      }\n   }\n   else {\n      for(auto const& row: op.key_values) {\n         if(row.first.length() > CUSTOM_OPERATIONS_MAX_KEY_SIZE)\n         {\n            wlog(\"Key can't be bigger than ${max} characters\", (\"max\", CUSTOM_OPERATIONS_MAX_KEY_SIZE));\n            continue;\n         }\n         auto itr = index.find(make_tuple(_account, op.catalog, row.first));\n         if(itr == index.end())\n         {\n            try {\n               const auto& created = _db->create<account_storage_object>(\n                                        [&op, this, &row]( account_storage_object& aso ) {\n                  aso.account = _account;\n                  aso.catalog = op.catalog;\n                  aso.key = row.first;\n                  if(row.second.valid())\n                     aso.value = fc::json::from_string(*row.second);\n               });\n               results.push_back(created.id);\n            }\n            catch(const fc::parse_error_exception& e) { wlog(e.to_detail_string()); }\n         }\n         else\n         {\n            try {\n               _db->modify(*itr, [&op, this, &row](account_storage_object &aso) {\n                  if(row.second.valid())\n                     aso.value = fc::json::from_string(*row.second);\n                  else\n                     aso.value.reset();\n               });\n               results.push_back(itr->id);\n            }\n            catch(const fc::parse_error_exception& e) { wlog((e.to_detail_string())); }\n         }\n      }\n   }\n   return results;\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/custom_operations/custom_operations.cpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/custom_operations/custom_operations.hpp>\n\nnamespace graphene { namespace custom_operations {\n\nvoid account_storage_map::validate()const\n{\n   FC_ASSERT(catalog.length() <= CUSTOM_OPERATIONS_MAX_KEY_SIZE && catalog.length() > 0);\n}\n\n} } //graphene::custom_operations\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::custom_operations::account_storage_map )\n"
  },
  {
    "path": "libraries/plugins/custom_operations/custom_operations_plugin.cpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n\n#include <fc/crypto/hex.hpp>\n#include <iostream>\n#include <graphene/app/database_api.hpp>\n\nnamespace graphene { namespace custom_operations {\n\nnamespace detail\n{\nclass custom_operations_plugin_impl\n{\n   public:\n   custom_operations_plugin_impl(custom_operations_plugin& _plugin)\n         : _self( _plugin )\n      {  }\n      virtual ~custom_operations_plugin_impl();\n\n      void onBlock();\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      custom_operations_plugin& _self;\n\n      uint32_t _start_block = 45000000;\n\n   private:\n\n};\n\nstruct custom_op_visitor\n{\n   typedef void result_type;\n   account_id_type _fee_payer;\n   database* _db;\n\n   custom_op_visitor(database& db, account_id_type fee_payer) { _db = &db; _fee_payer = fee_payer; };\n\n   template<typename T>\n   void operator()(T &v) const {\n      v.validate();\n      custom_generic_evaluator evaluator(*_db, _fee_payer);\n      evaluator.do_apply(v);\n   }\n};\n\nvoid custom_operations_plugin_impl::onBlock()\n{\n   graphene::chain::database& db = database();\n   const vector<optional< operation_history_object > >& hist = db.get_applied_operations();\n   for( const optional< operation_history_object >& o_operation : hist )\n   {\n      if(!o_operation.valid() || !o_operation->op.is_type<custom_operation>())\n         continue;\n\n      const custom_operation& custom_op = o_operation->op.get<custom_operation>();\n\n      if(custom_op.data.size() == 0)\n         continue;\n\n      try {\n         auto unpacked = fc::raw::unpack<custom_plugin_operation>(custom_op.data);\n         custom_op_visitor vtor(db, custom_op.fee_payer());\n         unpacked.visit(vtor);\n      }\n      catch (fc::exception& e) { // only api node will know if the unpack, validate or apply fails\n         wlog(\"Custom operations plugin serializing error: ${ex} in operation: ${op}\",\n               (\"ex\", e.to_detail_string())(\"op\", fc::json::to_string(custom_op)));\n         continue;\n      }\n   }\n}\n\ncustom_operations_plugin_impl::~custom_operations_plugin_impl()\n{\n   return;\n}\n\n} // end namespace detail\n\ncustom_operations_plugin::custom_operations_plugin() :\n   my( new detail::custom_operations_plugin_impl(*this) )\n{\n}\n\ncustom_operations_plugin::~custom_operations_plugin()\n{\n}\n\nstd::string custom_operations_plugin::plugin_name()const\n{\n   return \"custom_operations\";\n}\nstd::string custom_operations_plugin::plugin_description()const\n{\n   return \"Stores arbitrary data for accounts by creating specially crafted custom operations.\";\n}\n\nvoid custom_operations_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"custom-operations-start-block\", boost::program_options::value<uint32_t>()->default_value(45000000),\n          \"Start processing custom operations transactions with the plugin only after this block\")\n         ;\n   cfg.add(cli);\n\n}\n\nvoid custom_operations_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{\n   database().add_index< primary_index< account_storage_index  > >();\n\n   if (options.count(\"custom-operations-start-block\")) {\n      my->_start_block = options[\"custom-operations-start-block\"].as<uint32_t>();\n   }\n\n   database().applied_block.connect( [this]( const signed_block& b) {\n      if( b.block_num() >= my->_start_block )\n         my->onBlock();\n   } );\n}\n\nvoid custom_operations_plugin::plugin_startup()\n{\n   ilog(\"custom_operations: plugin_startup() begin\");\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/custom_operations/include/graphene/custom_operations/custom_evaluators.hpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/custom_operations/custom_objects.hpp>\n#include <graphene/custom_operations/custom_operations.hpp>\n\nnamespace graphene { namespace custom_operations {\n\nclass custom_generic_evaluator\n{\n   public:\n      database* _db;\n      account_id_type _account;\n      custom_generic_evaluator(database& db, const account_id_type account);\n\n      vector<object_id_type> do_apply(const account_storage_map& o);\n};\n\n} }\n\nFC_REFLECT_TYPENAME( graphene::custom_operations::custom_generic_evaluator )\n"
  },
  {
    "path": "libraries/plugins/custom_operations/include/graphene/custom_operations/custom_objects.hpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <boost/multi_index/composite_key.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace custom_operations {\n\nusing namespace chain;\n\n#ifndef CUSTOM_OPERATIONS_SPACE_ID\n#define CUSTOM_OPERATIONS_SPACE_ID 7\n#endif\n\n#define CUSTOM_OPERATIONS_MAX_KEY_SIZE (200)\n\nenum types {\n   account_map = 0\n};\n\nstruct account_storage_object : public abstract_object<account_storage_object>\n{\n   static const uint8_t space_id = CUSTOM_OPERATIONS_SPACE_ID;\n   static const uint8_t type_id  = account_map;\n\n   account_id_type account;\n   string catalog;\n   string key;\n   optional<variant> value;\n};\n\nstruct by_account_catalog_key;\n\ntypedef multi_index_container<\n      account_storage_object,\n      indexed_by<\n            ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n            ordered_unique< tag<by_account_catalog_key>,\n                  composite_key< account_storage_object,\n                        member< account_storage_object, account_id_type, &account_storage_object::account >,\n                        member< account_storage_object, string, &account_storage_object::catalog >,\n                        member< account_storage_object, string, &account_storage_object::key >\n                  >\n            >\n      >\n> account_storage_multi_index_type;\n\ntypedef generic_index<account_storage_object, account_storage_multi_index_type> account_storage_index;\n\nusing account_storage_id_type = object_id<CUSTOM_OPERATIONS_SPACE_ID, account_map>;\n\n} } //graphene::custom_operations\n\nFC_REFLECT_DERIVED( graphene::custom_operations::account_storage_object, (graphene::db::object),\n                    (account)(catalog)(key)(value))\nFC_REFLECT_ENUM( graphene::custom_operations::types, (account_map))\n"
  },
  {
    "path": "libraries/plugins/custom_operations/include/graphene/custom_operations/custom_operations.hpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/chain/database.hpp>\n\n#include \"custom_objects.hpp\"\n\nnamespace graphene { namespace custom_operations {\n\nusing namespace std;\nusing graphene::protocol::account_id_type;\n\nstruct account_storage_map : chain::base_operation\n{\n   bool remove;\n   string catalog;\n   flat_map<string, optional<string>> key_values;\n\n   void validate()const;\n};\n\n} } //graphene::custom_operations\n\nFC_REFLECT( graphene::custom_operations::account_storage_map, (remove)(catalog)(key_values) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::custom_operations::account_storage_map )\n"
  },
  {
    "path": "libraries/plugins/custom_operations/include/graphene/custom_operations/custom_operations_plugin.hpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <graphene/custom_operations/custom_objects.hpp>\n#include <graphene/custom_operations/custom_operations.hpp>\n#include <graphene/custom_operations/custom_evaluators.hpp>\n\nnamespace graphene { namespace custom_operations {\nusing namespace chain;\n\nnamespace detail\n{\n    class custom_operations_plugin_impl;\n}\n\nclass custom_operations_plugin : public graphene::app::plugin\n{\n   public:\n      custom_operations_plugin();\n      virtual ~custom_operations_plugin();\n\n      std::string plugin_name()const override;\n      std::string plugin_description()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      friend class detail::custom_operations_plugin_impl;\n      std::unique_ptr<detail::custom_operations_plugin_impl> my;\n};\n\ntypedef fc::static_variant<account_storage_map> custom_plugin_operation;\n\n} } //graphene::custom_operations\n\nFC_REFLECT_TYPENAME( graphene::custom_operations::custom_plugin_operation )\n"
  },
  {
    "path": "libraries/plugins/debug_witness/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/debug_witness/*.hpp\")\n\nadd_library( graphene_debug_witness\n             debug_api.cpp\n             debug_witness.cpp\n           )\n\ntarget_link_libraries( graphene_debug_witness graphene_chain graphene_app )\ntarget_include_directories( graphene_debug_witness\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\ninstall( TARGETS\n   graphene_debug_witness\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/debug_witness\" )\n"
  },
  {
    "path": "libraries/plugins/debug_witness/debug_api.cpp",
    "content": "\n#include <fc/filesystem.hpp>\n#include <fc/optional.hpp>\n#include <fc/variant_object.hpp>\n\n#include <graphene/app/application.hpp>\n\n#include <graphene/chain/block_database.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <graphene/utilities/key_conversion.hpp>\n\n#include <graphene/debug_witness/debug_api.hpp>\n#include <graphene/debug_witness/debug_witness.hpp>\n\nnamespace graphene { namespace debug_witness {\n\nnamespace detail {\n\nclass debug_api_impl\n{\n   public:\n      explicit debug_api_impl( graphene::app::application& _app );\n\n      void debug_push_blocks( const std::string& src_filename, uint32_t count );\n      void debug_generate_blocks( const std::string& debug_key, uint32_t count );\n      void debug_update_object( const fc::variant_object& update );\n      void debug_stream_json_objects( const std::string& filename );\n      void debug_stream_json_objects_flush();\n      std::shared_ptr< graphene::debug_witness_plugin::debug_witness_plugin > get_plugin();\n\n      graphene::app::application& app;\n};\n\ndebug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app )\n{}\n\n\nvoid debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count )\n{\n   if( count == 0 )\n      return;\n\n   std::shared_ptr< graphene::chain::database > db = app.chain_database();\n   fc::path src_path = fc::path( src_filename );\n   if( fc::is_directory( src_path ) )\n   {\n      ilog( \"Loading ${n} from block_database ${fn}\", (\"n\", count)(\"fn\", src_filename) );\n      graphene::chain::block_database bdb;\n      bdb.open( src_path );\n      uint32_t first_block = db->head_block_num()+1;\n      for( uint32_t i=0; i<count; i++ )\n      {\n         fc::optional< graphene::chain::signed_block > block = bdb.fetch_by_number( first_block+i );\n         if( !block.valid() )\n         {\n            wlog( \"Block database ${fn} only contained ${i} of ${n} requested blocks\", (\"i\", i)(\"n\", count)(\"fn\", src_filename) );\n            return;\n         }\n         try\n         {\n            db->push_block( *block );\n         }\n         catch( const fc::exception& e )\n         {\n            elog( \"Got exception pushing block ${bn} : ${bid} (${i} of ${n})\", (\"bn\", block->block_num())(\"bid\", block->id())(\"i\", i)(\"n\", count) );\n            elog( \"Exception backtrace: ${bt}\", (\"bt\", e.to_detail_string()) );\n         }\n      }\n      ilog( \"Completed loading block_database successfully\" );\n   }\n}\n\nvoid debug_api_impl::debug_generate_blocks( const std::string& debug_key, uint32_t count )\n{\n   if( count == 0 )\n      return;\n\n   fc::optional<fc::ecc::private_key> debug_private_key = graphene::utilities::wif_to_key( debug_key );\n   FC_ASSERT( debug_private_key.valid() );\n   graphene::chain::public_key_type debug_public_key = debug_private_key->get_public_key();\n\n   std::shared_ptr< graphene::chain::database > db = app.chain_database();\n   for( uint32_t i=0; i<count; i++ )\n   {\n      graphene::chain::witness_id_type scheduled_witness = db->get_scheduled_witness( 1 );\n      fc::time_point_sec scheduled_time = db->get_slot_time( 1 );\n      graphene::chain::public_key_type scheduled_key = scheduled_witness( *db ).signing_key;\n      if( scheduled_key != debug_public_key )\n      {\n         ilog( \"Modified key for witness ${w}\", (\"w\", scheduled_witness) );\n         fc::limited_mutable_variant_object update( GRAPHENE_MAX_NESTED_OBJECTS );\n         update(\"_action\", \"update\")(\"id\", scheduled_witness)(\"signing_key\", debug_public_key);\n         db->debug_update( update );\n      }\n      db->generate_block( scheduled_time, scheduled_witness, *debug_private_key, graphene::chain::database::skip_nothing );\n   }\n}\n\nvoid debug_api_impl::debug_update_object( const fc::variant_object& update )\n{\n   std::shared_ptr< graphene::chain::database > db = app.chain_database();\n   db->debug_update( update );\n}\n\nstd::shared_ptr< graphene::debug_witness_plugin::debug_witness_plugin > debug_api_impl::get_plugin()\n{\n   return app.get_plugin< graphene::debug_witness_plugin::debug_witness_plugin >( \"debug_witness\" );\n}\n\nvoid debug_api_impl::debug_stream_json_objects( const std::string& filename )\n{\n   get_plugin()->set_json_object_stream( filename );\n}\n\nvoid debug_api_impl::debug_stream_json_objects_flush()\n{\n   get_plugin()->flush_json_object_stream();\n}\n\n} // detail\n\ndebug_api::debug_api( graphene::app::application& app )\n{\n   my = std::make_shared< detail::debug_api_impl >(app);\n}\n\nvoid debug_api::debug_push_blocks( std::string source_filename, uint32_t count )\n{\n   my->debug_push_blocks( source_filename, count );\n}\n\nvoid debug_api::debug_generate_blocks( std::string debug_key, uint32_t count )\n{\n   my->debug_generate_blocks( debug_key, count );\n}\n\nvoid debug_api::debug_update_object( fc::variant_object update )\n{\n   my->debug_update_object( update );\n}\n\nvoid debug_api::debug_stream_json_objects( std::string filename )\n{\n   my->debug_stream_json_objects( filename );\n}\n\nvoid debug_api::debug_stream_json_objects_flush()\n{\n   my->debug_stream_json_objects_flush();\n}\n\n\n} } // graphene::debug_witness\n"
  },
  {
    "path": "libraries/plugins/debug_witness/debug_witness.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/debug_witness/debug_witness.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <graphene/utilities/key_conversion.hpp>\n\n#include <fc/thread/thread.hpp>\n\n#include <iostream>\n\nusing namespace graphene::debug_witness_plugin;\nusing std::string;\nusing std::vector;\n\nnamespace bpo = boost::program_options;\n\ndebug_witness_plugin::~debug_witness_plugin() {}\n\nvoid debug_witness_plugin::plugin_set_program_options(\n   boost::program_options::options_description& command_line_options,\n   boost::program_options::options_description& config_file_options)\n{\n   auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string(\"nathan\")));\n   command_line_options.add_options()\n         (\"debug-private-key\", bpo::value<vector<string>>()->composing()->multitoken()->\n          DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),\n          \"Tuple of [PublicKey, WIF private key] (may specify multiple times)\");\n   config_file_options.add(command_line_options);\n}\n\nstd::string debug_witness_plugin::plugin_name()const\n{\n   return \"debug_witness\";\n}\n\nvoid debug_witness_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{ try {\n   ilog(\"debug_witness plugin:  plugin_initialize() begin\");\n   _options = &options;\n\n   if( options.count(\"debug-private-key\") )\n   {\n      const std::vector<std::string> key_id_to_wif_pair_strings = options[\"debug-private-key\"].as<std::vector<std::string>>();\n      for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)\n      {\n         auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string> >(key_id_to_wif_pair_string, GRAPHENE_MAX_NESTED_OBJECTS);\n         idump((key_id_to_wif_pair));\n         fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second);\n         if (!private_key)\n         {\n            // the key isn't in WIF format; see if they are still passing the old native private key format.  This is\n            // just here to ease the transition, can be removed soon\n            try\n            {\n               private_key = fc::variant( key_id_to_wif_pair.second, GRAPHENE_MAX_NESTED_OBJECTS ).as<fc::ecc::private_key>( GRAPHENE_MAX_NESTED_OBJECTS );\n            }\n            catch (const fc::exception&)\n            {\n               FC_THROW(\"Invalid WIF-format private key ${key_string}\", (\"key_string\", key_id_to_wif_pair.second));\n            }\n         }\n         _private_keys[key_id_to_wif_pair.first] = *private_key;\n      }\n   }\n   ilog(\"debug_witness plugin:  plugin_initialize() end\");\n} FC_LOG_AND_RETHROW() }\n\nvoid debug_witness_plugin::plugin_startup()\n{\n   ilog(\"debug_witness_plugin::plugin_startup() begin\");\n   chain::database& db = database();\n\n   // connect needed signals\n\n   _applied_block_conn  = db.applied_block.connect([this](const graphene::chain::signed_block& b){ on_applied_block(b); });\n   _changed_objects_conn = db.changed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_changed_objects(ids, impacted_accounts); });\n   _removed_objects_conn = db.removed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*>& objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_removed_objects(ids, objs, impacted_accounts); });\n\n   return;\n}\n\nvoid debug_witness_plugin::on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts )\n{\n   if( _json_object_stream && (ids.size() > 0) )\n   {\n      const chain::database& db = database();\n      for( const graphene::db::object_id_type& oid : ids )\n      {\n         const graphene::db::object* obj = db.find_object( oid );\n         if( obj != nullptr )\n         {\n            (*_json_object_stream) << fc::json::to_string( obj->to_variant() ) << '\\n';\n         }\n      }\n   }\n}\n\nvoid debug_witness_plugin::on_removed_objects( const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*> objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts )\n{\n   if( _json_object_stream )\n   {\n      for( const graphene::db::object* obj : objs )\n      {\n         (*_json_object_stream) << \"{\\\"id\\\":\" << fc::json::to_string( obj->id ) << \"}\\n\";\n      }\n   }\n}\n\nvoid debug_witness_plugin::on_applied_block( const graphene::chain::signed_block& b )\n{\n   if( _json_object_stream )\n   {\n      (*_json_object_stream) << \"{\\\"bn\\\":\" << fc::to_string( b.block_num() ) << \"}\\n\";\n   }\n}\n\nvoid debug_witness_plugin::set_json_object_stream( const std::string& filename )\n{\n   if( _json_object_stream )\n   {\n      _json_object_stream->close();\n      _json_object_stream.reset();\n   }\n   _json_object_stream = std::make_shared< std::ofstream >( filename );\n}\n\nvoid debug_witness_plugin::flush_json_object_stream()\n{\n   if( _json_object_stream )\n      _json_object_stream->flush();\n}\n\nvoid debug_witness_plugin::plugin_shutdown()\n{\n   if( _json_object_stream )\n   {\n      _json_object_stream->close();\n      _json_object_stream.reset();\n   }\n   return;\n}\n"
  },
  {
    "path": "libraries/plugins/debug_witness/include/graphene/debug_witness/debug_api.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <memory>\n#include <string>\n\n#include <fc/api.hpp>\n#include <fc/variant_object.hpp>\n\nnamespace graphene { namespace app {\nclass application;\n} }\n\nnamespace graphene { namespace debug_witness {\n\nnamespace detail {\nclass debug_api_impl;\n}\n\nclass debug_api\n{\n   public:\n      debug_api( graphene::app::application& app );\n\n      /**\n       * Push blocks from existing database.\n       */\n      void debug_push_blocks( std::string src_filename, uint32_t count );\n\n      /**\n       * Generate blocks locally.\n       */\n      void debug_generate_blocks( std::string debug_key, uint32_t count );\n\n      /**\n       * Directly manipulate database objects (will undo and re-apply last block with new changes post-applied).\n       */\n      void debug_update_object( fc::variant_object update );\n\n      /**\n       * Start a node with given initial path.\n       */\n      // not implemented\n      //void start_node( std::string name, std::string initial_db_path );\n\n      /**\n       * Save the database to disk.\n       */\n      // not implemented\n      //void save_db( std::string db_path );\n\n      /**\n       * Stream objects to file.  (Hint:  Create with mkfifo and pipe it to a script)\n       */\n\n      void debug_stream_json_objects( std::string filename );\n\n      /**\n       * Flush streaming file.\n       */\n      void debug_stream_json_objects_flush();\n\n      std::shared_ptr< detail::debug_api_impl > my;\n};\n\n} }\n\nFC_API(graphene::debug_witness::debug_api,\n       (debug_push_blocks)\n       (debug_generate_blocks)\n       (debug_update_object)\n       (debug_stream_json_objects)\n       (debug_stream_json_objects_flush)\n     )\n"
  },
  {
    "path": "libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/protocol/types.hpp>\n\n#include <fc/thread/future.hpp>\n#include <fc/container/flat.hpp>\n\nnamespace graphene { namespace debug_witness_plugin {\n\nclass debug_witness_plugin : public graphene::app::plugin {\npublic:\n   ~debug_witness_plugin();\n\n   std::string plugin_name()const override;\n\n   virtual void plugin_set_program_options(\n      boost::program_options::options_description &command_line_options,\n      boost::program_options::options_description &config_file_options\n      ) override;\n\n   virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;\n   virtual void plugin_startup() override;\n   virtual void plugin_shutdown() override;\n\n   void set_json_object_stream( const std::string& filename );\n   void flush_json_object_stream();\n\nprivate:\n\n   void on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );\n   void on_removed_objects( const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*> objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );\n   void on_applied_block( const graphene::chain::signed_block& b );\n\n   boost::program_options::variables_map _options;\n\n   std::map<chain::public_key_type, fc::ecc::private_key, chain::pubkey_comparator> _private_keys;\n\n   std::shared_ptr< std::ofstream > _json_object_stream;\n   boost::signals2::scoped_connection _applied_block_conn;\n   boost::signals2::scoped_connection _changed_objects_conn;\n   boost::signals2::scoped_connection _removed_objects_conn;\n};\n\n} } //graphene::debug_witness_plugin\n"
  },
  {
    "path": "libraries/plugins/delayed_node/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/delayed_node/*.hpp\")\n\nadd_library( graphene_delayed_node \n             delayed_node_plugin.cpp\n           )\n\ntarget_link_libraries( graphene_delayed_node graphene_chain graphene_app )\ntarget_include_directories( graphene_delayed_node\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties( delayed_node_plugin.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_delayed_node\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "libraries/plugins/delayed_node/delayed_node_plugin.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/delayed_node/delayed_node_plugin.hpp>\n#include <graphene/protocol/types.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/app/api.hpp>\n\n#include <fc/network/http/websocket.hpp>\n#include <fc/rpc/websocket_api.hpp>\n#include <fc/api.hpp>\n\nnamespace graphene { namespace delayed_node {\nnamespace bpo = boost::program_options;\n\nnamespace detail {\nstruct delayed_node_plugin_impl {\n   std::string remote_endpoint;\n   fc::http::websocket_client client;\n   std::shared_ptr<fc::rpc::websocket_api_connection> client_connection;\n   fc::api<graphene::app::database_api> database_api;\n   boost::signals2::scoped_connection client_connection_closed;\n   graphene::chain::block_id_type last_received_remote_head;\n   graphene::chain::block_id_type last_processed_remote_head;\n};\n}\n\ndelayed_node_plugin::delayed_node_plugin()\n   : my(nullptr)\n{}\n\ndelayed_node_plugin::~delayed_node_plugin()\n{}\n\nvoid delayed_node_plugin::plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg)\n{\n   cli.add_options()\n         (\"trusted-node\", boost::program_options::value<std::string>(), \"RPC endpoint of a trusted validating node (required for delayed_node)\")\n         ;\n   cfg.add(cli);\n}\n\nvoid delayed_node_plugin::connect()\n{\n   my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(\n                              my->client.connect(my->remote_endpoint),\n                              GRAPHENE_NET_MAX_NESTED_OBJECTS );\n   my->database_api = my->client_connection->get_remote_api<graphene::app::database_api>(0);\n   my->client_connection_closed = my->client_connection->closed.connect([this] {\n      connection_failed();\n   });\n}\n\nvoid delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{\n   FC_ASSERT(options.count(\"trusted-node\") > 0);\n   my = std::unique_ptr<detail::delayed_node_plugin_impl>{ new detail::delayed_node_plugin_impl() };\n   my->remote_endpoint = \"ws://\" + options.at(\"trusted-node\").as<std::string>();\n}\n\nvoid delayed_node_plugin::sync_with_trusted_node()\n{\n   auto& db = database();\n   uint32_t synced_blocks = 0;\n   uint32_t pass_count = 0;\n   while( true )\n   {\n      graphene::chain::dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties();\n      if( remote_dpo.last_irreversible_block_num <= db.head_block_num() )\n      {\n         if( remote_dpo.last_irreversible_block_num < db.head_block_num() )\n         {\n            wlog( \"Trusted node seems to be behind delayed node\" );\n         }\n         if( synced_blocks > 1 )\n         {\n            ilog( \"Delayed node finished syncing ${n} blocks in ${k} passes\", (\"n\", synced_blocks)(\"k\", pass_count) );\n         }\n         break;\n      }\n      pass_count++;\n      while( remote_dpo.last_irreversible_block_num > db.head_block_num() )\n      {\n         fc::optional<graphene::chain::signed_block> block = my->database_api->get_block( db.head_block_num()+1 );\n         // TODO: during sync, decouple requesting blocks from preprocessing + applying them\n         FC_ASSERT(block, \"Trusted node claims it has blocks it doesn't actually have.\");\n         ilog(\"Pushing block #${n}\", (\"n\", block->block_num()));\n         db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait();\n         db.push_block(*block);\n         synced_blocks++;\n      }\n   }\n}\n\nvoid delayed_node_plugin::mainloop()\n{\n   while( true )\n   {\n      try\n      {\n         fc::usleep( fc::microseconds( 296645 ) );  // wake up a little over 3Hz\n\n         if( my->last_received_remote_head == my->last_processed_remote_head )\n            continue;\n\n         sync_with_trusted_node();\n         my->last_processed_remote_head = my->last_received_remote_head;\n      }\n      catch( const fc::exception& e )\n      {\n         elog(\"Error during connection: ${e}\", (\"e\", e.to_detail_string()));\n      }\n   }\n}\n\nvoid delayed_node_plugin::plugin_startup()\n{\n   fc::async([this]()\n   {\n      mainloop();\n   });\n\n   try\n   {\n      connect();\n      my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )\n      {\n         fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );\n      } );\n      return;\n   }\n   catch (const fc::exception& e)\n   {\n      elog(\"Error during connection: ${e}\", (\"e\", e.to_detail_string()));\n   }\n   fc::async([this]{connection_failed();});\n}\n\nvoid delayed_node_plugin::connection_failed()\n{\n   elog(\"Connection to trusted node failed; retrying in 5 seconds...\");\n   fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5));\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/delayed_node/include/graphene/delayed_node/delayed_node_plugin.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n\nnamespace graphene { namespace delayed_node {\nnamespace detail { struct delayed_node_plugin_impl; }\n\nclass delayed_node_plugin : public graphene::app::plugin\n{\n   std::unique_ptr<detail::delayed_node_plugin_impl> my;\npublic:\n   delayed_node_plugin();\n   virtual ~delayed_node_plugin();\n\n   std::string plugin_name()const override { return \"delayed_node\"; }\n   virtual void plugin_set_program_options(boost::program_options::options_description&,\n                                           boost::program_options::options_description& cfg) override;\n   virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n   virtual void plugin_startup() override;\n   void mainloop();\n\nprotected:\n   void connection_failed();\n   void connect();\n   void sync_with_trusted_node();\n};\n\n} } //graphene::account_history\n\n"
  },
  {
    "path": "libraries/plugins/elasticsearch/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/elasticsearch/*.hpp\")\n\nadd_library( graphene_elasticsearch\n        elasticsearch_plugin.cpp\n           )\n\nfind_curl()\n\ninclude_directories(${CURL_INCLUDE_DIRS})\nif(MSVC)\n  set_source_files_properties(elasticsearch_plugin.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\nif(CURL_STATICLIB)\n  SET_TARGET_PROPERTIES(graphene_elasticsearch PROPERTIES\n  COMPILE_DEFINITIONS \"CURL_STATICLIB\")\nendif(CURL_STATICLIB)\ntarget_link_libraries( graphene_elasticsearch graphene_chain graphene_app ${CURL_LIBRARIES} )\ntarget_include_directories( graphene_elasticsearch\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\"\n                            PUBLIC \"${CURL_INCLUDE_DIR}\" )\n\n\ninstall( TARGETS\n   graphene_elasticsearch\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/elasticsearch\" )\n\n"
  },
  {
    "path": "libraries/plugins/elasticsearch/elasticsearch_plugin.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/elasticsearch/elasticsearch_plugin.hpp>\n#include <graphene/chain/impacted.hpp>\n#include <graphene/chain/account_evaluator.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <curl/curl.h>\n\nnamespace graphene { namespace elasticsearch {\n\nnamespace detail\n{\n\nclass elasticsearch_plugin_impl\n{\n   public:\n      elasticsearch_plugin_impl(elasticsearch_plugin& _plugin)\n         : _self( _plugin )\n      {  curl = curl_easy_init(); }\n      virtual ~elasticsearch_plugin_impl();\n\n      bool update_account_histories( const signed_block& b );\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      elasticsearch_plugin& _self;\n      primary_index< operation_history_index >* _oho_index;\n\n      std::string _elasticsearch_node_url = \"http://localhost:9200/\";\n      uint32_t _elasticsearch_bulk_replay = 10000;\n      uint32_t _elasticsearch_bulk_sync = 100;\n      bool _elasticsearch_visitor = false;\n      std::string _elasticsearch_basic_auth = \"\";\n      std::string _elasticsearch_index_prefix = \"bitshares-\";\n      bool _elasticsearch_operation_object = true;\n      uint32_t _elasticsearch_start_es_after_block = 0;\n      bool _elasticsearch_operation_string = false;\n      mode _elasticsearch_mode = mode::only_save;\n      CURL *curl; // curl handler\n      vector <string> bulk_lines; //  vector of op lines\n      vector<std::string> prepare;\n\n      graphene::utilities::ES es;\n      uint32_t limit_documents;\n      int16_t op_type;\n      operation_history_struct os;\n      block_struct bs;\n      visitor_struct vs;\n      bulk_struct bulk_line_struct;\n      std::string bulk_line;\n      std::string index_name;\n      bool is_sync = false;\n   private:\n      bool add_elasticsearch( const account_id_type account_id, const optional<operation_history_object>& oho, const uint32_t block_number );\n      const account_transaction_history_object& addNewEntry(const account_statistics_object& stats_obj,\n                                                            const account_id_type& account_id,\n                                                            const optional <operation_history_object>& oho);\n      const account_statistics_object& getStatsObject(const account_id_type& account_id);\n      void growStats(const account_statistics_object& stats_obj, const account_transaction_history_object& ath);\n      void getOperationType(const optional <operation_history_object>& oho);\n      void doOperationHistory(const optional <operation_history_object>& oho);\n      void doBlock(uint32_t trx_in_block, const signed_block& b);\n      void doVisitor(const optional <operation_history_object>& oho);\n      void checkState(const fc::time_point_sec& block_time);\n      void cleanObjects(const account_transaction_history_id_type& ath, const account_id_type& account_id);\n      void createBulkLine(const account_transaction_history_object& ath);\n      void prepareBulk(const account_transaction_history_id_type& ath_id);\n      void populateESstruct();\n};\n\nelasticsearch_plugin_impl::~elasticsearch_plugin_impl()\n{\n   if (curl) {\n      curl_easy_cleanup(curl);\n      curl = nullptr;\n   }\n   return;\n}\n\nbool elasticsearch_plugin_impl::update_account_histories( const signed_block& b )\n{\n   checkState(b.timestamp);\n   index_name = graphene::utilities::generateIndexName(b.timestamp, _elasticsearch_index_prefix);\n\n   graphene::chain::database& db = database();\n   const vector<optional< operation_history_object > >& hist = db.get_applied_operations();\n   bool is_first = true;\n   auto skip_oho_id = [&is_first,&db,this]() {\n      if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo\n      {\n         db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );\n         is_first = false;\n      }\n      else\n         _oho_index->use_next_id();\n   };\n   for( const optional< operation_history_object >& o_op : hist ) {\n      optional <operation_history_object> oho;\n\n      auto create_oho = [&]() {\n         is_first = false;\n         return optional<operation_history_object>(\n               db.create<operation_history_object>([&](operation_history_object &h) {\n                  if (o_op.valid())\n                  {\n                     h.op           = o_op->op;\n                     h.result       = o_op->result;\n                     h.block_num    = o_op->block_num;\n                     h.trx_in_block = o_op->trx_in_block;\n                     h.op_in_trx    = o_op->op_in_trx;\n                     h.virtual_op   = o_op->virtual_op;\n                  }\n               }));\n      };\n\n      if( !o_op.valid() ) {\n         skip_oho_id();\n         continue;\n      }\n      oho = create_oho();\n\n      // populate what we can before impacted loop\n      getOperationType(oho);\n      doOperationHistory(oho);\n      doBlock(oho->trx_in_block, b);\n      if(_elasticsearch_visitor)\n         doVisitor(oho);\n\n      const operation_history_object& op = *o_op;\n\n      // get the set of accounts this operation applies to\n      flat_set<account_id_type> impacted;\n      vector<authority> other;\n      // fee_payer is added here\n      operation_get_required_authorities( op.op, impacted, impacted, other,\n                                          MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );\n\n      if( op.op.is_type< account_create_operation >() )\n         impacted.insert( op.result.get<object_id_type>() );\n      else\n         operation_get_impacted_accounts( op.op, impacted,\n                                          MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );\n\n      for( auto& a : other )\n         for( auto& item : a.account_auths )\n            impacted.insert( item.first );\n\n      for( auto& account_id : impacted )\n      {\n         if(!add_elasticsearch( account_id, oho, b.block_num() ))\n            return false;\n      }\n   }\n   // we send bulk at end of block when we are in sync for better real time client experience\n   if(is_sync)\n   {\n      populateESstruct();\n      if(es.bulk_lines.size() > 0)\n      {\n         prepare.clear();\n         if(!graphene::utilities::SendBulk(std::move(es)))\n            return false;\n         else\n            bulk_lines.clear();\n      }\n   }\n\n   if(bulk_lines.size() != limit_documents)\n      bulk_lines.reserve(limit_documents);\n\n   return true;\n}\n\nvoid elasticsearch_plugin_impl::checkState(const fc::time_point_sec& block_time)\n{\n   if((fc::time_point::now() - block_time) < fc::seconds(30))\n   {\n      limit_documents = _elasticsearch_bulk_sync;\n      is_sync = true;\n   }\n   else\n   {\n      limit_documents = _elasticsearch_bulk_replay;\n      is_sync = false;\n   }\n}\n\nvoid elasticsearch_plugin_impl::getOperationType(const optional <operation_history_object>& oho)\n{\n   if (!oho->id.is_null())\n      op_type = oho->op.which();\n}\n\nvoid elasticsearch_plugin_impl::doOperationHistory(const optional <operation_history_object>& oho)\n{\n   os.trx_in_block = oho->trx_in_block;\n   os.op_in_trx = oho->op_in_trx;\n   os.operation_result = fc::json::to_string(oho->result);\n   os.virtual_op = oho->virtual_op;\n\n   if(_elasticsearch_operation_object) {\n      oho->op.visit(fc::from_static_variant(os.op_object, FC_PACK_MAX_DEPTH));\n      adaptor_struct adaptor;\n      os.op_object = adaptor.adapt(os.op_object.get_object());\n   }\n   if(_elasticsearch_operation_string)\n      os.op = fc::json::to_string(oho->op);\n}\n\nvoid elasticsearch_plugin_impl::doBlock(uint32_t trx_in_block, const signed_block& b)\n{\n   std::string trx_id = \"\";\n   if(trx_in_block < b.transactions.size())\n      trx_id = b.transactions[trx_in_block].id().str();\n   bs.block_num = b.block_num();\n   bs.block_time = b.timestamp;\n   bs.trx_id = trx_id;\n}\n\nvoid elasticsearch_plugin_impl::doVisitor(const optional <operation_history_object>& oho)\n{\n   graphene::chain::database& db = database();\n\n   operation_visitor o_v;\n   oho->op.visit(o_v);\n\n   auto fee_asset = o_v.fee_asset(db);\n   vs.fee_data.asset = o_v.fee_asset;\n   vs.fee_data.asset_name = fee_asset.symbol;\n   vs.fee_data.amount = o_v.fee_amount;\n   vs.fee_data.amount_units = (o_v.fee_amount.value)/(double)asset::scaled_precision(fee_asset.precision).value;\n\n   auto transfer_asset = o_v.transfer_asset_id(db);\n   vs.transfer_data.asset = o_v.transfer_asset_id;\n   vs.transfer_data.asset_name = transfer_asset.symbol;\n   vs.transfer_data.amount = o_v.transfer_amount;\n   vs.transfer_data.amount_units = (o_v.transfer_amount.value)/(double)asset::scaled_precision(transfer_asset.precision).value;\n   vs.transfer_data.from = o_v.transfer_from;\n   vs.transfer_data.to = o_v.transfer_to;\n\n   auto fill_pays_asset = o_v.fill_pays_asset_id(db);\n   auto fill_receives_asset = o_v.fill_receives_asset_id(db);\n   vs.fill_data.order_id = o_v.fill_order_id;\n   vs.fill_data.account_id = o_v.fill_account_id;\n   vs.fill_data.pays_asset_id = o_v.fill_pays_asset_id;\n   vs.fill_data.pays_asset_name = fill_pays_asset.symbol;\n   vs.fill_data.pays_amount = o_v.fill_pays_amount;\n   vs.fill_data.pays_amount_units = (o_v.fill_pays_amount.value)/(double)asset::scaled_precision(fill_pays_asset.precision).value;\n   vs.fill_data.receives_asset_id = o_v.fill_receives_asset_id;\n   vs.fill_data.receives_asset_name = fill_receives_asset.symbol;\n   vs.fill_data.receives_amount = o_v.fill_receives_amount;\n   vs.fill_data.receives_amount_units = (o_v.fill_receives_amount.value)/(double)asset::scaled_precision(fill_receives_asset.precision).value;\n\n   auto fill_price = (o_v.fill_receives_amount.value/(double)asset::scaled_precision(fill_receives_asset.precision).value) /\n           (o_v.fill_pays_amount.value/(double)asset::scaled_precision(fill_pays_asset.precision).value);\n   vs.fill_data.fill_price_units = fill_price;\n   vs.fill_data.fill_price = o_v.fill_fill_price;\n   vs.fill_data.is_maker = o_v.fill_is_maker;\n}\n\nbool elasticsearch_plugin_impl::add_elasticsearch( const account_id_type account_id,\n                                                   const optional <operation_history_object>& oho,\n                                                   const uint32_t block_number)\n{\n   const auto &stats_obj = getStatsObject(account_id);\n   const auto &ath = addNewEntry(stats_obj, account_id, oho);\n   growStats(stats_obj, ath);\n   if(block_number > _elasticsearch_start_es_after_block)  {\n      createBulkLine(ath);\n      prepareBulk(ath.id);\n   }\n   cleanObjects(ath.id, account_id);\n\n   if (curl && bulk_lines.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech\n      prepare.clear();\n      populateESstruct();\n      if(!graphene::utilities::SendBulk(std::move(es)))\n         return false;\n      else\n         bulk_lines.clear();\n   }\n\n   return true;\n}\n\nconst account_statistics_object& elasticsearch_plugin_impl::getStatsObject(const account_id_type& account_id)\n{\n   graphene::chain::database& db = database();\n   const auto &stats_obj = db.get_account_stats_by_owner(account_id);\n\n   return stats_obj;\n}\n\nconst account_transaction_history_object& elasticsearch_plugin_impl::addNewEntry(const account_statistics_object& stats_obj,\n                                                                                 const account_id_type& account_id,\n                                                                                 const optional <operation_history_object>& oho)\n{\n   graphene::chain::database& db = database();\n   const auto &ath = db.create<account_transaction_history_object>([&](account_transaction_history_object &obj) {\n      obj.operation_id = oho->id;\n      obj.account = account_id;\n      obj.sequence = stats_obj.total_ops + 1;\n      obj.next = stats_obj.most_recent_op;\n   });\n\n   return ath;\n}\n\nvoid elasticsearch_plugin_impl::growStats(const account_statistics_object& stats_obj,\n                                          const account_transaction_history_object& ath)\n{\n   graphene::chain::database& db = database();\n   db.modify(stats_obj, [&](account_statistics_object &obj) {\n      obj.most_recent_op = ath.id;\n      obj.total_ops = ath.sequence;\n   });\n}\n\nvoid elasticsearch_plugin_impl::createBulkLine(const account_transaction_history_object& ath)\n{\n   bulk_line_struct.account_history = ath;\n   bulk_line_struct.operation_history = os;\n   bulk_line_struct.operation_type = op_type;\n   bulk_line_struct.operation_id_num = ath.operation_id.instance.value;\n   bulk_line_struct.block_data = bs;\n   if(_elasticsearch_visitor)\n      bulk_line_struct.additional_data = vs;\n   bulk_line = fc::json::to_string(bulk_line_struct, fc::json::legacy_generator);\n}\n\nvoid elasticsearch_plugin_impl::prepareBulk(const account_transaction_history_id_type& ath_id)\n{\n   const std::string _id = fc::json::to_string(ath_id);\n   fc::mutable_variant_object bulk_header;\n   bulk_header[\"_index\"] = index_name;\n   bulk_header[\"_type\"] = \"data\";\n   bulk_header[\"_id\"] = fc::to_string(ath_id.space_id) + \".\" + fc::to_string(ath_id.type_id) + \".\"\n                      + fc::to_string(ath_id.instance.value);\n   prepare = graphene::utilities::createBulk(bulk_header, std::move(bulk_line));\n   std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk_lines));\n   prepare.clear();\n}\n\nvoid elasticsearch_plugin_impl::cleanObjects(const account_transaction_history_id_type& ath_id, const account_id_type& account_id)\n{\n   graphene::chain::database& db = database();\n   // remove everything except current object from ath\n   const auto &his_idx = db.get_index_type<account_transaction_history_index>();\n   const auto &by_seq_idx = his_idx.indices().get<by_seq>();\n   auto itr = by_seq_idx.lower_bound(boost::make_tuple(account_id, 0));\n   if (itr != by_seq_idx.end() && itr->account == account_id && itr->id != ath_id) {\n      // if found, remove the entry\n      const auto remove_op_id = itr->operation_id;\n      const auto itr_remove = itr;\n      ++itr;\n      db.remove( *itr_remove );\n      // modify previous node's next pointer\n      // this should be always true, but just have a check here\n      if( itr != by_seq_idx.end() && itr->account == account_id )\n      {\n         db.modify( *itr, [&]( account_transaction_history_object& obj ){\n            obj.next = account_transaction_history_id_type();\n         });\n      }\n      // do the same on oho\n      const auto &by_opid_idx = his_idx.indices().get<by_opid>();\n      if (by_opid_idx.find(remove_op_id) == by_opid_idx.end()) {\n         db.remove(remove_op_id(db));\n      }\n   }\n}\n\nvoid elasticsearch_plugin_impl::populateESstruct()\n{\n   es.curl = curl;\n   es.bulk_lines = std::move(bulk_lines);\n   es.elasticsearch_url = _elasticsearch_node_url;\n   es.auth = _elasticsearch_basic_auth;\n   es.index_prefix = _elasticsearch_index_prefix;\n   es.endpoint = \"\";\n   es.query = \"\";\n}\n\n} // end namespace detail\n\nelasticsearch_plugin::elasticsearch_plugin() :\n   my( new detail::elasticsearch_plugin_impl(*this) )\n{\n}\n\nelasticsearch_plugin::~elasticsearch_plugin()\n{\n}\n\nstd::string elasticsearch_plugin::plugin_name()const\n{\n   return \"elasticsearch\";\n}\nstd::string elasticsearch_plugin::plugin_description()const\n{\n   return \"Stores account history data in elasticsearch database(EXPERIMENTAL).\";\n}\n\nvoid elasticsearch_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"elasticsearch-node-url\", boost::program_options::value<std::string>(),\n               \"Elastic Search database node url(http://localhost:9200/)\")\n         (\"elasticsearch-bulk-replay\", boost::program_options::value<uint32_t>(),\n               \"Number of bulk documents to index on replay(10000)\")\n         (\"elasticsearch-bulk-sync\", boost::program_options::value<uint32_t>(),\n               \"Number of bulk documents to index on a syncronied chain(100)\")\n         (\"elasticsearch-visitor\", boost::program_options::value<bool>(),\n               \"Use visitor to index additional data(slows down the replay(false))\")\n         (\"elasticsearch-basic-auth\", boost::program_options::value<std::string>(),\n               \"Pass basic auth to elasticsearch database('')\")\n         (\"elasticsearch-index-prefix\", boost::program_options::value<std::string>(),\n               \"Add a prefix to the index(bitshares-)\")\n         (\"elasticsearch-operation-object\", boost::program_options::value<bool>(),\n               \"Save operation as object(true)\")\n         (\"elasticsearch-start-es-after-block\", boost::program_options::value<uint32_t>(),\n               \"Start doing ES job after block(0)\")\n         (\"elasticsearch-operation-string\", boost::program_options::value<bool>(),\n               \"Save operation as string. Needed to serve history api calls(false)\")\n         (\"elasticsearch-mode\", boost::program_options::value<uint16_t>(),\n               \"Mode of operation: only_save(0), only_query(1), all(2) - Default: 0\")\n         ;\n   cfg.add(cli);\n}\n\nvoid elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{\n   my->_oho_index = database().add_index< primary_index< operation_history_index > >();\n   database().add_index< primary_index< account_transaction_history_index > >();\n\n   if (options.count(\"elasticsearch-node-url\")) {\n      my->_elasticsearch_node_url = options[\"elasticsearch-node-url\"].as<std::string>();\n   }\n   if (options.count(\"elasticsearch-bulk-replay\")) {\n      my->_elasticsearch_bulk_replay = options[\"elasticsearch-bulk-replay\"].as<uint32_t>();\n   }\n   if (options.count(\"elasticsearch-bulk-sync\")) {\n      my->_elasticsearch_bulk_sync = options[\"elasticsearch-bulk-sync\"].as<uint32_t>();\n   }\n   if (options.count(\"elasticsearch-visitor\")) {\n      my->_elasticsearch_visitor = options[\"elasticsearch-visitor\"].as<bool>();\n   }\n   if (options.count(\"elasticsearch-basic-auth\")) {\n      my->_elasticsearch_basic_auth = options[\"elasticsearch-basic-auth\"].as<std::string>();\n   }\n   if (options.count(\"elasticsearch-index-prefix\")) {\n      my->_elasticsearch_index_prefix = options[\"elasticsearch-index-prefix\"].as<std::string>();\n   }\n   if (options.count(\"elasticsearch-operation-object\")) {\n      my->_elasticsearch_operation_object = options[\"elasticsearch-operation-object\"].as<bool>();\n   }\n   if (options.count(\"elasticsearch-start-es-after-block\")) {\n      my->_elasticsearch_start_es_after_block = options[\"elasticsearch-start-es-after-block\"].as<uint32_t>();\n   }\n   if (options.count(\"elasticsearch-operation-string\")) {\n      my->_elasticsearch_operation_string = options[\"elasticsearch-operation-string\"].as<bool>();\n   }\n   if (options.count(\"elasticsearch-mode\")) {\n      const auto option_number = options[\"elasticsearch-mode\"].as<uint16_t>();\n      if(option_number > mode::all)\n         FC_THROW_EXCEPTION(graphene::chain::plugin_exception, \"Elasticsearch mode not valid\");\n      my->_elasticsearch_mode = static_cast<mode>(options[\"elasticsearch-mode\"].as<uint16_t>());\n   }\n\n   if(my->_elasticsearch_mode != mode::only_query) {\n      if (my->_elasticsearch_mode == mode::all && !my->_elasticsearch_operation_string)\n         FC_THROW_EXCEPTION(graphene::chain::plugin_exception,\n               \"If elasticsearch-mode is set to all then elasticsearch-operation-string need to be true\");\n\n      database().applied_block.connect([this](const signed_block &b) {\n         if (!my->update_account_histories(b))\n            FC_THROW_EXCEPTION(graphene::chain::plugin_exception,\n                  \"Error populating ES database, we are going to keep trying.\");\n      });\n   }\n}\n\nvoid elasticsearch_plugin::plugin_startup()\n{\n   graphene::utilities::ES es;\n   es.curl = my->curl;\n   es.elasticsearch_url = my->_elasticsearch_node_url;\n   es.auth = my->_elasticsearch_basic_auth;\n\n   if(!graphene::utilities::checkES(es))\n      FC_THROW_EXCEPTION(fc::exception, \"ES database is not up in url ${url}\", (\"url\", my->_elasticsearch_node_url));\n   ilog(\"elasticsearch ACCOUNT HISTORY: plugin_startup() begin\");\n}\n\noperation_history_object elasticsearch_plugin::get_operation_by_id(operation_history_id_type id)\n{\n   const string operation_id_string = std::string(object_id_type(id));\n\n   const string query = R\"(\n   {\n      \"query\": {\n         \"match\":\n         {\n            \"account_history.operation_id\": )\" + operation_id_string + R\"(\"\n         }\n      }\n   }\n   )\";\n\n   auto es = prepareHistoryQuery(query);\n   const auto response = graphene::utilities::simpleQuery(es);\n   variant variant_response = fc::json::from_string(response);\n   const auto source = variant_response[\"hits\"][\"hits\"][size_t(0)][\"_source\"];\n   return fromEStoOperation(source);\n}\n\nvector<operation_history_object> elasticsearch_plugin::get_account_history(\n      const account_id_type account_id,\n      operation_history_id_type stop = operation_history_id_type(),\n      unsigned limit = 100,\n      operation_history_id_type start = operation_history_id_type())\n{\n   const string account_id_string = std::string(object_id_type(account_id));\n\n   const auto stop_number = stop.instance.value;\n   const auto start_number = start.instance.value;\n\n   string range = \"\";\n   if(stop_number == 0)\n      range = \" AND operation_id_num: [\"+fc::to_string(stop_number)+\" TO \"+fc::to_string(start_number)+\"]\";\n   else if(stop_number > 0)\n      range = \" AND operation_id_num: {\"+fc::to_string(stop_number)+\" TO \"+fc::to_string(start_number)+\"]\";\n\n   const string query = R\"(\n   {\n      \"size\": )\" + fc::to_string(limit) + R\"(,\n      \"sort\" : [{ \"operation_id_num\" : {\"order\" : \"desc\"}}],\n      \"query\": {\n         \"bool\": {\n            \"must\": [\n            {\n               \"query_string\": {\n                  \"query\": \"account_history.account: )\" + account_id_string +  range + R\"(\"\n               }\n            }\n            ]\n         }\n      }\n   }\n   )\";\n\n   auto es = prepareHistoryQuery(query);\n\n   vector<operation_history_object> result;\n\n   if(!graphene::utilities::checkES(es))\n      return result;\n\n   const auto response = graphene::utilities::simpleQuery(es);\n   variant variant_response = fc::json::from_string(response);\n   \n   const auto hits = variant_response[\"hits\"][\"total\"];\n   const auto size = std::min(static_cast<uint32_t>(hits.as_uint64()), limit);\n\n   for(unsigned i=0; i<size; i++)\n   {\n      const auto source = variant_response[\"hits\"][\"hits\"][size_t(i)][\"_source\"];\n      result.push_back(fromEStoOperation(source));\n   }\n   return result;\n}\n\noperation_history_object elasticsearch_plugin::fromEStoOperation(variant source)\n{\n   operation_history_object result;\n\n   const auto operation_id = source[\"account_history\"][\"operation_id\"];\n   fc::from_variant( operation_id, result.id, GRAPHENE_MAX_NESTED_OBJECTS );\n\n   const auto op = fc::json::from_string(source[\"operation_history\"][\"op\"].as_string());\n   fc::from_variant( op, result.op, GRAPHENE_MAX_NESTED_OBJECTS );\n\n   const auto operation_result = fc::json::from_string(source[\"operation_history\"][\"operation_result\"].as_string());\n   fc::from_variant( operation_result, result.result, GRAPHENE_MAX_NESTED_OBJECTS );\n\n   result.block_num = source[\"block_data\"][\"block_num\"].as_uint64();\n   result.trx_in_block = source[\"operation_history\"][\"trx_in_block\"].as_uint64();\n   result.op_in_trx = source[\"operation_history\"][\"op_in_trx\"].as_uint64();\n   result.trx_in_block = source[\"operation_history\"][\"virtual_op\"].as_uint64();\n\n   return result;\n}\n\ngraphene::utilities::ES elasticsearch_plugin::prepareHistoryQuery(string query)\n{\n   CURL *curl;\n   curl = curl_easy_init();\n\n   graphene::utilities::ES es;\n   es.curl = curl;\n   es.elasticsearch_url = my->_elasticsearch_node_url;\n   es.index_prefix = my->_elasticsearch_index_prefix;\n   es.endpoint = es.index_prefix + \"*/data/_search\";\n   es.query = query;\n\n   return es;\n}\n\nmode elasticsearch_plugin::get_running_mode()\n{\n   return my->_elasticsearch_mode;\n}\n\n\n} }\n"
  },
  {
    "path": "libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/utilities/elasticsearch.hpp>\n\nnamespace graphene { namespace elasticsearch {\n   using namespace chain;\n\n//\n// Plugins should #define their SPACE_ID's so plugins with\n// conflicting SPACE_ID assignments can be compiled into the\n// same binary (by simply re-assigning some of the conflicting #defined\n// SPACE_ID's in a build script).\n//\n// Assignment of SPACE_ID's cannot be done at run-time because\n// various template automagic depends on them being known at compile\n// time.\n//\n#ifndef ELASTICSEARCH_SPACE_ID\n#define ELASTICSEARCH_SPACE_ID 6\n#endif\n\nnamespace detail\n{\n    class elasticsearch_plugin_impl;\n}\n\nenum mode { only_save = 0 , only_query = 1, all = 2 };\n\nclass elasticsearch_plugin : public graphene::app::plugin\n{\n   public:\n      elasticsearch_plugin();\n      virtual ~elasticsearch_plugin();\n\n      std::string plugin_name()const override;\n      std::string plugin_description()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      operation_history_object get_operation_by_id(operation_history_id_type id);\n      vector<operation_history_object> get_account_history(const account_id_type account_id,\n            operation_history_id_type stop, unsigned limit, operation_history_id_type start);\n      mode get_running_mode();\n\n      friend class detail::elasticsearch_plugin_impl;\n      std::unique_ptr<detail::elasticsearch_plugin_impl> my;\n\n   private:\n      operation_history_object fromEStoOperation(variant source);\n      graphene::utilities::ES prepareHistoryQuery(string query);\n};\n\n\nstruct operation_visitor\n{\n   typedef void result_type;\n\n   share_type fee_amount;\n   asset_id_type fee_asset;\n\n   asset_id_type transfer_asset_id;\n   share_type transfer_amount;\n   account_id_type transfer_from;\n   account_id_type transfer_to;\n\n   void operator()( const graphene::chain::transfer_operation& o )\n   {\n      fee_asset = o.fee.asset_id;\n      fee_amount = o.fee.amount;\n\n      transfer_asset_id = o.amount.asset_id;\n      transfer_amount = o.amount.amount;\n      transfer_from = o.from;\n      transfer_to = o.to;\n   }\n\n   object_id_type      fill_order_id;\n   account_id_type     fill_account_id;\n   asset_id_type       fill_pays_asset_id;\n   share_type          fill_pays_amount;\n   asset_id_type       fill_receives_asset_id;\n   share_type          fill_receives_amount;\n   double              fill_fill_price;\n   bool                fill_is_maker;\n\n   void operator()( const graphene::chain::fill_order_operation& o )\n   {\n      fee_asset = o.fee.asset_id;\n      fee_amount = o.fee.amount;\n\n      fill_order_id = o.order_id;\n      fill_account_id = o.account_id;\n      fill_pays_asset_id = o.pays.asset_id;\n      fill_pays_amount = o.pays.amount;\n      fill_receives_asset_id = o.receives.asset_id;\n      fill_receives_amount = o.receives.amount;\n      fill_fill_price = o.fill_price.to_real();\n      fill_is_maker = o.is_maker;\n   }\n\n   template<typename T>\n   void operator()( const T& o )\n   {\n      fee_asset = o.fee.asset_id;\n      fee_amount = o.fee.amount;\n   }\n};\n\nstruct operation_history_struct {\n   int trx_in_block;\n   int op_in_trx;\n   std::string operation_result;\n   int virtual_op;\n   std::string op;\n   variant op_object;\n};\n\nstruct block_struct {\n   int block_num;\n   fc::time_point_sec block_time;\n   std::string trx_id;\n};\n\nstruct fee_struct {\n   asset_id_type asset;\n   std::string asset_name;\n   share_type amount;\n   double amount_units;\n};\n\nstruct transfer_struct {\n   asset_id_type asset;\n   std::string asset_name;\n   share_type amount;\n   double amount_units;\n   account_id_type from;\n   account_id_type to;\n};\n\nstruct fill_struct {\n   object_id_type order_id;\n   account_id_type account_id;\n   asset_id_type pays_asset_id;\n   std::string pays_asset_name;\n   share_type pays_amount;\n   double pays_amount_units;\n   asset_id_type receives_asset_id;\n   std::string receives_asset_name;\n   share_type receives_amount;\n   double receives_amount_units;\n   double fill_price;\n   double fill_price_units;\n   bool is_maker;\n};\n\nstruct visitor_struct {\n   fee_struct fee_data;\n   transfer_struct transfer_data;\n   fill_struct fill_data;\n};\n\nstruct bulk_struct {\n   account_transaction_history_object account_history;\n   operation_history_struct operation_history;\n   int operation_type;\n   int operation_id_num;\n   block_struct block_data;\n   optional<visitor_struct> additional_data;\n};\n\nstruct adaptor_struct {\n   variant adapt(const variant_object& op)\n   {\n      fc::mutable_variant_object o(op);\n      vector<string> keys_to_rename;\n      for (auto i = o.begin(); i != o.end(); ++i)\n      {\n         auto& element = (*i).value();\n         if (element.is_object())\n         {\n            const string& name = (*i).key();\n            auto& vo = element.get_object();\n            if (vo.contains(name.c_str()))\n               keys_to_rename.emplace_back(name);\n            element = adapt(vo);\n         }\n         else if (element.is_array())\n            adapt(element.get_array());\n      }\n      for (const auto& i : keys_to_rename)\n      {\n         string new_name = i + \"_\";\n         o[new_name] = variant(o[i]);\n         o.erase(i);\n      }\n\n      if (o.find(\"memo\") != o.end())\n      {\n         auto& memo = o[\"memo\"];\n         if (memo.is_string())\n         {\n            o[\"memo_\"] = o[\"memo\"];\n            o.erase(\"memo\");\n         }\n         else if (memo.is_object())\n         {\n            fc::mutable_variant_object tmp(memo.get_object());\n            if (tmp.find(\"nonce\") != tmp.end())\n            {\n               tmp[\"nonce\"] = tmp[\"nonce\"].as_string();\n               o[\"memo\"] = tmp;\n            }\n         }\n      }\n      if (o.find(\"new_parameters\") != o.end())\n      {\n         auto& tmp = o[\"new_parameters\"];\n         if (tmp.is_object())\n         {\n            fc::mutable_variant_object tmp2(tmp.get_object());\n            if (tmp2.find(\"current_fees\") != tmp2.end())\n            {\n               tmp2.erase(\"current_fees\");\n               o[\"new_parameters\"] = tmp2;\n            }\n         }\n      }\n      if (o.find(\"owner\") != o.end() && o[\"owner\"].is_string())\n      {\n         o[\"owner_\"] = o[\"owner\"].as_string();\n         o.erase(\"owner\");\n      }\n      if (o.find(\"proposed_ops\") != o.end())\n      {\n         o[\"proposed_ops\"] = fc::json::to_string(o[\"proposed_ops\"]);\n      }\n      if (o.find(\"initializer\") != o.end())\n      {\n         o[\"initializer\"] = fc::json::to_string(o[\"initializer\"]);\n      }\n      if (o.find(\"policy\") != o.end())\n      {\n         o[\"policy\"] = fc::json::to_string(o[\"policy\"]);\n      }\n      if (o.find(\"predicates\") != o.end())\n      {\n         o[\"predicates\"] = fc::json::to_string(o[\"predicates\"]);\n      }\n      if (o.find(\"active_special_authority\") != o.end())\n      {\n         o[\"active_special_authority\"] = fc::json::to_string(o[\"active_special_authority\"]);\n      }\n      if (o.find(\"owner_special_authority\") != o.end())\n      {\n         o[\"owner_special_authority\"] = fc::json::to_string(o[\"owner_special_authority\"]);\n      }\n\n\n      variant v;\n      fc::to_variant(o, v, FC_PACK_MAX_DEPTH);\n      return v;\n   }\n\n   void adapt(fc::variants& v)\n   {\n      for (auto& array_element : v)\n      {\n         if (array_element.is_object())\n            array_element = adapt(array_element.get_object());\n         else if (array_element.is_array())\n            adapt(array_element.get_array());\n         else\n            array_element = array_element.as_string();\n      }\n   }\n};\n\n} } //graphene::elasticsearch\n\nFC_REFLECT_ENUM( graphene::elasticsearch::mode, (only_save)(only_query)(all) )\nFC_REFLECT( graphene::elasticsearch::operation_history_struct, (trx_in_block)(op_in_trx)(operation_result)(virtual_op)(op)(op_object) )\nFC_REFLECT( graphene::elasticsearch::block_struct, (block_num)(block_time)(trx_id) )\nFC_REFLECT( graphene::elasticsearch::fee_struct, (asset)(asset_name)(amount)(amount_units) )\nFC_REFLECT( graphene::elasticsearch::transfer_struct, (asset)(asset_name)(amount)(amount_units)(from)(to) )\nFC_REFLECT( graphene::elasticsearch::fill_struct, (order_id)(account_id)(pays_asset_id)(pays_asset_name)(pays_amount)(pays_amount_units)\n                                                  (receives_asset_id)(receives_asset_name)(receives_amount)(receives_amount_units)(fill_price)\n                                                  (fill_price_units)(is_maker))\nFC_REFLECT( graphene::elasticsearch::visitor_struct, (fee_data)(transfer_data)(fill_data) )\nFC_REFLECT( graphene::elasticsearch::bulk_struct, (account_history)(operation_history)(operation_type)(operation_id_num)(block_data)(additional_data) )\n"
  },
  {
    "path": "libraries/plugins/es_objects/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/es_objects/*.hpp\")\n\nadd_library( graphene_es_objects\n        es_objects.cpp\n           )\n\nfind_curl()\n\ninclude_directories(${CURL_INCLUDE_DIRS})\nif(CURL_STATICLIB)\n  SET_TARGET_PROPERTIES(graphene_es_objects PROPERTIES\n\tCOMPILE_DEFINITIONS \"CURL_STATICLIB\")\nendif(CURL_STATICLIB)\nif(MSVC)\n  set_source_files_properties(es_objects.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ntarget_link_libraries( graphene_es_objects graphene_chain graphene_app ${CURL_LIBRARIES} )\ntarget_include_directories( graphene_es_objects\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\n\ninstall( TARGETS\n   graphene_es_objects\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/es_objects\" )\n\n"
  },
  {
    "path": "libraries/plugins/es_objects/es_objects.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/es_objects/es_objects.hpp>\n\n#include <curl/curl.h>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/account_object.hpp>\n\n#include <graphene/utilities/elasticsearch.hpp>\n\nnamespace graphene { namespace es_objects {\n\nnamespace detail\n{\n\nclass es_objects_plugin_impl\n{\n   public:\n      es_objects_plugin_impl(es_objects_plugin& _plugin)\n         : _self( _plugin )\n      {  curl = curl_easy_init(); }\n      virtual ~es_objects_plugin_impl();\n\n      bool index_database(const vector<object_id_type>& ids, std::string action);\n      bool genesis();\n      void remove_from_database(object_id_type id, std::string index);\n\n      es_objects_plugin& _self;\n      std::string _es_objects_elasticsearch_url = \"http://localhost:9200/\";\n      std::string _es_objects_auth = \"\";\n      uint32_t _es_objects_bulk_replay = 10000;\n      uint32_t _es_objects_bulk_sync = 100;\n      bool _es_objects_proposals = true;\n      bool _es_objects_accounts = true;\n      bool _es_objects_assets = true;\n      bool _es_objects_balances = true;\n      bool _es_objects_limit_orders = false;\n      bool _es_objects_asset_bitasset = true;\n      std::string _es_objects_index_prefix = \"objects-\";\n      uint32_t _es_objects_start_es_after_block = 0;\n      CURL *curl; // curl handler\n      vector <std::string> bulk;\n      vector<std::string> prepare;\n\n      bool _es_objects_keep_only_current = true;\n\n      uint32_t block_number;\n      fc::time_point_sec block_time;\n\n   private:\n      template<typename T>\n      void prepareTemplate(T blockchain_object, string index_name);\n};\n\nbool es_objects_plugin_impl::genesis()\n{\n   ilog(\"elasticsearch OBJECTS: inserting data from genesis\");\n\n   graphene::chain::database &db = _self.database();\n\n   block_number = db.head_block_num();\n   block_time = db.head_block_time();\n\n   if (_es_objects_accounts) {\n      auto &index_accounts = db.get_index(1, 2);\n      index_accounts.inspect_all_objects([this, &db](const graphene::db::object &o) {\n         auto obj = db.find_object(o.id);\n         auto a = static_cast<const account_object *>(obj);\n         prepareTemplate<account_object>(*a, \"account\");\n      });\n   }\n   if (_es_objects_assets) {\n      auto &index_assets = db.get_index(1, 3);\n      index_assets.inspect_all_objects([this, &db](const graphene::db::object &o) {\n         auto obj = db.find_object(o.id);\n         auto a = static_cast<const asset_object *>(obj);\n         prepareTemplate<asset_object>(*a, \"asset\");\n      });\n   }\n   if (_es_objects_balances) {\n      auto &index_balances = db.get_index(2, 5);\n      index_balances.inspect_all_objects([this, &db](const graphene::db::object &o) {\n         auto obj = db.find_object(o.id);\n         auto b = static_cast<const account_balance_object *>(obj);\n         prepareTemplate<account_balance_object>(*b, \"balance\");\n      });\n   }\n\n   graphene::utilities::ES es;\n   es.curl = curl;\n   es.bulk_lines = bulk;\n   es.elasticsearch_url = _es_objects_elasticsearch_url;\n   es.auth = _es_objects_auth;\n   if (!graphene::utilities::SendBulk(std::move(es)))\n      FC_THROW_EXCEPTION(graphene::chain::plugin_exception, \"Error inserting genesis data.\");\n   else\n      bulk.clear();\n\n   return true;\n}\n\nbool es_objects_plugin_impl::index_database(const vector<object_id_type>& ids, std::string action)\n{\n   graphene::chain::database &db = _self.database();\n\n   block_time = db.head_block_time();\n   block_number = db.head_block_num();\n\n   if(block_number > _es_objects_start_es_after_block) {\n\n      // check if we are in replay or in sync and change number of bulk documents accordingly\n      uint32_t limit_documents = 0;\n      if ((fc::time_point::now() - block_time) < fc::seconds(30))\n         limit_documents = _es_objects_bulk_sync;\n      else\n         limit_documents = _es_objects_bulk_replay;\n\n\n      for (auto const &value: ids) {\n         if (value.is<proposal_object>() && _es_objects_proposals) {\n            auto obj = db.find_object(value);\n            auto p = static_cast<const proposal_object *>(obj);\n            if (p != nullptr) {\n               if (action == \"delete\")\n                  remove_from_database(p->id, \"proposal\");\n               else\n                  prepareTemplate<proposal_object>(*p, \"proposal\");\n            }\n         } else if (value.is<account_object>() && _es_objects_accounts) {\n            auto obj = db.find_object(value);\n            auto a = static_cast<const account_object *>(obj);\n            if (a != nullptr) {\n               if (action == \"delete\")\n                  remove_from_database(a->id, \"account\");\n               else\n                  prepareTemplate<account_object>(*a, \"account\");\n            }\n         } else if (value.is<asset_object>() && _es_objects_assets) {\n            auto obj = db.find_object(value);\n            auto a = static_cast<const asset_object *>(obj);\n            if (a != nullptr) {\n               if (action == \"delete\")\n                  remove_from_database(a->id, \"asset\");\n               else\n                  prepareTemplate<asset_object>(*a, \"asset\");\n            }\n         } else if (value.is<account_balance_object>() && _es_objects_balances) {\n            auto obj = db.find_object(value);\n            auto b = static_cast<const account_balance_object *>(obj);\n            if (b != nullptr) {\n               if (action == \"delete\")\n                  remove_from_database(b->id, \"balance\");\n               else\n                  prepareTemplate<account_balance_object>(*b, \"balance\");\n            }\n         } else if (value.is<limit_order_object>() && _es_objects_limit_orders) {\n            auto obj = db.find_object(value);\n            auto l = static_cast<const limit_order_object *>(obj);\n            if (l != nullptr) {\n               if (action == \"delete\")\n                  remove_from_database(l->id, \"limitorder\");\n               else\n                  prepareTemplate<limit_order_object>(*l, \"limitorder\");\n            }\n         } else if (value.is<asset_bitasset_data_object>() && _es_objects_asset_bitasset) {\n            auto obj = db.find_object(value);\n            auto ba = static_cast<const asset_bitasset_data_object *>(obj);\n            if (ba != nullptr) {\n               if (action == \"delete\")\n                  remove_from_database(ba->id, \"bitasset\");\n               else\n                  prepareTemplate<asset_bitasset_data_object>(*ba, \"bitasset\");\n            }\n         }\n      }\n\n      if (curl && bulk.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech\n\n         graphene::utilities::ES es;\n         es.curl = curl;\n         es.bulk_lines = bulk;\n         es.elasticsearch_url = _es_objects_elasticsearch_url;\n         es.auth = _es_objects_auth;\n\n         if (!graphene::utilities::SendBulk(std::move(es)))\n            return false;\n         else\n            bulk.clear();\n      }\n   }\n\n   return true;\n}\n\nvoid es_objects_plugin_impl::remove_from_database( object_id_type id, std::string index)\n{\n   if(_es_objects_keep_only_current)\n   {\n      fc::mutable_variant_object delete_line;\n      delete_line[\"_id\"] = string(id);\n      delete_line[\"_index\"] = _es_objects_index_prefix + index;\n      delete_line[\"_type\"] = \"data\";\n      fc::mutable_variant_object final_delete_line;\n      final_delete_line[\"delete\"] = delete_line;\n      prepare.push_back(fc::json::to_string(final_delete_line));\n      std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk));\n      prepare.clear();\n   }\n}\n\ntemplate<typename T>\nvoid es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_name)\n{\n   fc::mutable_variant_object bulk_header;\n   bulk_header[\"_index\"] = _es_objects_index_prefix + index_name;\n   bulk_header[\"_type\"] = \"data\";\n   if(_es_objects_keep_only_current)\n   {\n      bulk_header[\"_id\"] = string(blockchain_object.id);\n   }\n\n   adaptor_struct adaptor;\n   fc::variant blockchain_object_variant;\n   fc::to_variant( blockchain_object, blockchain_object_variant, GRAPHENE_NET_MAX_NESTED_OBJECTS );\n   fc::mutable_variant_object o = adaptor.adapt(blockchain_object_variant.get_object());\n\n   o[\"object_id\"] = string(blockchain_object.id);\n   o[\"block_time\"] = block_time;\n   o[\"block_number\"] = block_number;\n\n   string data = fc::json::to_string(o, fc::json::legacy_generator);\n\n   prepare = graphene::utilities::createBulk(bulk_header, std::move(data));\n   std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk));\n   prepare.clear();\n}\n\nes_objects_plugin_impl::~es_objects_plugin_impl()\n{\n   if (curl) {\n      curl_easy_cleanup(curl);\n      curl = nullptr;\n   }\n   return;\n}\n\n} // end namespace detail\n\nes_objects_plugin::es_objects_plugin() :\n   my( new detail::es_objects_plugin_impl(*this) )\n{\n}\n\nes_objects_plugin::~es_objects_plugin()\n{\n}\n\nstd::string es_objects_plugin::plugin_name()const\n{\n   return \"es_objects\";\n}\nstd::string es_objects_plugin::plugin_description()const\n{\n   return \"Stores blockchain objects in ES database. Experimental.\";\n}\n\nvoid es_objects_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"es-objects-elasticsearch-url\", boost::program_options::value<std::string>(),\n               \"Elasticsearch node url(http://localhost:9200/)\")\n         (\"es-objects-auth\", boost::program_options::value<std::string>(), \"Basic auth username:password('')\")\n         (\"es-objects-bulk-replay\", boost::program_options::value<uint32_t>(),\n               \"Number of bulk documents to index on replay(10000)\")\n         (\"es-objects-bulk-sync\", boost::program_options::value<uint32_t>(),\n               \"Number of bulk documents to index on a synchronized chain(100)\")\n         (\"es-objects-proposals\", boost::program_options::value<bool>(), \"Store proposal objects(true)\")\n         (\"es-objects-accounts\", boost::program_options::value<bool>(), \"Store account objects(true)\")\n         (\"es-objects-assets\", boost::program_options::value<bool>(), \"Store asset objects(true)\")\n         (\"es-objects-balances\", boost::program_options::value<bool>(), \"Store balances objects(true)\")\n         (\"es-objects-limit-orders\", boost::program_options::value<bool>(), \"Store limit order objects(false)\")\n         (\"es-objects-asset-bitasset\", boost::program_options::value<bool>(), \"Store feed data(true)\")\n         (\"es-objects-index-prefix\", boost::program_options::value<std::string>(),\n               \"Add a prefix to the index(objects-)\")\n         (\"es-objects-keep-only-current\", boost::program_options::value<bool>(),\n               \"Keep only current state of the objects(true)\")\n         (\"es-objects-start-es-after-block\", boost::program_options::value<uint32_t>(),\n               \"Start doing ES job after block(0)\")\n         ;\n   cfg.add(cli);\n}\n\nvoid es_objects_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{\n   if (options.count(\"es-objects-elasticsearch-url\")) {\n      my->_es_objects_elasticsearch_url = options[\"es-objects-elasticsearch-url\"].as<std::string>();\n   }\n   if (options.count(\"es-objects-auth\")) {\n      my->_es_objects_auth = options[\"es-objects-auth\"].as<std::string>();\n   }\n   if (options.count(\"es-objects-bulk-replay\")) {\n      my->_es_objects_bulk_replay = options[\"es-objects-bulk-replay\"].as<uint32_t>();\n   }\n   if (options.count(\"es-objects-bulk-sync\")) {\n      my->_es_objects_bulk_sync = options[\"es-objects-bulk-sync\"].as<uint32_t>();\n   }\n   if (options.count(\"es-objects-proposals\")) {\n      my->_es_objects_proposals = options[\"es-objects-proposals\"].as<bool>();\n   }\n   if (options.count(\"es-objects-accounts\")) {\n      my->_es_objects_accounts = options[\"es-objects-accounts\"].as<bool>();\n   }\n   if (options.count(\"es-objects-assets\")) {\n      my->_es_objects_assets = options[\"es-objects-assets\"].as<bool>();\n   }\n   if (options.count(\"es-objects-balances\")) {\n      my->_es_objects_balances = options[\"es-objects-balances\"].as<bool>();\n   }\n   if (options.count(\"es-objects-limit-orders\")) {\n      my->_es_objects_limit_orders = options[\"es-objects-limit-orders\"].as<bool>();\n   }\n   if (options.count(\"es-objects-asset-bitasset\")) {\n      my->_es_objects_asset_bitasset = options[\"es-objects-asset-bitasset\"].as<bool>();\n   }\n   if (options.count(\"es-objects-index-prefix\")) {\n      my->_es_objects_index_prefix = options[\"es-objects-index-prefix\"].as<std::string>();\n   }\n   if (options.count(\"es-objects-keep-only-current\")) {\n      my->_es_objects_keep_only_current = options[\"es-objects-keep-only-current\"].as<bool>();\n   }\n   if (options.count(\"es-objects-start-es-after-block\")) {\n      my->_es_objects_start_es_after_block = options[\"es-objects-start-es-after-block\"].as<uint32_t>();\n   }\n\n   database().applied_block.connect([this](const signed_block &b) {\n      if(b.block_num() == 1 && my->_es_objects_start_es_after_block == 0) {\n         if (!my->genesis())\n            FC_THROW_EXCEPTION(graphene::chain::plugin_exception, \"Error populating genesis data.\");\n      }\n   });\n   database().new_objects.connect([this]( const vector<object_id_type>& ids,\n         const flat_set<account_id_type>& impacted_accounts ) {\n      if(!my->index_database(ids, \"create\"))\n      {\n         FC_THROW_EXCEPTION(graphene::chain::plugin_exception,\n               \"Error creating object from ES database, we are going to keep trying.\");\n      }\n   });\n   database().changed_objects.connect([this]( const vector<object_id_type>& ids,\n         const flat_set<account_id_type>& impacted_accounts ) {\n      if(!my->index_database(ids, \"update\"))\n      {\n         FC_THROW_EXCEPTION(graphene::chain::plugin_exception,\n               \"Error updating object from ES database, we are going to keep trying.\");\n      }\n   });\n   database().removed_objects.connect([this](const vector<object_id_type>& ids,\n         const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts) {\n      if(!my->index_database(ids, \"delete\"))\n      {\n         FC_THROW_EXCEPTION(graphene::chain::plugin_exception,\n               \"Error deleting object from ES database, we are going to keep trying.\");\n      }\n   });\n}\n\nvoid es_objects_plugin::plugin_startup()\n{\n   graphene::utilities::ES es;\n   es.curl = my->curl;\n   es.elasticsearch_url = my->_es_objects_elasticsearch_url;\n   es.auth = my->_es_objects_auth;\n   es.auth = my->_es_objects_index_prefix;\n\n   if(!graphene::utilities::checkES(es))\n      FC_THROW_EXCEPTION(fc::exception, \"ES database is not up in url ${url}\", (\"url\", my->_es_objects_elasticsearch_url));\n   ilog(\"elasticsearch OBJECTS: plugin_startup() begin\");\n}\n\n} }"
  },
  {
    "path": "libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace es_objects {\n\nusing namespace chain;\n\nnamespace detail\n{\n    class es_objects_plugin_impl;\n}\n\nclass es_objects_plugin : public graphene::app::plugin\n{\n   public:\n      es_objects_plugin();\n      virtual ~es_objects_plugin();\n\n      std::string plugin_name()const override;\n      std::string plugin_description()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      friend class detail::es_objects_plugin_impl;\n      std::unique_ptr<detail::es_objects_plugin_impl> my;\n};\n\nstruct adaptor_struct {\n    fc::mutable_variant_object adapt(const variant_object &obj) {\n      fc::mutable_variant_object o(obj);\n      vector<string> keys_to_rename;\n      for (auto i = o.begin(); i != o.end(); ++i) {\n         auto &element = (*i).value();\n         if (element.is_object()) {\n            const string &name = (*i).key();\n            auto &vo = element.get_object();\n            if (vo.contains(name.c_str()))\n               keys_to_rename.emplace_back(name);\n            element = adapt(vo);\n         } else if (element.is_array())\n            adapt(element.get_array());\n      }\n      for (const auto &i : keys_to_rename) {\n         string new_name = i + \"_\";\n         o[new_name] = variant(o[i]);\n         o.erase(i);\n      }\n      if (o.find(\"owner\") != o.end() && o[\"owner\"].is_string())\n      {\n         o[\"owner_\"] = o[\"owner\"].as_string();\n         o.erase(\"owner\");\n      }\n      if (o.find(\"active_special_authority\") != o.end())\n      {\n         o[\"active_special_authority\"] = fc::json::to_string(o[\"active_special_authority\"]);\n      }\n      if (o.find(\"owner_special_authority\") != o.end())\n      {\n         o[\"owner_special_authority\"] = fc::json::to_string(o[\"owner_special_authority\"]);\n      }\n      if (o.find(\"feeds\") != o.end())\n      {\n         o[\"feeds\"] = fc::json::to_string(o[\"feeds\"]);\n      }\n      if (o.find(\"operations\") != o.end())\n      {\n         o[\"operations\"] = fc::json::to_string(o[\"operations\"]);\n      }\n      \n      return o;\n   }\n\n   void adapt(fc::variants &v) {\n      for (auto &array_element : v) {\n         if (array_element.is_object())\n            array_element = adapt(array_element.get_object());\n         else if (array_element.is_array())\n            adapt(array_element.get_array());\n         else\n            array_element = array_element.as_string();\n      }\n   }\n};\n\n} } //graphene::es_objects\n"
  },
  {
    "path": "libraries/plugins/grouped_orders/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/grouped_orders/*.hpp\")\n\nadd_library( graphene_grouped_orders\n             grouped_orders_plugin.cpp\n           )\n\ntarget_link_libraries( graphene_grouped_orders graphene_chain graphene_app )\ntarget_include_directories( graphene_grouped_orders\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties( grouped_orders_plugin.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_grouped_orders\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/grouped_orders\" )\n\n"
  },
  {
    "path": "libraries/plugins/grouped_orders/grouped_orders_plugin.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/grouped_orders/grouped_orders_plugin.hpp>\n\n#include <graphene/chain/market_object.hpp>\n\nnamespace graphene { namespace grouped_orders {\n\nnamespace detail\n{\n\nclass grouped_orders_plugin_impl\n{\n   public:\n      grouped_orders_plugin_impl(grouped_orders_plugin& _plugin)\n      :_self( _plugin ) {}\n      virtual ~grouped_orders_plugin_impl();\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      grouped_orders_plugin&     _self;\n      flat_set<uint16_t>         _tracked_groups;\n};\n\n/**\n *  @brief This secondary index is used to track changes on limit order objects.\n */\nclass limit_order_group_index : public secondary_index\n{\n   public:\n      limit_order_group_index( const flat_set<uint16_t>& groups ) : _tracked_groups( groups ) {};\n\n      virtual void object_inserted( const object& obj ) override;\n      virtual void object_removed( const object& obj ) override;\n      virtual void about_to_modify( const object& before ) override;\n      virtual void object_modified( const object& after  ) override;\n\n      const flat_set<uint16_t>& get_tracked_groups() const\n      { return _tracked_groups; }\n\n      const map< limit_order_group_key, limit_order_group_data >& get_order_groups() const\n      { return _og_data; }\n\n   private:\n      void remove_order( const limit_order_object& obj, bool remove_empty = true );\n\n      /** tracked groups */\n      flat_set<uint16_t> _tracked_groups;\n\n      /** maps the group key to group data */\n      map< limit_order_group_key, limit_order_group_data > _og_data;\n};\n\nvoid limit_order_group_index::object_inserted( const object& objct )\n{ try {\n   const limit_order_object& o = static_cast<const limit_order_object&>( objct );\n\n   auto& idx = _og_data;\n\n   for( uint16_t group : get_tracked_groups() )\n   {\n      auto create_ogo = [&]() {\n         idx[ limit_order_group_key( group, o.sell_price ) ] = limit_order_group_data( o.sell_price, o.for_sale );\n      };\n      // if idx is empty, insert this order\n      // Note: not capped\n      if( idx.empty() )\n      {\n         create_ogo();\n         continue;\n      }\n\n      // cap the price\n      price capped_price = o.sell_price;\n      price max = o.sell_price.max();\n      price min = o.sell_price.min();\n      bool capped_max = false;\n      bool capped_min = false;\n      if( o.sell_price > max )\n      {\n         capped_price = max;\n         capped_max = true;\n      }\n      else if( o.sell_price < min )\n      {\n         capped_price = min;\n         capped_min = true;\n      }\n      // if idx is not empty, find the group that is next to this order\n      auto itr = idx.lower_bound( limit_order_group_key( group, capped_price ) );\n      bool check_previous = false;\n      if( itr == idx.end() || itr->first.group != group\n            || itr->first.min_price.base.asset_id != o.sell_price.base.asset_id\n            || itr->first.min_price.quote.asset_id != o.sell_price.quote.asset_id )\n         // not same market or group type\n         check_previous = true;\n      else // same market and group type\n      {\n         bool update_max = false;\n         if( capped_price > itr->second.max_price ) // implies itr->min_price <= itr->max_price < max\n         {\n            update_max = true;\n            price max_price = itr->first.min_price * ratio_type( GRAPHENE_100_PERCENT + group, GRAPHENE_100_PERCENT );\n            // max_price should have been capped here\n            if( capped_price > max_price ) // new order is out of range\n               check_previous = true;\n         }\n         if( !check_previous ) // new order is within the range\n         {\n            if( capped_min && o.sell_price < itr->first.min_price )\n            {  // need to update itr->min_price here, if itr is below min, and new order is even lower\n               // TODO improve performance\n               limit_order_group_data data( itr->second.max_price, o.for_sale + itr->second.total_for_sale );\n               idx.erase( itr );\n               idx[ limit_order_group_key( group, o.sell_price ) ] = data;\n            }\n            else\n            {\n               if( update_max || ( capped_max && o.sell_price > itr->second.max_price ) )\n                  itr->second.max_price = o.sell_price; // store real price here, not capped\n               itr->second.total_for_sale += o.for_sale;\n            }\n         }\n      }\n\n      if( check_previous )\n      {\n         if( itr == idx.begin() ) // no previous\n            create_ogo();\n         else\n         {\n            --itr; // should be valid\n            if( itr->first.group != group || itr->first.min_price.base.asset_id != o.sell_price.base.asset_id\n                                          || itr->first.min_price.quote.asset_id != o.sell_price.quote.asset_id )\n               // not same market or group type\n               create_ogo();\n            else // same market and group type\n            {\n               // due to lower_bound, always true: capped_price < itr->first.min_price, so no need to check again,\n               // if new order is in range of itr group, always need to update itr->first.min_price, unless\n               //   o.sell_price is higher than max\n               price min_price = itr->second.max_price / ratio_type( GRAPHENE_100_PERCENT + group, GRAPHENE_100_PERCENT );\n               // min_price should have been capped here\n               if( capped_price < min_price ) // new order is out of range\n                  create_ogo();\n               else if( capped_max && o.sell_price >= itr->first.min_price )\n               {  // itr is above max, and price of new order is even higher\n                  if( o.sell_price > itr->second.max_price )\n                     itr->second.max_price = o.sell_price;\n                  itr->second.total_for_sale += o.for_sale;\n               }\n               else\n               {  // new order is within the range\n                  // TODO improve performance\n                  limit_order_group_data data( itr->second.max_price, o.for_sale + itr->second.total_for_sale );\n                  idx.erase( itr );\n                  idx[ limit_order_group_key( group, o.sell_price ) ] = data;\n               }\n            }\n         }\n      }\n   }\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid limit_order_group_index::object_removed( const object& objct )\n{ try {\n   const limit_order_object& o = static_cast<const limit_order_object&>( objct );\n   remove_order( o );\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid limit_order_group_index::about_to_modify( const object& objct )\n{ try {\n   const limit_order_object& o = static_cast<const limit_order_object&>( objct );\n   remove_order( o, false );\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid limit_order_group_index::object_modified( const object& objct )\n{ try {\n   object_inserted( objct );\n} FC_CAPTURE_AND_RETHROW( (objct) ); }\n\nvoid limit_order_group_index::remove_order( const limit_order_object& o, bool remove_empty )\n{\n   auto& idx = _og_data;\n\n   for( uint16_t group : get_tracked_groups() )\n   {\n      // find the group that should contain this order\n      auto itr = idx.lower_bound( limit_order_group_key( group, o.sell_price ) );\n      if( itr == idx.end() || itr->first.group != group\n            || itr->first.min_price.base.asset_id != o.sell_price.base.asset_id\n            || itr->first.min_price.quote.asset_id != o.sell_price.quote.asset_id\n            || itr->second.max_price < o.sell_price )\n      {\n         // can not find corresponding group, should not happen\n         wlog( \"can not find the order group containing order for removing (price dismatch): ${o}\", (\"o\",o) );\n         continue;\n      }\n      else // found\n      {\n         if( itr->second.total_for_sale < o.for_sale )\n            // should not happen\n            wlog( \"can not find the order group containing order for removing (amount dismatch): ${o}\", (\"o\",o) );\n         else if( !remove_empty || itr->second.total_for_sale > o.for_sale )\n            itr->second.total_for_sale -= o.for_sale;\n         else\n            // it's the only order in the group and need to be removed\n            idx.erase( itr );\n      }\n   }\n}\n\ngrouped_orders_plugin_impl::~grouped_orders_plugin_impl()\n{}\n\n} // end namespace detail\n\n\ngrouped_orders_plugin::grouped_orders_plugin() :\n   my( new detail::grouped_orders_plugin_impl(*this) )\n{\n}\n\ngrouped_orders_plugin::~grouped_orders_plugin()\n{\n}\n\nstd::string grouped_orders_plugin::plugin_name()const\n{\n   return \"grouped_orders\";\n}\n\nvoid grouped_orders_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"tracked-groups\", boost::program_options::value<string>()->default_value(\"[10,100]\"), // 0.1% and 1%\n          \"Group orders by percentage increase on price. Specify a JSON array of numbers here, each number is a group, number 1 means 0.01%. \")\n         ;\n   cfg.add(cli);\n}\n\nvoid grouped_orders_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{ try {\n\n   if( options.count( \"tracked-groups\" ) )\n   {\n      const std::string& groups = options[\"tracked-groups\"].as<string>();\n      my->_tracked_groups = fc::json::from_string(groups).as<flat_set<uint16_t>>( 2 );\n      my->_tracked_groups.erase( 0 );\n   }\n   else\n      my->_tracked_groups = fc::json::from_string(\"[10,100]\").as<flat_set<uint16_t>>(2);\n\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid grouped_orders_plugin::plugin_startup()\n{\n   auto& groups = *database().add_secondary_index< primary_index<limit_order_index>,\n                                                   detail::limit_order_group_index >( my->_tracked_groups );\n   for( const auto& order : database().get_index_type< limit_order_index >().indices() )\n      groups.object_inserted( order );\n}\n\nconst flat_set<uint16_t>& grouped_orders_plugin::tracked_groups() const\n{\n   return my->_tracked_groups;\n}\n\nconst map< limit_order_group_key, limit_order_group_data >& grouped_orders_plugin::limit_order_groups()\n{\n   const auto& idx = database().get_index_type< limit_order_index >();\n   const auto& pidx = dynamic_cast<const primary_index< limit_order_index >&>(idx);\n   const auto& logidx = pidx.get_secondary_index< detail::limit_order_group_index >();\n   return logidx.get_order_groups();\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/grouped_orders/include/graphene/grouped_orders/grouped_orders_plugin.hpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace grouped_orders {\nusing namespace chain;\n\nstruct limit_order_group_key\n{\n   limit_order_group_key( const uint16_t g, const price& p ) : group(g), min_price(p) {}\n   limit_order_group_key() {}\n\n   uint16_t      group = 0; ///< percentage, 1 means 1 / 10000\n   price         min_price;\n\n   friend bool operator < ( const limit_order_group_key& a, const limit_order_group_key& b )\n   {\n      // price is ordered descendingly, same as limit_order_index\n      return std::tie( a.group, b.min_price ) < std::tie( b.group, a.min_price );\n   }\n   friend bool operator == ( const limit_order_group_key& a, const limit_order_group_key& b )\n   {\n      return std::tie( a.group, a.min_price ) == std::tie( b.group, b.min_price );\n   }\n};\n\nstruct limit_order_group_data\n{\n   limit_order_group_data( const price& p, const share_type s ) : max_price(p), total_for_sale(s) {}\n   limit_order_group_data() {}\n\n   price         max_price;\n   share_type    total_for_sale; ///< asset id is min_price.base.asset_id\n};\n\nnamespace detail\n{\n    class grouped_orders_plugin_impl;\n}\n\n/**\n *  The grouped orders plugin can be configured to track any number of price diff percentages via its configuration.\n *  Every time when there is a change on an order in object database, it will update internal state to reflect the change.\n */\nclass grouped_orders_plugin : public graphene::app::plugin\n{\n   public:\n      grouped_orders_plugin();\n      virtual ~grouped_orders_plugin();\n\n      std::string plugin_name()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(\n         const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      const flat_set<uint16_t>&   tracked_groups()const;\n\n      const map< limit_order_group_key, limit_order_group_data >& limit_order_groups();\n\n   private:\n      friend class detail::grouped_orders_plugin_impl;\n      std::unique_ptr<detail::grouped_orders_plugin_impl> my;\n};\n\n} } //graphene::grouped_orders\n\nFC_REFLECT( graphene::grouped_orders::limit_order_group_key, (group)(min_price) )\nFC_REFLECT( graphene::grouped_orders::limit_order_group_data, (max_price)(total_for_sale) )\n"
  },
  {
    "path": "libraries/plugins/make_new_plugin.sh",
    "content": "#!/bin/bash\n\n# Find next available space id by checking all current plugins\nnext_space_id () {\n    SPACE_IDS=()\n    for i in * ; do\n        if [ -d \"$i\" ] && [ \"$i\" != \"CMakeFiles\" ]; then\n            cd \"$i/include/graphene/$i\"\n            result=$(grep -rnw '.' -e '#define[[:space:]]*[[:alnum:]]*_SPACE_ID')\n            B=$(echo $result | cut -d ' ' -f 3)\n            if [[ $B =~ [[:digit:]] ]]; then\n                SPACE_IDS+=($B)\n            fi\n            cd \"../../../..\"\n        fi\n    done\n    max=$( printf \"%d\\n\" \"${SPACE_IDS[@]}\" | sort -n | tail -1 )\n    next=$(($max + 1))\n    return $next\n}\n\n## create new plugin\nif [ $# -ne 1 ]; then\n    echo \"Usage: $0 my_new_plugin\"\n    echo \"... where my_new_plugin is the name of the plugin you want to create\"\n    exit 1\nfi\n\npluginName=\"$1\"\n\necho \"Copying template...\"\ncp -r template_plugin \"$pluginName\"\n\necho \"Renaming files/directories...\"\nmv \"$pluginName/include/graphene/template_plugin\" \"$pluginName/include/graphene/$pluginName\"\nfor file in `find \"$pluginName\" -type f -name '*template_plugin*'`; do mv \"$file\" `sed s/template_plugin/\"$pluginName\"/g <<< $file`; done;\necho \"Renaming in files...\"\nfind \"$pluginName\" -type f -exec sed -i \"s/template_plugin/$pluginName/g\" {} \\;\necho \"Assigning next available SPACE_ID...\"\nnext_space_id\nfind \"$pluginName\" -type f -exec sed -i \"s/@SPACE_ID@/$?/g\" {} \\;\n\necho \"Done! $pluginName is ready.\"\necho \"Next steps:\"\necho \"1- Add 'add_subdirectory( $pluginName )' to CmakeLists.txt in this directory.\"\necho \"2- Add 'graphene_$pluginName' to ../../programs/witness_node/CMakeLists.txt with the other plugins.\"\necho \"3- Include plugin header file '#include <graphene/$pluginName/$pluginName.hpp>' to ../../programs/witness_node/main.cpp.\"\necho \"4- Initialize plugin with the others with 'auto ${pluginName}_plug = node->register_plugin<$pluginName::$pluginName>();' in ../../programs/witness_node/main.cpp\"\necho \"5- cmake and make\"\necho \"6- Start plugin with './../programs/witness_node/witness_node --plugins \\\"$pluginName\\\"'. After the seed nodes are added you start to see see a msgs from the plugin 'onBlock' \"\n"
  },
  {
    "path": "libraries/plugins/market_history/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/market_history/*.hpp\")\n\nadd_library( graphene_market_history \n             market_history_plugin.cpp\n           )\n\ntarget_link_libraries( graphene_market_history graphene_chain graphene_app )\ntarget_include_directories( graphene_market_history\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties( market_history_plugin.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_market_history\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/market_history\" )\n\n"
  },
  {
    "path": "libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <fc/thread/future.hpp>\n#include <fc/uint128.hpp>\n\n#include <boost/multi_index/composite_key.hpp>\n\nnamespace graphene { namespace market_history {\nusing namespace chain;\n\n//\n// Plugins should #define their SPACE_ID's so plugins with\n// conflicting SPACE_ID assignments can be compiled into the\n// same binary (by simply re-assigning some of the conflicting #defined\n// SPACE_ID's in a build script).\n//\n// Assignment of SPACE_ID's cannot be done at run-time because\n// various template automagic depends on them being known at compile\n// time.\n//\n#ifndef MARKET_HISTORY_SPACE_ID\n#define MARKET_HISTORY_SPACE_ID 5\n#endif\n\nenum market_history_object_type\n{\n   order_history_object_type = 0,\n   bucket_object_type = 1,\n   market_ticker_object_type = 2,\n   market_ticker_meta_object_type = 3\n};\n\nstruct bucket_key\n{\n   bucket_key( asset_id_type a, asset_id_type b, uint32_t s, fc::time_point_sec o )\n   :base(a),quote(b),seconds(s),open(o){}\n   bucket_key(){}\n\n   asset_id_type      base;\n   asset_id_type      quote;\n   uint32_t           seconds = 0;\n   fc::time_point_sec open;\n\n   friend bool operator < ( const bucket_key& a, const bucket_key& b )\n   {\n      return std::tie( a.base, a.quote, a.seconds, a.open ) < std::tie( b.base, b.quote, b.seconds, b.open );\n   }\n   friend bool operator == ( const bucket_key& a, const bucket_key& b )\n   {\n      return std::tie( a.base, a.quote, b.seconds, a.open ) == std::tie( b.base, b.quote, b.seconds, b.open );\n   }\n};\n\nstruct bucket_object : public abstract_object<bucket_object>\n{\n   static const uint8_t space_id = MARKET_HISTORY_SPACE_ID;\n   static const uint8_t type_id  = bucket_object_type;\n\n   price high()const { return asset( high_base, key.base ) / asset( high_quote, key.quote ); }\n   price low()const { return asset( low_base, key.base ) / asset( low_quote, key.quote ); }\n\n   bucket_key          key;\n   share_type          high_base;\n   share_type          high_quote;\n   share_type          low_base;\n   share_type          low_quote;\n   share_type          open_base;\n   share_type          open_quote;\n   share_type          close_base;\n   share_type          close_quote;\n   share_type          base_volume;\n   share_type          quote_volume;\n};\n\nstruct history_key {\n  asset_id_type        base;\n  asset_id_type        quote;\n  int64_t              sequence = 0;\n\n  friend bool operator < ( const history_key& a, const history_key& b ) {\n    return std::tie( a.base, a.quote, a.sequence ) < std::tie( b.base, b.quote, b.sequence );\n  }\n  friend bool operator == ( const history_key& a, const history_key& b ) {\n    return std::tie( a.base, a.quote, a.sequence ) == std::tie( b.base, b.quote, b.sequence );\n  }\n};\nstruct order_history_object : public abstract_object<order_history_object>\n{\n   static const uint8_t space_id = MARKET_HISTORY_SPACE_ID;\n   static const uint8_t type_id  = order_history_object_type;\n\n   history_key          key;\n   fc::time_point_sec   time;\n   fill_order_operation op;\n};\nstruct order_history_object_key_base_extractor\n{\n   typedef asset_id_type result_type;\n   result_type operator()(const order_history_object& o)const { return o.key.base; }\n};\nstruct order_history_object_key_quote_extractor\n{\n   typedef asset_id_type result_type;\n   result_type operator()(const order_history_object& o)const { return o.key.quote; }\n};\nstruct order_history_object_key_sequence_extractor\n{\n   typedef int64_t result_type;\n   result_type operator()(const order_history_object& o)const { return o.key.sequence; }\n};\n\nstruct market_ticker_object : public abstract_object<market_ticker_object>\n{\n   static const uint8_t space_id = MARKET_HISTORY_SPACE_ID;\n   static const uint8_t type_id  = market_ticker_object_type;\n\n   asset_id_type       base;\n   asset_id_type       quote;\n   share_type          last_day_base;\n   share_type          last_day_quote;\n   share_type          latest_base;\n   share_type          latest_quote;\n   fc::uint128_t       base_volume;\n   fc::uint128_t       quote_volume;\n};\n\nstruct market_ticker_meta_object : public abstract_object<market_ticker_meta_object>\n{\n   static const uint8_t space_id = MARKET_HISTORY_SPACE_ID;\n   static const uint8_t type_id  = market_ticker_meta_object_type;\n\n   object_id_type      rolling_min_order_his_id;\n   bool                skip_min_order_his_id = false;\n};\n\nstruct by_key;\ntypedef multi_index_container<\n   bucket_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_key>, member< bucket_object, bucket_key, &bucket_object::key > >\n   >\n> bucket_object_multi_index_type;\n\nstruct by_market_time;\ntypedef multi_index_container<\n   order_history_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_unique< tag<by_key>, member< order_history_object, history_key, &order_history_object::key > >,\n      ordered_unique<\n         tag<by_market_time>,\n         composite_key<\n            order_history_object,\n            order_history_object_key_base_extractor,\n            order_history_object_key_quote_extractor,\n            member<order_history_object, time_point_sec, &order_history_object::time>,\n            order_history_object_key_sequence_extractor\n         >,\n         composite_key_compare<\n            std::less< asset_id_type >,\n            std::less< asset_id_type >,\n            std::greater< time_point_sec >,\n            std::less< int64_t >\n         >\n      >\n   >\n> order_history_multi_index_type;\n\nstruct by_market;\nstruct by_volume;\ntypedef multi_index_container<\n   market_ticker_object,\n   indexed_by<\n      ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,\n      ordered_non_unique< tag<by_volume>,\n                          member< market_ticker_object, fc::uint128_t, &market_ticker_object::base_volume > >,\n      ordered_unique<\n         tag<by_market>,\n         composite_key<\n            market_ticker_object,\n            member<market_ticker_object, asset_id_type, &market_ticker_object::base>,\n            member<market_ticker_object, asset_id_type, &market_ticker_object::quote>\n         >\n      >\n   >\n> market_ticker_object_multi_index_type;\n\ntypedef generic_index<bucket_object, bucket_object_multi_index_type> bucket_index;\ntypedef generic_index<order_history_object, order_history_multi_index_type> history_index;\ntypedef generic_index<market_ticker_object, market_ticker_object_multi_index_type> market_ticker_index;\n\n\nnamespace detail\n{\n    class market_history_plugin_impl;\n}\n\n/**\n *  The market history plugin can be configured to track any number of intervals via its configuration.  Once per block it\n *  will scan the virtual operations and look for fill_order_operations and then adjust the appropriate bucket objects for\n *  each fill order.\n */\nclass market_history_plugin : public graphene::app::plugin\n{\n   public:\n      market_history_plugin();\n      virtual ~market_history_plugin();\n\n      std::string plugin_name()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(\n         const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      uint32_t                    max_history()const;\n      const flat_set<uint32_t>&   tracked_buckets()const;\n      uint32_t                    max_order_his_records_per_market()const;\n      uint32_t                    max_order_his_seconds_per_market()const;\n\n   private:\n      friend class detail::market_history_plugin_impl;\n      std::unique_ptr<detail::market_history_plugin_impl> my;\n};\n\n} } //graphene::market_history\n\nFC_REFLECT( graphene::market_history::history_key, (base)(quote)(sequence) )\nFC_REFLECT_DERIVED( graphene::market_history::order_history_object, (graphene::db::object), (key)(time)(op) )\nFC_REFLECT( graphene::market_history::bucket_key, (base)(quote)(seconds)(open) )\nFC_REFLECT_DERIVED( graphene::market_history::bucket_object, (graphene::db::object),\n                    (key)\n                    (high_base)(high_quote)\n                    (low_base)(low_quote)\n                    (open_base)(open_quote)\n                    (close_base)(close_quote)\n                    (base_volume)(quote_volume) )\nFC_REFLECT_DERIVED( graphene::market_history::market_ticker_object, (graphene::db::object),\n                    (base)(quote)\n                    (last_day_base)(last_day_quote)\n                    (latest_base)(latest_quote)\n                    (base_volume)(quote_volume) )\nFC_REFLECT_DERIVED( graphene::market_history::market_ticker_meta_object, (graphene::db::object),\n                    (rolling_min_order_his_id)(skip_min_order_his_id) )\n"
  },
  {
    "path": "libraries/plugins/market_history/market_history_plugin.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/market_history/market_history_plugin.hpp>\n\n#include <graphene/chain/account_evaluator.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/config.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/evaluator.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/transaction_evaluation_state.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/thread/thread.hpp>\n\nnamespace graphene { namespace market_history {\n\nnamespace detail\n{\n\nclass market_history_plugin_impl\n{\n   public:\n      market_history_plugin_impl(market_history_plugin& _plugin)\n      :_self( _plugin ) {}\n      virtual ~market_history_plugin_impl();\n\n      /** this method is called as a callback after a block is applied\n       * and will process/index all operations that were applied in the block.\n       */\n      void update_market_histories( const signed_block& b );\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      market_history_plugin&     _self;\n      flat_set<uint32_t>         _tracked_buckets;\n      uint32_t                   _maximum_history_per_bucket_size = 1000;\n      uint32_t                   _max_order_his_records_per_market = 1000;\n      uint32_t                   _max_order_his_seconds_per_market = 259200;\n};\n\n\nstruct operation_process_fill_order\n{\n   market_history_plugin&            _plugin;\n   fc::time_point_sec                _now;\n   const market_ticker_meta_object*& _meta;\n\n   operation_process_fill_order( market_history_plugin& mhp, fc::time_point_sec n, const market_ticker_meta_object*& meta )\n   :_plugin(mhp),_now(n),_meta(meta) {}\n\n   typedef void result_type;\n\n   /** do nothing for other operation types */\n   template<typename T>\n   void operator()( const T& )const{}\n\n   void operator()( const fill_order_operation& o )const \n   {\n      //ilog( \"processing ${o}\", (\"o\",o) );\n      auto& db         = _plugin.database();\n      const auto& order_his_idx = db.get_index_type<history_index>().indices();\n      const auto& history_idx = order_his_idx.get<by_key>();\n      const auto& his_time_idx = order_his_idx.get<by_market_time>();\n\n      // To save new filled order data\n      history_key hkey;\n      hkey.base = o.pays.asset_id;\n      hkey.quote = o.receives.asset_id;\n      if( hkey.base > hkey.quote ) \n         std::swap( hkey.base, hkey.quote );\n      hkey.sequence = std::numeric_limits<int64_t>::min();\n\n      auto itr = history_idx.lower_bound( hkey );\n\n      if( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote )\n         hkey.sequence = itr->key.sequence - 1;\n      else\n         hkey.sequence = 0;\n\n      const auto& new_order_his_obj = db.create<order_history_object>( [&]( order_history_object& ho ) {\n         ho.key = hkey;\n         ho.time = _now;\n         ho.op = o;\n      });\n\n      // save a reference to market ticker meta object\n      if( _meta == nullptr )\n      {\n         const auto& meta_idx = db.get_index_type<simple_index<market_ticker_meta_object>>();\n         if( meta_idx.size() == 0 )\n            _meta = &db.create<market_ticker_meta_object>( [&]( market_ticker_meta_object& mtm ) {\n               mtm.rolling_min_order_his_id = new_order_his_obj.id;\n               mtm.skip_min_order_his_id = false;\n            });\n         else\n            _meta = &( *meta_idx.begin() );\n      }\n\n      // To remove old filled order data\n      const auto max_records = _plugin.max_order_his_records_per_market();\n      hkey.sequence += max_records;\n      itr = history_idx.lower_bound( hkey );\n      if( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote )\n      {\n         const auto max_seconds = _plugin.max_order_his_seconds_per_market();\n         fc::time_point_sec min_time;\n         if( min_time + max_seconds < _now )\n            min_time = _now - max_seconds;\n         auto time_itr = his_time_idx.lower_bound( std::make_tuple( hkey.base, hkey.quote, min_time ) );\n         if( time_itr != his_time_idx.end() && time_itr->key.base == hkey.base && time_itr->key.quote == hkey.quote )\n         {\n            if( itr->key.sequence >= time_itr->key.sequence )\n            {\n               while( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote )\n               {\n                  auto old_itr = itr;\n                  ++itr;\n                  db.remove( *old_itr );\n               }\n            }\n            else\n            {\n               while( time_itr != his_time_idx.end() && time_itr->key.base == hkey.base && time_itr->key.quote == hkey.quote )\n               {\n                  auto old_itr = time_itr;\n                  ++time_itr;\n                  db.remove( *old_itr );\n               }\n            }\n         }\n      }\n\n      // To update ticker data and buckets data, only update for maker orders\n      if( !o.is_maker )\n         return;\n\n      bucket_key key;\n      key.base    = o.pays.asset_id;\n      key.quote   = o.receives.asset_id;\n\n      price trade_price = o.pays / o.receives;\n\n      if( key.base > key.quote )\n      {\n         std::swap( key.base, key.quote );\n         trade_price = ~trade_price;\n      }\n\n      price fill_price = o.fill_price;\n      if( fill_price.base.asset_id > fill_price.quote.asset_id )\n         fill_price = ~fill_price;\n\n      // To update ticker data\n      const auto& ticker_idx = db.get_index_type<market_ticker_index>().indices().get<by_market>();\n      auto ticker_itr = ticker_idx.find( std::make_tuple( key.base, key.quote ) );\n      if( ticker_itr == ticker_idx.end() )\n      {\n         db.create<market_ticker_object>( [&]( market_ticker_object& mt ) {\n            mt.base           = key.base;\n            mt.quote          = key.quote;\n            mt.last_day_base  = 0;\n            mt.last_day_quote = 0;\n            mt.latest_base    = fill_price.base.amount;\n            mt.latest_quote   = fill_price.quote.amount;\n            mt.base_volume    = trade_price.base.amount.value;\n            mt.quote_volume   = trade_price.quote.amount.value;\n         });\n      }\n      else\n      {\n         db.modify( *ticker_itr, [&]( market_ticker_object& mt ) {\n            mt.latest_base    = fill_price.base.amount;\n            mt.latest_quote   = fill_price.quote.amount;\n            mt.base_volume    += trade_price.base.amount.value;  // ignore overflow\n            mt.quote_volume   += trade_price.quote.amount.value; // ignore overflow\n         });\n      }\n\n      // To update buckets data\n      const auto max_history = _plugin.max_history();\n      if( max_history == 0 ) return;\n\n      const auto& buckets = _plugin.tracked_buckets();\n      if( buckets.size() == 0 ) return;\n\n      const auto& bucket_idx = db.get_index_type<bucket_index>();\n      for( auto bucket : buckets )\n      {\n          auto bucket_num = _now.sec_since_epoch() / bucket;\n          fc::time_point_sec cutoff;\n          if( bucket_num > max_history )\n             cutoff = cutoff + ( bucket * ( bucket_num - max_history ) );\n\n          key.seconds = bucket;\n          key.open    = fc::time_point_sec() + ( bucket_num * bucket );\n\n          const auto& by_key_idx = bucket_idx.indices().get<by_key>();\n          auto bucket_itr = by_key_idx.find( key );\n          if( bucket_itr == by_key_idx.end() )\n          { // create new bucket\n            /* const auto& obj = */\n            db.create<bucket_object>( [&]( bucket_object& b ){\n                 b.key = key;\n                 b.base_volume = trade_price.base.amount;\n                 b.quote_volume = trade_price.quote.amount;\n                 b.open_base = fill_price.base.amount;\n                 b.open_quote = fill_price.quote.amount;\n                 b.close_base = fill_price.base.amount;\n                 b.close_quote = fill_price.quote.amount;\n                 b.high_base = b.close_base;\n                 b.high_quote = b.close_quote;\n                 b.low_base = b.close_base;\n                 b.low_quote = b.close_quote;\n            });\n            //wlog( \"    creating bucket ${b}\", (\"b\",obj) );\n          }\n          else\n          { // update existing bucket\n             //wlog( \"    before updating bucket ${b}\", (\"b\",*bucket_itr) );\n             db.modify( *bucket_itr, [&]( bucket_object& b ){\n                  try {\n                     b.base_volume += trade_price.base.amount;\n                  } catch( fc::overflow_exception& ) {\n                     b.base_volume = std::numeric_limits<int64_t>::max();\n                  }\n                  try {\n                     b.quote_volume += trade_price.quote.amount;\n                  } catch( fc::overflow_exception& ) {\n                     b.quote_volume = std::numeric_limits<int64_t>::max();\n                  }\n                  b.close_base = fill_price.base.amount;\n                  b.close_quote = fill_price.quote.amount;\n                  if( b.high() < fill_price )\n                  {\n                      b.high_base = b.close_base;\n                      b.high_quote = b.close_quote;\n                  }\n                  if( b.low() > fill_price )\n                  {\n                      b.low_base = b.close_base;\n                      b.low_quote = b.close_quote;\n                  }\n             });\n             //wlog( \"    after bucket bucket ${b}\", (\"b\",*bucket_itr) );\n          }\n\n          {\n             key.open = fc::time_point_sec();\n             bucket_itr = by_key_idx.lower_bound( key );\n\n             while( bucket_itr != by_key_idx.end() &&\n                    bucket_itr->key.base == key.base &&\n                    bucket_itr->key.quote == key.quote &&\n                    bucket_itr->key.seconds == bucket &&\n                    bucket_itr->key.open < cutoff )\n             {\n              //  elog( \"    removing old bucket ${b}\", (\"b\", *bucket_itr) );\n                auto old_bucket_itr = bucket_itr;\n                ++bucket_itr;\n                db.remove( *old_bucket_itr );\n             }\n          }\n      }\n   }\n};\n\nmarket_history_plugin_impl::~market_history_plugin_impl()\n{}\n\nvoid market_history_plugin_impl::update_market_histories( const signed_block& b )\n{\n   graphene::chain::database& db = database();\n   const market_ticker_meta_object* _meta = nullptr;\n   const auto& meta_idx = db.get_index_type<simple_index<market_ticker_meta_object>>();\n   if( meta_idx.size() > 0 )\n      _meta = &( *meta_idx.begin() );\n   const vector<optional< operation_history_object > >& hist = db.get_applied_operations();\n   for( const optional< operation_history_object >& o_op : hist )\n   {\n      if( o_op.valid() )\n      {\n         try\n         {\n            o_op->op.visit( operation_process_fill_order( _self, b.timestamp, _meta ) );\n         } FC_CAPTURE_AND_LOG( (o_op) )\n      }\n   }\n   // roll out expired data from ticker\n   if( _meta != nullptr )\n   {\n      time_point_sec last_day = b.timestamp - 86400;\n      object_id_type last_min_his_id = _meta->rolling_min_order_his_id;\n      bool skip = _meta->skip_min_order_his_id;\n\n      const auto& ticker_idx = db.get_index_type<market_ticker_index>().indices().get<by_market>();\n      const auto& history_idx = db.get_index_type<history_index>().indices().get<by_id>();\n      auto history_itr = history_idx.lower_bound( _meta->rolling_min_order_his_id );\n      while( history_itr != history_idx.end() && history_itr->time < last_day )\n      {\n         const fill_order_operation& o = history_itr->op;\n         if( skip && history_itr->id == _meta->rolling_min_order_his_id )\n            skip = false;\n         else if( o.is_maker )\n         {\n            bucket_key key;\n            key.base    = o.pays.asset_id;\n            key.quote   = o.receives.asset_id;\n\n            price trade_price = o.pays / o.receives;\n\n            if( key.base > key.quote )\n            {\n               std::swap( key.base, key.quote );\n               trade_price = ~trade_price;\n            }\n\n            price fill_price = o.fill_price;\n            if( fill_price.base.asset_id > fill_price.quote.asset_id )\n               fill_price = ~fill_price;\n\n            auto ticker_itr = ticker_idx.find( std::make_tuple( key.base, key.quote ) );\n            if( ticker_itr != ticker_idx.end() ) // should always be true\n            {\n               db.modify( *ticker_itr, [&]( market_ticker_object& mt ) {\n                  mt.last_day_base  = fill_price.base.amount;\n                  mt.last_day_quote = fill_price.quote.amount;\n                  mt.base_volume    -= trade_price.base.amount.value;  // ignore underflow\n                  mt.quote_volume   -= trade_price.quote.amount.value; // ignore underflow\n               });\n            }\n         }\n         last_min_his_id = history_itr->id;\n         ++history_itr;\n      }\n      // update meta\n      if( history_itr != history_idx.end() ) // if still has some data rolling\n      {\n         if( history_itr->id != _meta->rolling_min_order_his_id ) // if rolled out some\n         {\n            db.modify( *_meta, [&]( market_ticker_meta_object& mtm ) {\n               mtm.rolling_min_order_his_id = history_itr->id;\n               mtm.skip_min_order_his_id = false;\n            });\n         }\n      }\n      else // if all data are rolled out\n      {\n         if( !_meta->skip_min_order_his_id\n             || last_min_his_id != _meta->rolling_min_order_his_id ) // if rolled out some\n         {\n            db.modify( *_meta, [&]( market_ticker_meta_object& mtm ) {\n               mtm.rolling_min_order_his_id = last_min_his_id;\n               mtm.skip_min_order_his_id = true;\n            });\n         }\n      }\n   }\n}\n\n} // end namespace detail\n\n\n\n\n\n\nmarket_history_plugin::market_history_plugin() :\n   my( new detail::market_history_plugin_impl(*this) )\n{\n}\n\nmarket_history_plugin::~market_history_plugin()\n{\n}\n\nstd::string market_history_plugin::plugin_name()const\n{\n   return \"market_history\";\n}\n\nvoid market_history_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"bucket-size\", boost::program_options::value<string>()->default_value(\"[60,300,900,1800,3600,14400,86400]\"),\n           \"Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers\")\n         (\"history-per-size\", boost::program_options::value<uint32_t>()->default_value(1000),\n           \"How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)\")\n         (\"max-order-his-records-per-market\", boost::program_options::value<uint32_t>()->default_value(1000),\n           \"Will only store this amount of matched orders for each market in order history for querying, or those meet the other option, which has more data (default: 1000)\")\n         (\"max-order-his-seconds-per-market\", boost::program_options::value<uint32_t>()->default_value(259200),\n           \"Will only store matched orders in last X seconds for each market in order history for querying, or those meet the other option, which has more data (default: 259200 (3 days))\")\n         ;\n   cfg.add(cli);\n}\n\nvoid market_history_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{ try {\n   database().applied_block.connect( [this]( const signed_block& b){ my->update_market_histories(b); } );\n   database().add_index< primary_index< bucket_index  > >();\n   database().add_index< primary_index< history_index  > >();\n   database().add_index< primary_index< market_ticker_index  > >();\n   database().add_index< primary_index< simple_index< market_ticker_meta_object > > >();\n\n   if( options.count( \"bucket-size\" ) )\n   {\n      const std::string& buckets = options[\"bucket-size\"].as<string>();\n      my->_tracked_buckets = fc::json::from_string(buckets).as<flat_set<uint32_t>>(2);\n      my->_tracked_buckets.erase( 0 );\n   }\n   if( options.count( \"history-per-size\" ) )\n      my->_maximum_history_per_bucket_size = options[\"history-per-size\"].as<uint32_t>();\n   if( options.count( \"max-order-his-records-per-market\" ) )\n      my->_max_order_his_records_per_market = options[\"max-order-his-records-per-market\"].as<uint32_t>();\n   if( options.count( \"max-order-his-seconds-per-market\" ) )\n      my->_max_order_his_seconds_per_market = options[\"max-order-his-seconds-per-market\"].as<uint32_t>();\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid market_history_plugin::plugin_startup()\n{\n}\n\nconst flat_set<uint32_t>& market_history_plugin::tracked_buckets() const\n{\n   return my->_tracked_buckets;\n}\n\nuint32_t market_history_plugin::max_history()const\n{\n   return my->_maximum_history_per_bucket_size;\n}\n\nuint32_t market_history_plugin::max_order_his_records_per_market()const\n{\n   return my->_max_order_his_records_per_market;\n}\n\nuint32_t market_history_plugin::max_order_his_seconds_per_market()const\n{\n   return my->_max_order_his_seconds_per_market;\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/snapshot/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/snapshot/*.hpp\")\n\nadd_library( graphene_snapshot\n             snapshot.cpp\n           )\n\ntarget_link_libraries( graphene_snapshot graphene_chain graphene_app )\ntarget_include_directories( graphene_snapshot\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\ninstall( TARGETS\n   graphene_snapshot\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp",
    "content": "/*\n * Copyright (c) 2017 Peter Conrad, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <fc/time.hpp>\n\nnamespace graphene { namespace snapshot_plugin {\n\nclass snapshot_plugin : public graphene::app::plugin {\n   public:\n      ~snapshot_plugin() {}\n\n      std::string plugin_name()const override;\n      std::string plugin_description()const override;\n\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description &command_line_options,\n         boost::program_options::options_description &config_file_options\n      ) override;\n\n      virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;\n      virtual void plugin_startup() override;\n      virtual void plugin_shutdown() override;\n\n   private:\n       void check_snapshot( const graphene::chain::signed_block& b);\n\n       uint32_t           snapshot_block = -1, last_block = 0;\n       fc::time_point_sec snapshot_time = fc::time_point_sec::maximum(), last_time = fc::time_point_sec(1);\n       fc::path           dest;\n};\n\n} } //graphene::snapshot_plugin\n"
  },
  {
    "path": "libraries/plugins/snapshot/snapshot.cpp",
    "content": "/*\n * Copyright (c) 2017 Peter Conrad, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/snapshot/snapshot.hpp>\n\n#include <graphene/chain/database.hpp>\n\n#include <fc/io/fstream.hpp>\n\nusing namespace graphene::snapshot_plugin;\nusing std::string;\nusing std::vector;\n\nnamespace bpo = boost::program_options;\n\nstatic const char* OPT_BLOCK_NUM  = \"snapshot-at-block\";\nstatic const char* OPT_BLOCK_TIME = \"snapshot-at-time\";\nstatic const char* OPT_DEST       = \"snapshot-to\";\n\nvoid snapshot_plugin::plugin_set_program_options(\n   boost::program_options::options_description& command_line_options,\n   boost::program_options::options_description& config_file_options)\n{\n   command_line_options.add_options()\n         (OPT_BLOCK_NUM, bpo::value<uint32_t>(), \"Block number after which to do a snapshot\")\n         (OPT_BLOCK_TIME, bpo::value<string>(), \"Block time (ISO format) after which to do a snapshot\")\n         (OPT_DEST, bpo::value<string>(), \"Pathname of JSON file where to store the snapshot\")\n         ;\n   config_file_options.add(command_line_options);\n}\n\nstd::string snapshot_plugin::plugin_name()const\n{\n   return \"snapshot\";\n}\n\nstd::string snapshot_plugin::plugin_description()const\n{\n   return \"Create snapshots at a specified time or block number.\";\n}\n\nvoid snapshot_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{ try {\n   ilog(\"snapshot plugin: plugin_initialize() begin\");\n\n   if( options.count(OPT_BLOCK_NUM) || options.count(OPT_BLOCK_TIME) )\n   {\n      FC_ASSERT( options.count(OPT_DEST), \"Must specify snapshot-to in addition to snapshot-at-block or snapshot-at-time!\" );\n      dest = options[OPT_DEST].as<std::string>();\n      if( options.count(OPT_BLOCK_NUM) )\n         snapshot_block = options[OPT_BLOCK_NUM].as<uint32_t>();\n      if( options.count(OPT_BLOCK_TIME) )\n         snapshot_time = fc::time_point_sec::from_iso_string( options[OPT_BLOCK_TIME].as<std::string>() );\n      database().applied_block.connect( [&]( const graphene::chain::signed_block& b ) {\n         check_snapshot( b );\n      });\n   }\n   else\n      FC_ASSERT( !options.count(\"snapshot-to\"), \"Must specify snapshot-at-block or snapshot-at-time in addition to snapshot-to!\" );\n   ilog(\"snapshot plugin: plugin_initialize() end\");\n} FC_LOG_AND_RETHROW() }\n\nvoid snapshot_plugin::plugin_startup() {}\n\nvoid snapshot_plugin::plugin_shutdown() {}\n\nstatic void create_snapshot( const graphene::chain::database& db, const fc::path& dest )\n{\n   ilog(\"snapshot plugin: creating snapshot\");\n   fc::ofstream out;\n   try\n   {\n      out.open( dest );\n   }\n   catch ( fc::exception& e )\n   {\n      wlog( \"Failed to open snapshot destination: ${ex}\", (\"ex\",e) );\n      return;\n   }\n   for( uint32_t space_id = 0; space_id < 256; space_id++ )\n      for( uint32_t type_id = 0; type_id < 256; type_id++ )\n      {\n         try\n         {\n            db.get_index( (uint8_t)space_id, (uint8_t)type_id );\n         }\n         catch (fc::assert_exception& e)\n         {\n            continue;\n         }\n         auto& index = db.get_index( (uint8_t)space_id, (uint8_t)type_id );\n         index.inspect_all_objects( [&out]( const graphene::db::object& o ) {\n            out << fc::json::to_string( o.to_variant() ) << '\\n';\n         });\n      }\n   out.close();\n   ilog(\"snapshot plugin: created snapshot\");\n}\n\nvoid snapshot_plugin::check_snapshot( const graphene::chain::signed_block& b )\n{ try {\n    uint32_t current_block = b.block_num();\n    if( (last_block < snapshot_block && snapshot_block <= current_block)\n           || (last_time < snapshot_time && snapshot_time <= b.timestamp) )\n       create_snapshot( database(), dest );\n    last_block = current_block;\n    last_time = b.timestamp;\n} FC_LOG_AND_RETHROW() }\n"
  },
  {
    "path": "libraries/plugins/template_plugin/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/template_plugin/*.hpp\")\n\nadd_library( graphene_template_plugin\n        template_plugin.cpp\n           )\n\ntarget_link_libraries( graphene_template_plugin graphene_chain graphene_app )\ntarget_include_directories( graphene_template_plugin\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties(template_plugin.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_template_plugin\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\nINSTALL( FILES ${HEADERS} DESTINATION \"include/graphene/template_plugin\" )\n"
  },
  {
    "path": "libraries/plugins/template_plugin/include/graphene/template_plugin/template_plugin.hpp",
    "content": "/*\n * Copyright (c) 2018 template_plugin and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\nnamespace graphene { namespace template_plugin {\nusing namespace chain;\n\n//\n// Plugins should #define their SPACE_ID's so plugins with\n// conflicting SPACE_ID assignments can be compiled into the\n// same binary (by simply re-assigning some of the conflicting #defined\n// SPACE_ID's in a build script).\n//\n// Assignment of SPACE_ID's cannot be done at run-time because\n// various template automagic depends on them being known at compile\n// time.\n//\n#ifndef template_plugin_SPACE_ID\n#define template_plugin_SPACE_ID @SPACE_ID@\n#endif\n\n\nnamespace detail\n{\n    class template_plugin_impl;\n}\n\nclass template_plugin : public graphene::app::plugin\n{\n   public:\n      template_plugin();\n      virtual ~template_plugin();\n\n      std::string plugin_name()const override;\n      std::string plugin_description()const override;\n      virtual void plugin_set_program_options(\n         boost::program_options::options_description& cli,\n         boost::program_options::options_description& cfg) override;\n      virtual void plugin_initialize(const boost::program_options::variables_map& options) override;\n      virtual void plugin_startup() override;\n\n      friend class detail::template_plugin_impl;\n      std::unique_ptr<detail::template_plugin_impl> my;\n};\n\n} } //graphene::template\n"
  },
  {
    "path": "libraries/plugins/template_plugin/template_plugin.cpp",
    "content": "/*\n * Copyright (c) 2018 template_plugin and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/template_plugin/template_plugin.hpp>\n\nnamespace graphene { namespace template_plugin {\n\nnamespace detail\n{\n\nclass template_plugin_impl\n{\n   public:\n      template_plugin_impl(template_plugin& _plugin)\n         : _self( _plugin )\n      {  }\n      virtual ~template_plugin_impl();\n\n      void onBlock( const signed_block& b );\n\n      graphene::chain::database& database()\n      {\n         return _self.database();\n      }\n\n      template_plugin& _self;\n\n      std::string _plugin_option = \"\";\n\n   private:\n\n};\n\nvoid template_plugin_impl::onBlock( const signed_block& b )\n{\n   wdump((b.block_num()));\n}\n\ntemplate_plugin_impl::~template_plugin_impl()\n{\n   return;\n}\n\n} // end namespace detail\n\ntemplate_plugin::template_plugin() :\n   my( new detail::template_plugin_impl(*this) )\n{\n}\n\ntemplate_plugin::~template_plugin()\n{\n}\n\nstd::string template_plugin::plugin_name()const\n{\n   return \"template_plugin\";\n}\nstd::string template_plugin::plugin_description()const\n{\n   return \"template_plugin description\";\n}\n\nvoid template_plugin::plugin_set_program_options(\n   boost::program_options::options_description& cli,\n   boost::program_options::options_description& cfg\n   )\n{\n   cli.add_options()\n         (\"template_plugin_option\", boost::program_options::value<std::string>(), \"template_plugin option\")\n         ;\n   cfg.add(cli);\n}\n\nvoid template_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{\n   database().applied_block.connect( [&]( const signed_block& b) {\n      my->onBlock(b);\n   } );\n\n   if (options.count(\"template_plugin\")) {\n      my->_plugin_option = options[\"template_plugin\"].as<std::string>();\n   }\n}\n\nvoid template_plugin::plugin_startup()\n{\n   ilog(\"template_plugin: plugin_startup() begin\");\n}\n\n} }\n"
  },
  {
    "path": "libraries/plugins/witness/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/witness/*.hpp\")\n\nadd_library( graphene_witness \n             witness.cpp\n           )\n\ntarget_link_libraries( graphene_witness graphene_chain graphene_app )\ntarget_include_directories( graphene_witness\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif (MSVC)\n    set_target_properties( graphene_witness PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_witness\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "libraries/plugins/witness/include/graphene/witness/witness.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/plugin.hpp>\n#include <graphene/chain/database.hpp>\n\n#include <fc/thread/future.hpp>\n\nnamespace graphene { namespace witness_plugin {\n\nnamespace block_production_condition\n{\n   enum block_production_condition_enum\n   {\n      produced = 0,\n      not_synced = 1,\n      not_my_turn = 2,\n      not_time_yet = 3,\n      no_private_key = 4,\n      low_participation = 5,\n      lag = 6,\n      exception_producing_block = 7,\n      shutdown = 8\n   };\n}\n\nclass witness_plugin : public graphene::app::plugin {\npublic:\n   ~witness_plugin() { stop_block_production(); }\n\n   std::string plugin_name()const override;\n\n   virtual void plugin_set_program_options(\n      boost::program_options::options_description &command_line_options,\n      boost::program_options::options_description &config_file_options\n      ) override;\n\n   void set_block_production(bool allow) { _production_enabled = allow; }\n   void stop_block_production();\n\n   virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;\n   virtual void plugin_startup() override;\n   virtual void plugin_shutdown() override;\n\n   inline const fc::flat_map< chain::witness_id_type, fc::optional<chain::public_key_type> >& get_witness_key_cache()\n   { return _witness_key_cache; }\n\nprivate:\n   void schedule_production_loop();\n   block_production_condition::block_production_condition_enum block_production_loop();\n   block_production_condition::block_production_condition_enum maybe_produce_block( fc::limited_mutable_variant_object& capture );\n   void add_private_key(const std::string& key_id_to_wif_pair_string);\n\n   /// Fetch signing keys of all witnesses in the cache from object database and update the cache accordingly\n   void refresh_witness_key_cache();\n\n   boost::program_options::variables_map _options;\n   bool _production_enabled = false;\n   bool _shutting_down = false;\n   uint32_t _required_witness_participation = 33 * GRAPHENE_1_PERCENT;\n   uint32_t _production_skip_flags = graphene::chain::database::skip_nothing;\n\n   std::map<chain::public_key_type, fc::ecc::private_key, chain::pubkey_comparator> _private_keys;\n   std::set<chain::witness_id_type> _witnesses;\n   fc::future<void> _block_production_task;\n\n   /// For tracking signing keys of specified witnesses, only update when applied a block\n   fc::flat_map< chain::witness_id_type, fc::optional<chain::public_key_type> > _witness_key_cache;\n\n};\n\n} } //graphene::witness_plugin\n"
  },
  {
    "path": "libraries/plugins/witness/witness.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/witness/witness.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <graphene/utilities/key_conversion.hpp>\n\n#include <fc/thread/thread.hpp>\n#include <fc/io/fstream.hpp>\n\n#include <boost/filesystem/path.hpp>\n\n#include <iostream>\n\nusing namespace graphene::witness_plugin;\nusing std::string;\nusing std::vector;\n\nnamespace bpo = boost::program_options;\n\nvoid new_chain_banner( const graphene::chain::database& db )\n{\n   ilog(\"\\n\"\n      \"********************************\\n\"\n      \"*                              *\\n\"\n      \"*   ------- NEW CHAIN ------   *\\n\"\n      \"*   -    Welcome to NBS!    -  *\\n\"\n      \"*   ------------------------   *\\n\"\n      \"*                              *\\n\"\n      \"********************************\\n\"\n      \"\\n\");\n   if( db.get_slot_at_time( fc::time_point::now() ) > 200 )\n   {\n      wlog(\"Your genesis seems to have an old timestamp\");\n      wlog(\"Please consider using the --genesis-timestamp option to give your genesis a recent timestamp\");\n   }\n}\n\nvoid witness_plugin::plugin_set_program_options(\n   boost::program_options::options_description& command_line_options,\n   boost::program_options::options_description& config_file_options)\n{\n   auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string(\"nathan\")));\n   string witness_id_example = fc::json::to_string(chain::witness_id_type(5));\n   command_line_options.add_options()\n         (\"enable-stale-production\", bpo::bool_switch()->notifier([this](bool e){_production_enabled = e;}),\n               \"Enable block production, even if the chain is stale.\")\n         (\"required-participation\", bpo::value<uint32_t>()->default_value(33),\n               \"Percent of witnesses (0-100) that must be participating in order to produce blocks\")\n         (\"witness-id,w\", bpo::value<vector<string>>()->composing()->multitoken(),\n               (\"ID of witness controlled by this node (e.g. \" + witness_id_example +\n               \", quotes are required, may specify multiple times)\").c_str())\n         (\"private-key\", bpo::value<vector<string>>()->composing()->multitoken()->\n          DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()),\n                graphene::utilities::key_to_wif(default_priv_key))),\n                \"Tuple of [PublicKey, WIF private key] (may specify multiple times)\")\n         (\"private-key-file\", bpo::value<vector<boost::filesystem::path>>()->composing()->multitoken(),\n          \"Path to a file containing tuples of [PublicKey, WIF private key].\"\n          \" The file has to contain exactly one tuple (i.e. private - public key pair) per line.\"\n          \" This option may be specified multiple times, thus multiple files can be provided.\")\n         ;\n   config_file_options.add(command_line_options);\n}\n\nstd::string witness_plugin::plugin_name()const\n{\n   return \"witness\";\n}\n\nvoid witness_plugin::add_private_key(const std::string& key_id_to_wif_pair_string)\n{\n   auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string>>\n         (key_id_to_wif_pair_string, 5);\n   fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second);\n   if (!private_key)\n   {\n      // the key isn't in WIF format; see if they are still passing the old native private key format.  This is\n      // just here to ease the transition, can be removed soon\n      try\n      {\n         private_key = fc::variant(key_id_to_wif_pair.second, 2).as<fc::ecc::private_key>(1);\n      }\n      catch (const fc::exception&)\n      {\n         FC_THROW(\"Invalid WIF-format private key ${key_string}\", (\"key_string\", key_id_to_wif_pair.second));\n      }\n   }\n\n   if (_private_keys.find(key_id_to_wif_pair.first) == _private_keys.end())\n   {\n      ilog(\"Public Key: ${public}\", (\"public\", key_id_to_wif_pair.first));\n      _private_keys[key_id_to_wif_pair.first] = *private_key;\n   }\n}\n\nvoid witness_plugin::plugin_initialize(const boost::program_options::variables_map& options)\n{ try {\n   ilog(\"witness plugin:  plugin_initialize() begin\");\n   _options = &options;\n   LOAD_VALUE_SET(options, \"witness-id\", _witnesses, chain::witness_id_type)\n\n   if( options.count(\"private-key\") )\n   {\n      const std::vector<std::string> key_id_to_wif_pair_strings = options[\"private-key\"].as<std::vector<std::string>>();\n      for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)\n      {\n         add_private_key(key_id_to_wif_pair_string);\n      }\n   }\n   if (options.count(\"private-key-file\"))\n   {\n      const std::vector<boost::filesystem::path> key_id_to_wif_pair_files =\n            options[\"private-key-file\"].as<std::vector<boost::filesystem::path>>();\n      for (const boost::filesystem::path& key_id_to_wif_pair_file : key_id_to_wif_pair_files)\n      {\n         if (fc::exists(key_id_to_wif_pair_file))\n         {\n            std::string file_content;\n            fc::read_file_contents(key_id_to_wif_pair_file, file_content);\n            std::istringstream file_content_as_stream(file_content);\n\n            std::string line; // key_id_to_wif_pair_string\n            while (std::getline(file_content_as_stream, line))\n            {\n               add_private_key(line);\n            }\n         }\n         else\n         {\n            FC_THROW(\"Failed to load private key file from ${path}\", (\"path\", key_id_to_wif_pair_file.string()));\n         }\n      }\n   }\n   if(options.count(\"required-participation\"))\n   {\n       auto required_participation = options[\"required-participation\"].as<uint32_t>();\n       FC_ASSERT(required_participation <= 100);\n       _required_witness_participation = options[\"required-participation\"].as<uint32_t>()*GRAPHENE_1_PERCENT;\n       if(required_participation < 10)\n           wlog(\"witness plugin: Warning - Low required participation of ${rp}% found\", (\"rp\", required_participation));\n       else if(required_participation > 90)\n           wlog(\"witness plugin: Warning - High required participation of ${rp}% found\", (\"rp\", required_participation));\n   }\n   ilog(\"witness plugin:  plugin_initialize() end\");\n} FC_LOG_AND_RETHROW() }\n\nvoid witness_plugin::plugin_startup()\n{ try {\n   ilog(\"witness plugin:  plugin_startup() begin\");\n   chain::database& d = database();\n   if( !_witnesses.empty() )\n   {\n      ilog(\"Launching block production for ${n} witnesses.\", (\"n\", _witnesses.size()));\n      app().set_block_production(true);\n      if( _production_enabled )\n      {\n         if( d.head_block_num() == 0 )\n            new_chain_banner(d);\n         _production_skip_flags |= graphene::chain::database::skip_undo_history_check;\n      }\n      refresh_witness_key_cache();\n      d.applied_block.connect( [this]( const chain::signed_block& b )\n      {\n         refresh_witness_key_cache();\n      });\n      schedule_production_loop();\n   }\n   else\n   {\n      ilog(\"No witness configured.\");\n   }\n   ilog(\"witness plugin:  plugin_startup() end\");\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid witness_plugin::plugin_shutdown()\n{\n   stop_block_production();\n}\n\nvoid witness_plugin::stop_block_production()\n{\n   _shutting_down = true;\n   \n   try {\n      if( _block_production_task.valid() )\n         _block_production_task.cancel_and_wait(__FUNCTION__);\n   } catch(fc::canceled_exception&) {\n      //Expected exception. Move along.\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n   }\n}\n\nvoid witness_plugin::refresh_witness_key_cache()\n{\n   const auto& db = database();\n   for( const chain::witness_id_type wit_id : _witnesses )\n   {\n      const chain::witness_object* wit_obj = db.find( wit_id );\n      if( wit_obj )\n         _witness_key_cache[wit_id] = wit_obj->signing_key;\n      else\n         _witness_key_cache[wit_id] = fc::optional<chain::public_key_type>();\n   }\n}\n\nvoid witness_plugin::schedule_production_loop()\n{\n   if (_shutting_down) return;\n\n   //Schedule for the next second's tick regardless of chain state\n   // If we would wait less than 50ms, wait for the whole second.\n   fc::time_point now = fc::time_point::now();\n   int64_t time_to_next_second = 1000000 - (now.time_since_epoch().count() % 1000000);\n   if( time_to_next_second < 50000 )      // we must sleep for at least 50ms\n       time_to_next_second += 1000000;\n\n   fc::time_point next_wakeup( now + fc::microseconds( time_to_next_second ) );\n\n   _block_production_task = fc::schedule([this]{block_production_loop();},\n                                         next_wakeup, \"Witness Block Production\");\n}\n\nblock_production_condition::block_production_condition_enum witness_plugin::block_production_loop()\n{\n   block_production_condition::block_production_condition_enum result;\n   fc::limited_mutable_variant_object capture( GRAPHENE_MAX_NESTED_OBJECTS );\n\n   if (_shutting_down) \n   {\n      result = block_production_condition::shutdown;\n   }\n   else\n   {\n      try\n      {\n         result = maybe_produce_block(capture);\n      }\n      catch( const fc::canceled_exception& )\n      {\n         //We're trying to exit. Go ahead and let this one out.\n         throw;\n      }\n      catch( const fc::exception& e )\n      {\n         elog(\"Got exception while generating block:\\n${e}\", (\"e\", e.to_detail_string()));\n         result = block_production_condition::exception_producing_block;\n      }\n   }\n\n   switch( result )\n   {\n      case block_production_condition::produced:\n         ilog(\"Generated block #${n} with ${x} transaction(s) and timestamp ${t} at time ${c}\", (capture));\n         break;\n      case block_production_condition::not_synced:\n         ilog(\"Not producing block because production is disabled until we receive a recent block \"\n              \"(see: --enable-stale-production)\");\n         break;\n      case block_production_condition::not_my_turn:\n         break;\n      case block_production_condition::not_time_yet:\n         break;\n      case block_production_condition::no_private_key:\n         ilog(\"Not producing block because I don't have the private key for ${scheduled_key}\", (capture) );\n         break;\n      case block_production_condition::low_participation:\n         elog(\"Not producing block because node appears to be on a minority fork with only ${pct}% witness participation\",\n               (capture) );\n         break;\n      case block_production_condition::lag:\n         elog(\"Not producing block because node didn't wake up within 2500ms of the slot time.\");\n         break;\n      case block_production_condition::exception_producing_block:\n         elog( \"exception producing block\" );\n         break;\n      case block_production_condition::shutdown:\n         ilog( \"shutdown producing block\" );\n         return result;\n      default:\n         elog( \"unknown condition ${result} while producing block\", (\"result\", (unsigned char)result) );\n         break;\n   }\n\n   schedule_production_loop();\n   return result;\n}\n\nblock_production_condition::block_production_condition_enum witness_plugin::maybe_produce_block(\n      fc::limited_mutable_variant_object& capture )\n{\n   chain::database& db = database();\n   fc::time_point now_fine = fc::time_point::now();\n   fc::time_point_sec now = now_fine + fc::microseconds( 500000 );\n\n   // If the next block production opportunity is in the present or future, we're synced.\n   if( !_production_enabled )\n   {\n      if( db.get_slot_time(1) >= now )\n         _production_enabled = true;\n      else\n         return block_production_condition::not_synced;\n   }\n\n   // is anyone scheduled to produce now or one second in the future?\n   uint32_t slot = db.get_slot_at_time( now );\n   if( slot == 0 )\n   {\n      capture(\"next_time\", db.get_slot_time(1));\n      return block_production_condition::not_time_yet;\n   }\n\n   //\n   // this assert should not fail, because now <= db.head_block_time()\n   // should have resulted in slot == 0.\n   //\n   // if this assert triggers, there is a serious bug in get_slot_at_time()\n   // which would result in allowing a later block to have a timestamp\n   // less than or equal to the previous block\n   //\n   assert( now > db.head_block_time() );\n\n   graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot );\n   // we must control the witness scheduled to produce the next block.\n   if( _witnesses.find( scheduled_witness ) == _witnesses.end() )\n   {\n      capture(\"scheduled_witness\", scheduled_witness);\n      return block_production_condition::not_my_turn;\n   }\n\n   fc::time_point_sec scheduled_time = db.get_slot_time( slot );\n   graphene::chain::public_key_type scheduled_key = *_witness_key_cache[scheduled_witness]; // should be valid\n   auto private_key_itr = _private_keys.find( scheduled_key );\n\n   if( private_key_itr == _private_keys.end() )\n   {\n      capture(\"scheduled_key\", scheduled_key);\n      return block_production_condition::no_private_key;\n   }\n\n   uint32_t prate = db.witness_participation_rate();\n   if( prate < _required_witness_participation )\n   {\n      capture(\"pct\", uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT));\n      return block_production_condition::low_participation;\n   }\n\n   if( llabs((scheduled_time - now).count()) > fc::milliseconds( 2500 ).count() )\n   {\n      capture(\"scheduled_time\", scheduled_time)(\"now\", now);\n      return block_production_condition::lag;\n   }\n\n   auto block = db.generate_block(\n      scheduled_time,\n      scheduled_witness,\n      private_key_itr->second,\n      _production_skip_flags\n      );\n   capture(\"n\", block.block_num())(\"t\", block.timestamp)(\"c\", now)(\"x\", block.transactions.size());\n   fc::async( [this,block](){ p2p_node().broadcast(net::block_message(block)); } );\n\n   return block_production_condition::produced;\n}\n"
  },
  {
    "path": "libraries/protocol/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/protocol/*.hpp\")\n\nlist(APPEND SOURCES account.cpp\n                    assert.cpp\n                    asset_ops.cpp\n                    block.cpp\n                    confidential.cpp\n                    chain_parameters.cpp\n                    fee_schedule.cpp\n                    fee_schedule_calc.cpp\n                    memo.cpp\n                    proposal.cpp\n                    transfer.cpp\n                    vote.cpp\n                    witness.cpp\n                    address.cpp\n                    asset.cpp\n                    authority.cpp\n                    special_authority.cpp\n                    restriction.cpp\n                    custom_authority.cpp\n                    committee_member.cpp\n                    custom.cpp\n                    market.cpp\n                    liquidity_pool.cpp\n                    ticket.cpp\n                    operations.cpp\n                    pts_address.cpp\n                    small_ops.cpp\n                    transaction.cpp\n                    types.cpp\n                    withdraw_permission.cpp\n                    worker.cpp\n                    htlc.cpp)\n\n\nlist(APPEND CUSTOM_AUTHS_FILES\n     custom_authorities/create_predicate_fwd_1.cpp\n     custom_authorities/create_predicate_fwd_2.cpp\n     custom_authorities/create_predicate_fwd_3.cpp\n     custom_authorities/restriction_predicate.cpp\n     custom_authorities/list_1.cpp\n     custom_authorities/list_2.cpp\n     custom_authorities/list_3.cpp\n     custom_authorities/list_4.cpp\n     custom_authorities/list_5.cpp\n     custom_authorities/list_6.cpp\n     custom_authorities/list_7.cpp\n     custom_authorities/list_8.cpp\n     custom_authorities/list_9.cpp\n     custom_authorities/list_10.cpp\n     custom_authorities/list_11.cpp\n     custom_authorities/list_12.cpp)\n\nfile(GLOB CUSTOM_AUTHS_HEADERS \"custom_authorities/*.hxx\")\n\nadd_library( graphene_protocol_custom_auths ${CUSTOM_AUTHS_FILES} ${CUSTOM_AUTHS_HEADERS} )\ntarget_link_libraries( graphene_protocol_custom_auths fc )\ntarget_include_directories( graphene_protocol_custom_auths PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif( MSVC )\n   set_source_files_properties( ${CUSTOM_AUTHS_FILES} ${SOURCES} PROPERTIES COMPILE_FLAGS /bigobj )\nelse( MSVC )\n   if( MINGW )\n      set_source_files_properties( ${CUSTOM_AUTHS_FILES} ${SOURCES} PROPERTIES COMPILE_FLAGS -Wa,-mbig-obj )\n   endif( MINGW )\nendif( MSVC )\n\nadd_library( graphene_protocol ${SOURCES} ${HEADERS} )\ntarget_link_libraries( graphene_protocol fc graphene_protocol_custom_auths )\ntarget_include_directories( graphene_protocol PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\ninstall( TARGETS\n   graphene_protocol\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\ninstall( FILES ${HEADERS} DESTINATION \"include/graphene/protocol\" )\n"
  },
  {
    "path": "libraries/protocol/account.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/account.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\n/**\n * Names must comply with the following grammar (RFC 1035):\n * <domain> ::= <subdomain> | \" \"\n * <subdomain> ::= <label> | <subdomain> \".\" <label>\n * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]\n * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>\n * <let-dig-hyp> ::= <let-dig> | \"-\"\n * <let-dig> ::= <letter> | <digit>\n *\n * Which is equivalent to the following:\n *\n * <domain> ::= <subdomain> | \" \"\n * <subdomain> ::= <label> (\".\" <label>)*\n * <label> ::= <letter> [ [ <let-dig-hyp>+ ] <let-dig> ]\n * <let-dig-hyp> ::= <let-dig> | \"-\"\n * <let-dig> ::= <letter> | <digit>\n *\n * I.e. a valid name consists of a dot-separated sequence\n * of one or more labels consisting of the following rules:\n *\n * - Each label is three characters or more\n * - Each label begins with a letter\n * - Each label ends with a letter or digit\n * - Each label contains only letters, digits or hyphens\n *\n * In addition we require the following:\n *\n * - All letters are lowercase\n * - Length is between (inclusive) GRAPHENE_MIN_ACCOUNT_NAME_LENGTH and GRAPHENE_MAX_ACCOUNT_NAME_LENGTH\n */\nbool is_valid_name( const string& name )\n{ try {\n   const size_t len = name.size();\n\n   if( len < GRAPHENE_MIN_ACCOUNT_NAME_LENGTH )\n   {\n      return false;\n   }\n\n   if( len > GRAPHENE_MAX_ACCOUNT_NAME_LENGTH )\n   {\n      return false;\n   }\n\n   size_t begin = 0;\n   while( true )\n   {\n      size_t end = name.find_first_of( '.', begin );\n      if( end == std::string::npos )\n         end = len;\n      if( (end - begin) < GRAPHENE_MIN_ACCOUNT_NAME_LENGTH )\n      {\n         return false;\n      }\n      switch( name[begin] )\n      {\n         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':\n         case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':\n         case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\n         case 'y': case 'z':\n            break;\n         default:\n            return false;\n      }\n      switch( name[end-1] )\n      {\n         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':\n         case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':\n         case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\n         case 'y': case 'z':\n         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':\n         case '8': case '9':\n            break;\n         default:\n            return false;\n      }\n      for( size_t i=begin+1; i<end-1; i++ )\n      {\n         switch( name[i] )\n         {\n            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':\n            case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':\n            case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\n            case 'y': case 'z':\n            case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':\n            case '8': case '9':\n            case '-':\n               break;\n            default:\n               return false;\n         }\n      }\n      if( end == len )\n         break;\n      begin = end+1;\n   }\n   return true;\n} FC_CAPTURE_AND_RETHROW( (name) ) }\n\nbool is_cheap_name( const string& n )\n{\n   bool v = false;\n   for( auto c : n )\n   {\n      if( c >= '0' && c <= '9' ) return true;\n      if( c == '.' || c == '-' || c == '/' ) return true;\n      switch( c )\n      {\n         case 'a':\n         case 'e':\n         case 'i':\n         case 'o':\n         case 'u':\n         case 'y':\n            v = true;\n      }\n   }\n   if( !v )\n      return true;\n   return false;\n}\n\nvoid account_options::validate() const\n{\n   auto needed_witnesses = num_witness;\n   auto needed_committee = num_committee;\n\n   for( vote_id_type id : votes )\n      if( id.type() == vote_id_type::witness && needed_witnesses )\n         --needed_witnesses;\n      else if ( id.type() == vote_id_type::committee && needed_committee )\n         --needed_committee;\n\n   FC_ASSERT( needed_witnesses == 0 && needed_committee == 0,\n              \"May not specify fewer witnesses or committee members than the number voted for.\");\n}\n\nshare_type account_create_operation::calculate_fee( const fee_parameters_type& k )const\n{\n   auto core_fee_required = k.basic_fee;\n\n   if( !is_cheap_name(name) )\n      core_fee_required = k.premium_fee;\n\n   // Authorities and vote lists can be arbitrarily large, so charge a data fee for big ones\n   auto data_fee =  calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte ); \n   core_fee_required += data_fee;\n\n   return core_fee_required;\n}\n\nvoid account_create_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( is_valid_name( name ) );\n   FC_ASSERT( referrer_percent <= GRAPHENE_100_PERCENT );\n   FC_ASSERT( owner.num_auths() != 0 );\n   FC_ASSERT( owner.address_auths.size() == 0 );\n   FC_ASSERT( active.num_auths() != 0 );\n   FC_ASSERT( active.address_auths.size() == 0 );\n   FC_ASSERT( !owner.is_impossible(), \"cannot create an account with an impossible owner authority threshold\" );\n   FC_ASSERT( !active.is_impossible(), \"cannot create an account with an impossible active authority threshold\" );\n   options.validate();\n   if( extensions.value.owner_special_authority.valid() )\n      validate_special_authority( *extensions.value.owner_special_authority );\n   if( extensions.value.active_special_authority.valid() )\n      validate_special_authority( *extensions.value.active_special_authority );\n   if( extensions.value.buyback_options.valid() )\n   {\n      FC_ASSERT( !(extensions.value.owner_special_authority.valid()) );\n      FC_ASSERT( !(extensions.value.active_special_authority.valid()) );\n      FC_ASSERT( owner == authority::null_authority() );\n      FC_ASSERT( active == authority::null_authority() );\n      size_t n_markets = extensions.value.buyback_options->markets.size();\n      FC_ASSERT( n_markets > 0 );\n      for( const asset_id_type m : extensions.value.buyback_options->markets )\n      {\n         FC_ASSERT( m != extensions.value.buyback_options->asset_to_buy );\n      }\n   }\n}\n\nshare_type account_update_operation::calculate_fee( const fee_parameters_type& k )const\n{\n   auto core_fee_required = k.fee;  \n   if( new_options )\n      core_fee_required += calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );\n   return core_fee_required;\n}\n\nvoid account_update_operation::validate()const\n{\n   FC_ASSERT( account != GRAPHENE_TEMP_ACCOUNT );\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( account != account_id_type() );\n\n   bool has_action = (\n         owner.valid()\n      || active.valid()\n      || new_options.valid()\n      || extensions.value.owner_special_authority.valid()\n      || extensions.value.active_special_authority.valid()\n      );\n\n   FC_ASSERT( has_action );\n\n   if( owner )\n   {\n      FC_ASSERT( owner->num_auths() != 0 );\n      FC_ASSERT( owner->address_auths.size() == 0 );\n      FC_ASSERT( !owner->is_impossible(), \"cannot update an account with an impossible owner authority threshold\" );\n   }\n   if( active )\n   {\n      FC_ASSERT( active->num_auths() != 0 );\n      FC_ASSERT( active->address_auths.size() == 0 );\n      FC_ASSERT( !active->is_impossible(), \"cannot update an account with an impossible active authority threshold\" );\n   }\n\n   if( new_options )\n      new_options->validate();\n   if( extensions.value.owner_special_authority.valid() )\n      validate_special_authority( *extensions.value.owner_special_authority );\n   if( extensions.value.active_special_authority.valid() )\n      validate_special_authority( *extensions.value.active_special_authority );\n}\n\nshare_type account_upgrade_operation::calculate_fee(const fee_parameters_type& k) const\n{\n   if( upgrade_to_lifetime_member )\n      return k.membership_lifetime_fee;\n   return k.membership_annual_fee;\n}\n\nvoid account_upgrade_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n}\n\nvoid account_transfer_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_options )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation )\n"
  },
  {
    "path": "libraries/protocol/address.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/address.hpp>\n#include <graphene/protocol/pts_address.hpp>\n#include <fc/crypto/base58.hpp>\n#include <algorithm>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\n   address::address( const std::string& base58str )\n   {\n      std::string prefix( GRAPHENE_ADDRESS_PREFIX );\n      FC_ASSERT( is_valid( base58str, prefix ), \"${str}\", (\"str\",base58str) );\n\n      std::vector<char> v = fc::from_base58( base58str.substr( prefix.size() ) );\n      memcpy( (char*)addr._hash, v.data(), std::min<size_t>( v.size()-4, sizeof( addr ) ) );\n   }\n\n   bool address::is_valid( const std::string& base58str, const std::string& prefix )\n   {\n      const size_t prefix_len = prefix.size();\n      if( base58str.size() <= prefix_len )\n          return false;\n      if( base58str.substr( 0, prefix_len ) != prefix )\n          return false;\n      std::vector<char> v;\n      try\n      {\n\t\t     v = fc::from_base58( base58str.substr( prefix_len ) );\n      }\n      catch( const fc::parse_error_exception& e )\n      {\n        return false;\n      }\n\n      if( v.size() != sizeof( fc::ripemd160 ) + 4 )\n          return false;\n\n      const fc::ripemd160 checksum = fc::ripemd160::hash( v.data(), v.size() - 4 );\n      if( memcmp( v.data() + 20, (char*)checksum._hash, 4 ) != 0 )\n          return false;\n\n      return true;\n   }\n\n   address::address( const fc::ecc::public_key& pub )\n   {\n       auto dat = pub.serialize();\n       addr = fc::ripemd160::hash( fc::sha512::hash( (char*) dat.data(), dat.size() ) );\n   }\n\n   address::address( const pts_address& ptsaddr )\n   {\n       addr = fc::ripemd160::hash( (char*)&ptsaddr, sizeof( ptsaddr ) );\n   }\n\n   address::address( const fc::ecc::public_key_data& pub )\n   {\n       addr = fc::ripemd160::hash( fc::sha512::hash( (char*) pub.data(), pub.size() ) );\n   }\n\n   address::address( const graphene::protocol::public_key_type& pub )\n   {\n       addr = fc::ripemd160::hash( fc::sha512::hash( (char*) pub.key_data.data(), pub.key_data.size() ) );\n   }\n\n   address::operator std::string()const\n   {\n        char bin_addr[24];\n        static_assert( sizeof(bin_addr) >= sizeof(addr) + 4, \"address size mismatch\" );\n        memcpy( bin_addr, addr.data(), sizeof(addr) );\n        auto checksum = fc::ripemd160::hash( addr.data(), sizeof(addr) );\n        memcpy( bin_addr + sizeof(addr), (char*)&checksum._hash[0], 4 );\n        return GRAPHENE_ADDRESS_PREFIX + fc::to_base58( bin_addr, sizeof(bin_addr) );\n   }\n\n} } // namespace graphene::protocol\n\nnamespace fc\n{\n    void to_variant( const graphene::protocol::address& var,  variant& vo, uint32_t max_depth )\n    {\n        vo = std::string(var);\n    }\n    void from_variant( const variant& var,  graphene::protocol::address& vo, uint32_t max_depth )\n    {\n        vo = graphene::protocol::address( var.as_string() );\n    }\n}\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::address )\n"
  },
  {
    "path": "libraries/protocol/assert.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/account.hpp>\n#include <graphene/protocol/asset_ops.hpp>\n#include <graphene/protocol/assert.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nbool account_name_eq_lit_predicate::validate()const\n{\n   return is_valid_name( name );\n}\n\nbool asset_symbol_eq_lit_predicate::validate()const\n{\n   return is_valid_symbol( symbol );\n}\n\nstruct predicate_validator\n{\n   typedef void result_type;\n\n   template<typename T>\n   void operator()( const T& p )const\n   {\n      p.validate();\n   }\n};\n\nvoid assert_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   for( const auto& item : predicates )\n      item.visit( predicate_validator() );\n}\n\n/**\n * The fee for assert operations is proportional to their size,\n * but cheaper than a data fee because they require no storage\n */\nshare_type  assert_operation::calculate_fee(const fee_parameters_type& k)const\n{\n   return k.fee * predicates.size();\n}\n\n} }  // namespace graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::assert_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::assert_operation )\n"
  },
  {
    "path": "libraries/protocol/asset.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/asset.hpp>\n#include <boost/rational.hpp>\n#include <boost/multiprecision/cpp_int.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/uint128.hpp>\n\nnamespace graphene { namespace protocol {\n      using fc::uint128_t;\n      using fc::int128_t;\n\n      bool operator == ( const price& a, const price& b )\n      {\n         if( std::tie( a.base.asset_id, a.quote.asset_id ) != std::tie( b.base.asset_id, b.quote.asset_id ) )\n            return false;\n\n         const auto amult = uint128_t( b.quote.amount.value ) * a.base.amount.value;\n         const auto bmult = uint128_t( a.quote.amount.value ) * b.base.amount.value;\n\n         return amult == bmult;\n      }\n\n      bool operator < ( const price& a, const price& b )\n      {\n         if( a.base.asset_id < b.base.asset_id ) return true;\n         if( a.base.asset_id > b.base.asset_id ) return false;\n         if( a.quote.asset_id < b.quote.asset_id ) return true;\n         if( a.quote.asset_id > b.quote.asset_id ) return false;\n\n         const auto amult = uint128_t( b.quote.amount.value ) * a.base.amount.value;\n         const auto bmult = uint128_t( a.quote.amount.value ) * b.base.amount.value;\n\n         return amult < bmult;\n      }\n\n      asset operator * ( const asset& a, const price& b )\n      {\n         if( a.asset_id == b.base.asset_id )\n         {\n            FC_ASSERT( b.base.amount.value > 0 );\n            uint128_t result = (uint128_t(a.amount.value) * b.quote.amount.value)/b.base.amount.value;\n            FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );\n            return asset( static_cast<int64_t>(result), b.quote.asset_id );\n         }\n         else if( a.asset_id == b.quote.asset_id )\n         {\n            FC_ASSERT( b.quote.amount.value > 0 );\n            uint128_t result = (uint128_t(a.amount.value) * b.base.amount.value)/b.quote.amount.value;\n            FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );\n            return asset( static_cast<int64_t>(result), b.base.asset_id );\n         }\n         FC_THROW_EXCEPTION( fc::assert_exception, \"invalid asset * price\", (\"asset\",a)(\"price\",b) );\n      }\n\n      asset asset::multiply_and_round_up( const price& b )const\n      {\n         const asset& a = *this;\n         if( a.asset_id == b.base.asset_id )\n         {\n            FC_ASSERT( b.base.amount.value > 0 );\n            uint128_t result = (uint128_t(a.amount.value) * b.quote.amount.value + b.base.amount.value - 1)/b.base.amount.value;\n            FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );\n            return asset( static_cast<int64_t>(result), b.quote.asset_id );\n         }\n         else if( a.asset_id == b.quote.asset_id )\n         {\n            FC_ASSERT( b.quote.amount.value > 0 );\n            uint128_t result = (uint128_t(a.amount.value) * b.base.amount.value + b.quote.amount.value - 1)/b.quote.amount.value;\n            FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );\n            return asset( static_cast<int64_t>(result), b.base.asset_id );\n         }\n         FC_THROW_EXCEPTION( fc::assert_exception, \"invalid asset::multiply_and_round_up(price)\", (\"asset\",a)(\"price\",b) );\n      }\n\n      price operator / ( const asset& base, const asset& quote )\n      { try {\n         FC_ASSERT( base.asset_id != quote.asset_id );\n         return price{base,quote};\n      } FC_CAPTURE_AND_RETHROW( (base)(quote) ) }\n\n      price price::max( asset_id_type base, asset_id_type quote ) { return asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), base ) / asset( share_type(1), quote); }\n      price price::min( asset_id_type base, asset_id_type quote ) { return asset( 1, base ) / asset( GRAPHENE_MAX_SHARE_SUPPLY, quote); }\n\n      price operator *  ( const price& p, const ratio_type& r )\n      { try {\n         p.validate();\n\n         FC_ASSERT( r.numerator() > 0 && r.denominator() > 0 );\n\n         if( r.numerator() == r.denominator() ) return p;\n\n         boost::rational<int128_t> p128( p.base.amount.value, p.quote.amount.value );\n         boost::rational<int128_t> r128( r.numerator(), r.denominator() );\n         auto cp = p128 * r128;\n         auto ocp = cp;\n\n         bool shrinked = false;\n         bool using_max = false;\n         static const int128_t max( GRAPHENE_MAX_SHARE_SUPPLY );\n         while( cp.numerator() > max || cp.denominator() > max )\n         {\n            if( cp.numerator() == 1 )\n            {\n               cp = boost::rational<int128_t>( 1, max );\n               using_max = true;\n               break;\n            }\n            else if( cp.denominator() == 1 )\n            {\n               cp = boost::rational<int128_t>( max, 1 );\n               using_max = true;\n               break;\n            }\n            else\n            {\n               cp = boost::rational<int128_t>( cp.numerator() >> 1, cp.denominator() >> 1 );\n               shrinked = true;\n            }\n         }\n         if( shrinked ) // maybe not accurate enough due to rounding, do additional checks here\n         {\n            int128_t num = ocp.numerator();\n            int128_t den = ocp.denominator();\n            if( num > den )\n            {\n               num /= den;\n               if( num > max )\n                  num = max;\n               den = 1;\n            }\n            else\n            {\n               den /= num;\n               if( den > max )\n                  den = max;\n               num = 1;\n            }\n            boost::rational<int128_t> ncp( num, den );\n            if( num == max || den == max ) // it's on the edge, we know it's accurate enough\n               cp = ncp;\n            else\n            {\n               // from the accurate ocp, now we have ncp and cp. use the one which is closer to ocp.\n               // TODO improve performance\n               auto diff1 = abs( ncp - ocp );\n               auto diff2 = abs( cp - ocp );\n               if( diff1 < diff2 ) cp = ncp;\n            }\n         }\n\n         price np = asset( static_cast<int64_t>(cp.numerator()), p.base.asset_id )\n                  / asset( static_cast<int64_t>(cp.denominator()), p.quote.asset_id );\n\n         if( shrinked || using_max )\n         {\n            if( ( r.numerator() > r.denominator() && np < p )\n                  || ( r.numerator() < r.denominator() && np > p ) )\n               // even with an accurate result, if p is out of valid range, return it\n               np = p;\n         }\n\n         np.validate();\n         return np;\n      } FC_CAPTURE_AND_RETHROW( (p)(r.numerator())(r.denominator()) ) }\n\n      price operator /  ( const price& p, const ratio_type& r )\n      { try {\n         return p * ratio_type( r.denominator(), r.numerator() );\n      } FC_CAPTURE_AND_RETHROW( (p)(r.numerator())(r.denominator()) ) }\n\n      /**\n       *  The black swan price is defined as debt/collateral, we want to perform a margin call\n       *  before debt == collateral.   Given a debt/collateral ratio of 1 USD / CORE and\n       *  a maintenance collateral requirement of 2x we can define the call price to be\n       *  2 USD / CORE.\n       *\n       *  This method divides the collateral by the maintenance collateral ratio to derive\n       *  a call price for the given black swan ratio.\n       *\n       *  There exists some cases where the debt and collateral values are so small that\n       *  dividing by the collateral ratio will result in a 0 price or really poor\n       *  rounding errors.   No matter what the collateral part of the price ratio can\n       *  never go to 0 and the debt can never go more than GRAPHENE_MAX_SHARE_SUPPLY\n       *\n       *  CR * DEBT/COLLAT or DEBT/(COLLAT/CR)\n       *\n       *  Note: this function is only used before core-1270 hard fork.\n       */\n      price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio)\n      { try {\n         boost::rational<int128_t> swan(debt.amount.value,collateral.amount.value);\n         boost::rational<int128_t> ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );\n         auto cp = swan * ratio;\n\n         while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )\n            cp = boost::rational<int128_t>( (cp.numerator() >> 1)+1, (cp.denominator() >> 1)+1 );\n\n         return  (  asset( static_cast<int64_t>(cp.denominator()), collateral.asset_id )\n                  / asset( static_cast<int64_t>(cp.numerator()), debt.asset_id ) );\n      } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) }\n\n      bool price::is_null() const\n      {\n         // Effectively same as \"return *this == price();\" but perhaps faster\n         return ( base.asset_id == asset_id_type() && quote.asset_id == asset_id_type() );\n      }\n\n      void price::validate() const\n      { try {\n         FC_ASSERT( base.amount > share_type(0) );\n         FC_ASSERT( quote.amount > share_type(0) );\n         FC_ASSERT( base.asset_id != quote.asset_id );\n      } FC_CAPTURE_AND_RETHROW( (base)(quote) ) }\n\n      void price_feed::validate() const\n      { try {\n         if( !settlement_price.is_null() )\n            settlement_price.validate();\n         FC_ASSERT( maximum_short_squeeze_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );\n         FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n         FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );\n         FC_ASSERT( maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n         // Note: there was code here calling `max_short_squeeze_price();` before core-1270 hard fork,\n         //       in order to make sure that it doesn't overflow,\n         //       but the code doesn't actually check overflow, and it won't overflow, so the code is removed.\n\n         // Note: not checking `maintenance_collateral_ratio >= maximum_short_squeeze_ratio` since launch\n      } FC_CAPTURE_AND_RETHROW( (*this) ) }\n\n      bool price_feed::is_for( asset_id_type asset_id ) const\n      {\n         try\n         {\n            if( !settlement_price.is_null() )\n               return (settlement_price.base.asset_id == asset_id);\n            if( !core_exchange_rate.is_null() )\n               return (core_exchange_rate.base.asset_id == asset_id);\n            // (null, null) is valid for any feed\n            return true;\n         }\n         FC_CAPTURE_AND_RETHROW( (*this) )\n      }\n\n      // This function is kept here due to potential different behavior in edge cases.\n      // TODO check after core-1270 hard fork to see if we can safely remove it\n      price price_feed::max_short_squeeze_price_before_hf_1270()const\n      {\n         // settlement price is in debt/collateral\n         boost::rational<int128_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value );\n         boost::rational<int128_t> ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );\n         auto cp = sp * ratio;\n\n         while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )\n            cp = boost::rational<int128_t>( (cp.numerator() >> 1)+(cp.numerator()&1),\n                                            (cp.denominator() >> 1)+(cp.denominator()&1) );\n\n         return (  asset( static_cast<int64_t>(cp.numerator()), settlement_price.base.asset_id )\n                 / asset( static_cast<int64_t>(cp.denominator()), settlement_price.quote.asset_id ) );\n      }\n\n\n      // Documentation in header.\n      // Calculation:  MSSP = settlement_price / MSSR\n      price price_feed::max_short_squeeze_price()const\n      {\n         // settlement price is in debt/collateral\n         return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );\n      }\n\n      // Documentation in header.\n      // Calculation:  MCOP = settlement_price / (MSSR - MCFR); result is in debt/collateral\n      price price_feed::margin_call_order_price(const fc::optional<uint16_t> maybe_mcfr)const\n      {\n         const uint16_t mcfr = maybe_mcfr.valid() ? *maybe_mcfr : 0;\n         uint16_t numerator = (mcfr < maximum_short_squeeze_ratio) ?\n            (maximum_short_squeeze_ratio - mcfr) : GRAPHENE_COLLATERAL_RATIO_DENOM; // won't underflow\n         if (numerator < GRAPHENE_COLLATERAL_RATIO_DENOM)\n            numerator = GRAPHENE_COLLATERAL_RATIO_DENOM; // floor at 1.00\n         return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, numerator );\n      }\n\n      // Reason for this function is explained in header.\n      // Calculation: (MSSR - MCFR) / MSSR\n      ratio_type price_feed::margin_call_pays_ratio(const fc::optional<uint16_t> maybe_mcfr)const\n      {\n         if (!maybe_mcfr.valid())\n            return ratio_type(1,1);\n         const uint16_t mcfr = *maybe_mcfr;\n         uint16_t numerator = (mcfr < maximum_short_squeeze_ratio) ?\n            (maximum_short_squeeze_ratio - mcfr) : GRAPHENE_COLLATERAL_RATIO_DENOM; // won't underflow\n         if (numerator < GRAPHENE_COLLATERAL_RATIO_DENOM)\n            numerator = GRAPHENE_COLLATERAL_RATIO_DENOM; // floor at 1.00\n         return ratio_type( numerator, maximum_short_squeeze_ratio );\n         // Note: This ratio, if it multiplied margin_call_order_price, would yield the\n         // max_short_squeeze_price, apart perhaps for truncation (rounding) error.\n      }\n\n      price price_feed::maintenance_collateralization()const\n      {\n         if( settlement_price.is_null() )\n            return price();\n         return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );\n      }\n\n// compile-time table of powers of 10 using template metaprogramming\n\ntemplate< int N >\nstruct p10\n{\n   static const int64_t v = 10 * p10<N-1>::v;\n};\n\ntemplate<>\nstruct p10<0>\n{\n   static const int64_t v = 1;\n};\n\nconst int64_t scaled_precision_lut[19] =\n{\n   p10<  0 >::v, p10<  1 >::v, p10<  2 >::v, p10<  3 >::v,\n   p10<  4 >::v, p10<  5 >::v, p10<  6 >::v, p10<  7 >::v,\n   p10<  8 >::v, p10<  9 >::v, p10< 10 >::v, p10< 11 >::v,\n   p10< 12 >::v, p10< 13 >::v, p10< 14 >::v, p10< 15 >::v,\n   p10< 16 >::v, p10< 17 >::v, p10< 18 >::v\n};\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::price )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::price_feed )\n"
  },
  {
    "path": "libraries/protocol/asset_ops.cpp",
    "content": "/*\n * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/asset_ops.hpp>\n\n#include <fc/io/raw.hpp>\n\n#include <locale>\n\nnamespace graphene { namespace protocol {\n\n/**\n *  Valid symbols can contain [A-Z0-9], and '.'\n *  They must start with [A, Z]\n *  They must end with [A, Z] before HF_620 or [A-Z0-9] after it\n *  They can contain a maximum of one '.'\n */\nbool is_valid_symbol( const string& symbol )\n{\n    static const std::locale& loc = std::locale::classic();\n    if( symbol.size() < GRAPHENE_MIN_ASSET_SYMBOL_LENGTH )\n        return false;\n\n    if( symbol.substr(0,3) == \"BIT\" )\n        return false;\n\n    if( symbol.size() > GRAPHENE_MAX_ASSET_SYMBOL_LENGTH )\n        return false;\n\n    if( !isalpha( symbol.front(), loc ) )\n        return false;\n\n    if( !isalnum( symbol.back(), loc ) )\n        return false;\n\n    bool dot_already_present = false;\n    for( const auto c : symbol )\n    {\n        if( (isalpha( c, loc ) && isupper( c, loc )) || isdigit( c, loc ) )\n            continue;\n\n        if( c == '.' )\n        {\n            if( dot_already_present )\n                return false;\n\n            dot_already_present = true;\n            continue;\n        }\n\n        return false;\n    }\n\n    return true;\n}\n\nshare_type asset_issue_operation::calculate_fee(const fee_parameters_type& k)const\n{\n   return k.fee + calculate_data_fee( fc::raw::pack_size(memo), k.price_per_kbyte );\n}\n\nshare_type asset_create_operation::calculate_fee( const asset_create_operation::fee_parameters_type& param,\n                                                  optional<uint64_t> sub_asset_creation_fee )const\n{\n   share_type core_fee_required = param.long_symbol;\n\n   if( sub_asset_creation_fee.valid() && symbol.find('.') != std::string::npos )\n   {\n      core_fee_required = *sub_asset_creation_fee;\n   }\n   else\n   {\n      switch( symbol.size() )\n      {\n      case 3: core_fee_required = param.symbol3;\n          break;\n      case 4: core_fee_required = param.symbol4;\n          break;\n      default:\n          break;\n      }\n   }\n\n   // common_options contains several lists and a string. Charge fees for its size\n   core_fee_required += calculate_data_fee( fc::raw::pack_size(*this), param.price_per_kbyte );\n\n   return core_fee_required;\n}\n\nvoid  asset_create_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( is_valid_symbol(symbol) );\n   common_options.validate();\n   if( common_options.issuer_permissions\n         & (disable_force_settle|global_settle|disable_mcr_update|disable_icr_update|disable_mssr_update) )\n      FC_ASSERT( bitasset_opts.valid() );\n   if( is_prediction_market )\n   {\n      FC_ASSERT( bitasset_opts.valid(), \"Cannot have a User-Issued Asset implement a prediction market.\" );\n      FC_ASSERT( common_options.issuer_permissions & global_settle );\n   }\n   if( bitasset_opts ) bitasset_opts->validate();\n\n   asset dummy = asset(1) * common_options.core_exchange_rate;\n   FC_ASSERT(dummy.asset_id == asset_id_type(1));\n   FC_ASSERT(precision <= 12);\n}\n\nvoid asset_update_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   if( new_issuer )\n      FC_ASSERT(issuer != *new_issuer);\n   new_options.validate();\n\n   asset dummy = asset(1, asset_to_update) * new_options.core_exchange_rate;\n   FC_ASSERT(dummy.asset_id == asset_id_type());\n\n   if( extensions.value.new_precision.valid() )\n      FC_ASSERT( *extensions.value.new_precision <= 12 );\n\n   if( extensions.value.skip_core_exchange_rate.valid() )\n   {\n      FC_ASSERT( *extensions.value.skip_core_exchange_rate == true,\n                 \"If skip_core_exchange_rate is specified, it can only be true\" );\n   }\n}\n\nvoid asset_update_issuer_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( issuer != new_issuer );\n}\n\nshare_type asset_update_operation::calculate_fee(const asset_update_operation::fee_parameters_type& k)const\n{\n   return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );\n}\n\n\nvoid asset_publish_feed_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   feed.validate();\n\n   // maybe some of these could be moved to feed.validate()\n   if( !feed.core_exchange_rate.is_null() )\n   {\n      feed.core_exchange_rate.validate();\n   }\n   if( (!feed.settlement_price.is_null()) && (!feed.core_exchange_rate.is_null()) )\n   {\n      FC_ASSERT( feed.settlement_price.base.asset_id == feed.core_exchange_rate.base.asset_id );\n   }\n\n   FC_ASSERT( !feed.settlement_price.is_null() );\n   FC_ASSERT( !feed.core_exchange_rate.is_null() );\n   FC_ASSERT( feed.is_for( asset_id ) );\n\n   if( extensions.value.initial_collateral_ratio.valid() )\n   {\n      FC_ASSERT( *extensions.value.initial_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );\n      FC_ASSERT( *extensions.value.initial_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n   }\n}\n\nvoid asset_reserve_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( amount_to_reserve.amount.value <= GRAPHENE_MAX_SHARE_SUPPLY );\n   FC_ASSERT( amount_to_reserve.amount.value > 0 );\n}\n\nvoid asset_issue_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( asset_to_issue.amount.value <= GRAPHENE_MAX_SHARE_SUPPLY );\n   FC_ASSERT( asset_to_issue.amount.value > 0 );\n   FC_ASSERT( asset_to_issue.asset_id != asset_id_type(0) );\n}\n\nvoid asset_fund_fee_pool_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( fee.asset_id == asset_id_type() );\n   FC_ASSERT( amount > 0 );\n}\n\nvoid asset_settle_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( amount.amount >= 0 );\n}\n\nvoid asset_update_bitasset_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   new_options.validate();\n}\n\nvoid asset_update_feed_producers_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n}\n\nvoid asset_global_settle_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( asset_to_settle == settle_price.base.asset_id );\n}\n\nvoid bitasset_options::validate() const\n{\n   FC_ASSERT(minimum_feeds > 0);\n   FC_ASSERT(force_settlement_offset_percent <= GRAPHENE_100_PERCENT);\n   FC_ASSERT(maximum_force_settlement_volume <= GRAPHENE_100_PERCENT);\n\n   if( extensions.value.margin_call_fee_ratio.valid() )\n      FC_ASSERT( *extensions.value.margin_call_fee_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n\n   if( extensions.value.initial_collateral_ratio.valid() )\n   {\n      FC_ASSERT( *extensions.value.initial_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );\n      FC_ASSERT( *extensions.value.initial_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n   }\n   if( extensions.value.maintenance_collateral_ratio.valid() )\n   {\n      FC_ASSERT( *extensions.value.maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );\n      FC_ASSERT( *extensions.value.maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n   }\n   if( extensions.value.maximum_short_squeeze_ratio.valid() )\n   {\n      FC_ASSERT( *extensions.value.maximum_short_squeeze_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );\n      FC_ASSERT( *extensions.value.maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );\n   }\n\n   if( extensions.value.force_settle_fee_percent.valid() )\n      FC_ASSERT( *extensions.value.force_settle_fee_percent <= GRAPHENE_100_PERCENT );\n\n}\n\nvoid asset_options::validate()const\n{\n   FC_ASSERT( max_supply > 0 );\n   FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY );\n   // The non-negative maker fee must be less than or equal to 100%\n   FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT );\n\n   // The non-negative taker fee must be less than or equal to 100%\n   if( extensions.value.taker_fee_percent.valid() )\n      FC_ASSERT( *extensions.value.taker_fee_percent <= GRAPHENE_100_PERCENT );\n\n   FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY );\n   // There must be no high bits in permissions whose meaning is not known.\n   FC_ASSERT( !(issuer_permissions & ~ASSET_ISSUER_PERMISSION_MASK) );\n   // The permission-only bits can not be set in flag\n   FC_ASSERT( !(flags & global_settle),\n              \"Can not set global_settle flag, it is for issuer permission only\" );\n\n   // the witness_fed and committee_fed flags cannot be set simultaneously\n   FC_ASSERT( (flags & (witness_fed_asset | committee_fed_asset)) != (witness_fed_asset | committee_fed_asset) );\n   core_exchange_rate.validate();\n   FC_ASSERT( core_exchange_rate.base.asset_id.instance.value == 0 ||\n              core_exchange_rate.quote.asset_id.instance.value == 0 );\n\n   if(!whitelist_authorities.empty() || !blacklist_authorities.empty())\n      FC_ASSERT( flags & white_list );\n   for( auto item : whitelist_markets )\n   {\n      FC_ASSERT( blacklist_markets.find(item) == blacklist_markets.end() );\n   }\n   for( auto item : blacklist_markets )\n   {\n      FC_ASSERT( whitelist_markets.find(item) == whitelist_markets.end() );\n   }\n   if( extensions.value.reward_percent.valid() )\n      FC_ASSERT( *extensions.value.reward_percent <= GRAPHENE_100_PERCENT );\n}\n\nvoid asset_options::validate_flags( bool is_market_issued )const\n{\n   FC_ASSERT( !(flags & ~ASSET_ISSUER_PERMISSION_MASK),\n              \"Can not set an unknown bit in flags\" );\n   // Note: global_settle is checked in validate(), so do not check again here\n   FC_ASSERT( !(flags & disable_mcr_update),\n              \"Can not set disable_mcr_update flag, it is for issuer permission only\" );\n   FC_ASSERT( !(flags & disable_icr_update),\n              \"Can not set disable_icr_update flag, it is for issuer permission only\" );\n   FC_ASSERT( !(flags & disable_mssr_update),\n              \"Can not set disable_mssr_update flag, it is for issuer permission only\" );\n   if( !is_market_issued )\n   {\n      FC_ASSERT( !(flags & ~UIA_ASSET_ISSUER_PERMISSION_MASK),\n                 \"Can not set a flag for bitassets only to UIA\" );\n   }\n}\n\nuint16_t asset_options::get_enabled_issuer_permissions_mask() const\n{\n   return ( (issuer_permissions & ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK)\n          | (~issuer_permissions & ASSET_ISSUER_PERMISSION_DISABLE_BITS_MASK) );\n}\n\nvoid asset_claim_fees_operation::validate()const {\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( amount_to_claim.amount > 0 );\n   if( extensions.value.claim_from_asset_id.valid() )\n     FC_ASSERT( *extensions.value.claim_from_asset_id != amount_to_claim.asset_id );\n}\n\nvoid asset_claim_pool_operation::validate()const {\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( fee.asset_id != asset_id);\n   FC_ASSERT( amount_to_claim.amount > 0 );\n   FC_ASSERT( amount_to_claim.asset_id == asset_id_type());\n}\n\n} } // namespace graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_options )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::bitasset_options::ext )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::bitasset_options )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::additional_asset_options )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::ext )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_publish_feed_operation::ext )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_global_settle_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::additional_options_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_feed_producers_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_publish_feed_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_issue_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_reserve_operation::fee_parameters_type )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_global_settle_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_cancel_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_feed_producers_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_publish_feed_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_issue_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_reserve_operation )\n"
  },
  {
    "path": "libraries/protocol/authority.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/authority.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid add_authority_accounts(\n   flat_set<account_id_type>& result,\n   const authority& a\n   )\n{\n   for( auto& item : a.account_auths )\n      result.insert( item.first );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::authority )\n"
  },
  {
    "path": "libraries/protocol/block.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <boost/endian/conversion.hpp>\n#include <graphene/protocol/block.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <fc/io/raw.hpp>\n#include <algorithm>\n\nnamespace graphene { namespace protocol {\n   digest_type block_header::digest()const\n   {\n      return digest_type::hash(*this);\n   }\n\n   uint32_t block_header::num_from_id(const block_id_type& id)\n   {\n      return boost::endian::endian_reverse(id._hash[0].value());\n   }\n\n   const block_id_type& signed_block_header::id()const\n   {\n      if( !_block_id._hash[0].value() )\n      {\n         auto tmp = fc::sha224::hash( *this );\n         tmp._hash[0] = boost::endian::endian_reverse(block_num()); // store the block num in the ID, 160 bits is plenty for the hash\n         static_assert( sizeof(tmp._hash[0]) == 4, \"should be 4 bytes\" );\n         memcpy(_block_id._hash, tmp._hash, std::min(sizeof(_block_id), sizeof(tmp)));\n      }\n      return _block_id;\n   }\n\n   const fc::ecc::public_key& signed_block_header::signee()const\n   {\n      if( !_signee.valid() )\n         _signee = fc::ecc::public_key( witness_signature, digest(), true/*enforce canonical*/ );\n      return _signee;\n   }\n\n   void signed_block_header::sign( const fc::ecc::private_key& signer )\n   {\n      witness_signature = signer.sign_compact( digest() );\n   }\n\n   bool signed_block_header::validate_signee( const fc::ecc::public_key& expected_signee )const\n   {\n      return signee() == expected_signee;\n   }\n\n   const checksum_type& signed_block::calculate_merkle_root()const\n   {\n      static const checksum_type empty_checksum;\n      if( transactions.size() == 0 ) \n         return empty_checksum;\n\n      if( !_calculated_merkle_root._hash[0].value() )\n      {\n         vector<digest_type> ids;\n         ids.resize( transactions.size() );\n         for( uint32_t i = 0; i < transactions.size(); ++i )\n            ids[i] = transactions[i].merkle_digest();\n\n         vector<digest_type>::size_type current_number_of_hashes = ids.size();\n         while( current_number_of_hashes > 1 )\n         {\n            // hash ID's in pairs\n            uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1);\n            uint32_t k = 0;\n\n            for( uint32_t i = 0; i < i_max; i += 2 )\n               ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) );\n\n            if( current_number_of_hashes&1 )\n               ids[k++] = ids[i_max];\n            current_number_of_hashes = k;\n         }\n         _calculated_merkle_root = checksum_type::hash( ids[0] );\n      }\n      return _calculated_merkle_root;\n   }\n} }\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::block_header)\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::signed_block_header)\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::signed_block)\n"
  },
  {
    "path": "libraries/protocol/chain_parameters.cpp",
    "content": "#include <graphene/protocol/chain_parameters.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n   chain_parameters::chain_parameters() {\n       current_fees = std::make_shared<fee_schedule>();\n   }\n\n   // copy constructor\n   chain_parameters::chain_parameters(const chain_parameters& other)\n   {\n      current_fees = std::make_shared<fee_schedule>(*other.current_fees);\n      safe_copy(*this, other);\n   }\n\n   // copy assignment\n   chain_parameters& chain_parameters::operator=(const chain_parameters& other)\n   {\n      if (&other != this)\n      {\n         current_fees = std::make_shared<fee_schedule>(*other.current_fees);\n         safe_copy(*this, other);\n      }\n      return *this;\n   }\n\n   // copies the easy stuff\n   void chain_parameters::safe_copy(chain_parameters& to, const chain_parameters& from)\n   {\n      to.block_interval = from.block_interval;\n      to.maintenance_interval = from.maintenance_interval;\n      to.maintenance_skip_slots = from.maintenance_skip_slots;\n      to.committee_proposal_review_period = from.committee_proposal_review_period;\n      to.maximum_transaction_size = from.maximum_transaction_size;\n      to.maximum_block_size = from.maximum_block_size;\n      to.maximum_time_until_expiration = from.maximum_time_until_expiration;\n      to.maximum_proposal_lifetime = from.maximum_proposal_lifetime;\n      to.maximum_asset_whitelist_authorities = from.maximum_asset_whitelist_authorities;\n      to.maximum_asset_feed_publishers = from.maximum_asset_feed_publishers;\n      to.maximum_witness_count = from.maximum_witness_count;\n      to.maximum_committee_count = from.maximum_committee_count;\n      to.maximum_authority_membership = from.maximum_authority_membership;\n      to.reserve_percent_of_fee = from.reserve_percent_of_fee;\n      to.network_percent_of_fee = from.network_percent_of_fee;\n      to.lifetime_referrer_percent_of_fee = from.lifetime_referrer_percent_of_fee;\n      to.cashback_vesting_period_seconds = from.cashback_vesting_period_seconds;\n      to.cashback_vesting_threshold = from.cashback_vesting_threshold;\n      to.count_non_member_votes = from.count_non_member_votes;\n      to.allow_non_member_whitelists = from.allow_non_member_whitelists;\n      to.witness_pay_per_block = from.witness_pay_per_block;\n      to.witness_pay_vesting_seconds = from.witness_pay_vesting_seconds;\n      to.worker_budget_per_day = from.worker_budget_per_day;\n      to.max_predicate_opcode = from.max_predicate_opcode;\n      to.fee_liquidation_threshold = from.fee_liquidation_threshold;\n      to.accounts_per_fee_scale = from.accounts_per_fee_scale;\n      to.account_fee_scale_bitshifts = from.account_fee_scale_bitshifts;\n      to.max_authority_depth = from.max_authority_depth;\n      to.extensions = from.extensions;\n   }\n\n   // move constructor\n   chain_parameters::chain_parameters(chain_parameters&& other)\n   {\n      current_fees = std::move(other.current_fees);\n      safe_copy(*this, other);\n   }\n\n   // move assignment\n   chain_parameters& chain_parameters::operator=(chain_parameters&& other)\n   {\n      if (&other != this)\n      {\n         current_fees = std::move(other.current_fees);\n         safe_copy(*this, other);\n      }\n      return *this;\n   }\n\n   void chain_parameters::validate()const\n   {\n      get_current_fees().validate();\n      FC_ASSERT( reserve_percent_of_fee <= GRAPHENE_100_PERCENT );\n      FC_ASSERT( network_percent_of_fee <= GRAPHENE_100_PERCENT );\n      FC_ASSERT( lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );\n      FC_ASSERT( network_percent_of_fee + lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );\n\n      FC_ASSERT( block_interval >= GRAPHENE_MIN_BLOCK_INTERVAL );\n      FC_ASSERT( block_interval <= GRAPHENE_MAX_BLOCK_INTERVAL );\n      FC_ASSERT( block_interval > 0 );\n      FC_ASSERT( maintenance_interval > block_interval,\n                 \"Maintenance interval must be longer than block interval\" );\n      FC_ASSERT( maintenance_interval % block_interval == 0,\n                 \"Maintenance interval must be a multiple of block interval\" );\n      FC_ASSERT( maximum_transaction_size >= GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT,\n                 \"Transaction size limit is too low\" );\n      FC_ASSERT( maximum_block_size >= GRAPHENE_MIN_BLOCK_SIZE_LIMIT,\n                 \"Block size limit is too low\" );\n      FC_ASSERT( maximum_time_until_expiration > block_interval,\n                 \"Maximum transaction expiration time must be greater than a block interval\" );\n      FC_ASSERT( maximum_proposal_lifetime - committee_proposal_review_period > block_interval,\n                 \"Committee proposal review period must be less than the maximum proposal lifetime\" );\n      if( extensions.value.market_fee_network_percent.valid() )\n      {\n         FC_ASSERT( *extensions.value.market_fee_network_percent <= 3000, // GRAPHENE_100_PERCENT is 10000\n                    \"The market_fee_network_percent parameter can not exceed 30%\" );\n      }\n      if( extensions.value.maker_fee_discount_percent.valid() )\n      {\n         FC_ASSERT( *extensions.value.maker_fee_discount_percent <= GRAPHENE_100_PERCENT,\n                    \"The maker_fee_discount_percent parameter can not exceed 100%\" );\n      }\n   }\n\n   uint16_t chain_parameters::get_market_fee_network_percent() const\n   {\n      return extensions.value.market_fee_network_percent.valid() ?\n                *extensions.value.market_fee_network_percent : 0;\n   }\n\n   uint16_t chain_parameters::get_maker_fee_discount_percent() const\n   {\n      return extensions.value.maker_fee_discount_percent.valid() ?\n                *extensions.value.maker_fee_discount_percent : 0;\n   }\n\n}}\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::chain_parameters )\n"
  },
  {
    "path": "libraries/protocol/committee_member.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/committee_member.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid committee_member_create_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH );\n}\n\nvoid committee_member_update_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   if( new_url.valid() )\n      FC_ASSERT(new_url->size() < GRAPHENE_MAX_URL_LENGTH );\n}\n\nvoid committee_member_update_global_parameters_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   new_parameters.validate();\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_global_parameters_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_global_parameters_operation )\n"
  },
  {
    "path": "libraries/protocol/confidential.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/confidential.hpp>\n\n#include <fc/crypto/base58.hpp>\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid transfer_to_blind_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( amount.amount > 0 );\n\n   vector<commitment_type> in;\n   vector<commitment_type> out(outputs.size());\n   int64_t                 net_public = amount.amount.value;\n   for( uint32_t i = 0; i < out.size(); ++i )\n   {\n      out[i] = outputs[i].commitment;\n      /// require all outputs to be sorted prevents duplicates AND prevents implementations\n      /// from accidentally leaking information by how they arrange commitments.\n      if( i > 0 ) FC_ASSERT( out[i-1] < out[i], \"all outputs must be sorted by commitment id\" );\n      FC_ASSERT( !outputs[i].owner.is_impossible() );\n   }\n   FC_ASSERT( out.size(), \"there must be at least one output\" );\n\n   auto public_c = fc::ecc::blind(blinding_factor,net_public);\n\n   FC_ASSERT( fc::ecc::verify_sum( {public_c}, out, 0 ), \"\", (\"net_public\",net_public) );\n\n   if( outputs.size() > 1 )\n   {\n      for( auto out : outputs )\n      {\n         auto info = fc::ecc::range_get_info( out.range_proof );\n         FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );\n      }\n   }\n}\n\nshare_type transfer_to_blind_operation::calculate_fee( const fee_parameters_type& k )const\n{\n    return k.fee + outputs.size() * k.price_per_output;\n}\n\n\nvoid transfer_from_blind_operation::validate()const\n{\n   FC_ASSERT( amount.amount > 0 );\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( inputs.size() > 0 );\n   FC_ASSERT( amount.asset_id == fee.asset_id );\n\n\n   vector<commitment_type> in(inputs.size());\n   vector<commitment_type> out;\n   int64_t                 net_public = fee.amount.value + amount.amount.value;\n   out.push_back( fc::ecc::blind( blinding_factor, net_public ) );\n   for( uint32_t i = 0; i < in.size(); ++i )\n   {\n      in[i] = inputs[i].commitment;\n      /// by requiring all inputs to be sorted we also prevent duplicate commitments on the input\n      if( i > 0 ) FC_ASSERT( in[i-1] < in[i], \"all inputs must be sorted by commitment id\" );\n   }\n   FC_ASSERT( in.size(), \"there must be at least one input\" );\n   FC_ASSERT( fc::ecc::verify_sum( in, out, 0 ) );\n}\n\n\n/**\n *  If fee_payer = temp_account_id, then the fee is paid by the surplus balance of inputs-outputs and\n *  100% of the fee goes to the network.\n */\naccount_id_type blind_transfer_operation::fee_payer()const\n{\n   return GRAPHENE_TEMP_ACCOUNT;\n}\n\n\n/**\n *  This method can be computationally intensive because it verifies that input commitments - output commitments add up to 0\n */\nvoid blind_transfer_operation::validate()const\n{ try {\n   vector<commitment_type> in(inputs.size());\n   vector<commitment_type> out(outputs.size());\n   int64_t                 net_public = fee.amount.value;//from_amount.value - to_amount.value;\n   for( uint32_t i = 0; i < in.size(); ++i )\n   {\n      in[i] = inputs[i].commitment;\n      /// by requiring all inputs to be sorted we also prevent duplicate commitments on the input\n      if( i > 0 ) FC_ASSERT( in[i-1] < in[i] );\n   }\n   for( uint32_t i = 0; i < out.size(); ++i )\n   {\n      out[i] = outputs[i].commitment;\n      if( i > 0 ) FC_ASSERT( out[i-1] < out[i] );\n      FC_ASSERT( !outputs[i].owner.is_impossible() );\n   }\n   FC_ASSERT( in.size(), \"there must be at least one input\" );\n   FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), \"\", (\"net_public\", net_public) );\n\n   if( outputs.size() > 1 )\n   {\n      for( auto out : outputs )\n      {\n         auto info = fc::ecc::range_get_info( out.range_proof );\n         FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );\n      }\n   }\n   FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), \"\", (\"net_public\", net_public) );\n} FC_CAPTURE_AND_RETHROW( (*this) ) }\n\nshare_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k )const\n{\n    return k.fee + outputs.size() * k.price_per_output;\n}\n\n/**\n *  Packs *this then encodes as base58 encoded string.\n */\nstealth_confirmation::operator string()const\n{\n   return fc::to_base58( fc::raw::pack( *this ) );\n}\n/**\n * Unpacks from a base58 string\n */\nstealth_confirmation::stealth_confirmation( const std::string& base58 )\n{\n   *this = fc::raw::unpack<stealth_confirmation>( fc::from_base58( base58 ) );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_to_blind_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_from_blind_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::blind_transfer_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_to_blind_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_from_blind_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::blind_transfer_operation )\n"
  },
  {
    "path": "libraries/protocol/custom.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/custom.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid custom_operation::validate()const\n{\n   FC_ASSERT( fee.amount > 0 );\n}\nshare_type custom_operation::calculate_fee(const fee_parameters_type& k)const\n{\n   return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );\n}\n\n} }\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::custom_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::custom_operation )\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/BOOST_LICENSE_1_0.txt",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/create_predicate_fwd.hxx",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n/* This file contains forward declarations for externalized specializations of the create_predicate_function\n * template. Generate using the following shell code, and paste below.\n\nFWD_FIELD_TYPES=\"share_type asset_id_type flat_set<asset_id_type> asset price\"\nFWD_FIELD_TYPES=\"$FWD_FIELD_TYPES string std::vector<char> time_point_sec\"\nFWD_FIELD_TYPES=\"$FWD_FIELD_TYPES account_id_type flat_set<account_id_type> public_key_type authority\"\nFWD_FIELD_TYPES=\"$FWD_FIELD_TYPES optional<authority>\"\nFWD_FIELD_TYPES=\"$FWD_FIELD_TYPES bool uint8_t uint16_t uint32_t unsigned_int extensions_type\"\n\nfor T in $FWD_FIELD_TYPES; do\n    echo \"extern template\"\n    echo \"object_restriction_predicate<$T> create_predicate_function( \"\n    echo \"    restriction_function func, restriction_argument arg );\"\ndone\n * ---------------- CUT ---------------- */\n\nextern template\nobject_restriction_predicate<share_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<asset_id_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<flat_set<asset_id_type>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<asset> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<price> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<string> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<std::vector<char>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<time_point_sec> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<account_id_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<flat_set<account_id_type>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<public_key_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<authority> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<optional<authority>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<bool> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<uint8_t> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<uint16_t> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<uint32_t> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<unsigned_int> create_predicate_function(\n    restriction_function func, restriction_argument arg );\nextern template\nobject_restriction_predicate<extensions_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/create_predicate_fwd_1.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n\n/* This file contains explicit specializations of the create_predicate_function template.\n * Generate using the following shell code, and paste below.\n\nFWD_FIELD_TYPES=\"share_type asset_id_type flat_set<asset_id_type> asset price\"\nFWD_FIELD_TYPES=\"$FWD_FIELD_TYPES string std::vector<char> time_point_sec\"\n\nfor T in $FWD_FIELD_TYPES; do\n    echo \"template\"\n    echo \"object_restriction_predicate<$T> create_predicate_function( \"\n    echo \"    restriction_function func, restriction_argument arg );\"\ndone\n */\n\nnamespace graphene { namespace protocol {\n\ntemplate\nobject_restriction_predicate<share_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<asset_id_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<flat_set<asset_id_type>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<asset> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<price> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<string> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<std::vector<char>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<time_point_sec> create_predicate_function(\n    restriction_function func, restriction_argument arg );\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/create_predicate_fwd_2.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n\n/* This file contains explicit specializations of the create_predicate_function template.\n * Generate using the following shell code, and paste below.\n\nFWD_FIELD_TYPES=\"account_id_type flat_set<account_id_type> public_key_type authority optional<authority>\"\n\nfor T in $FWD_FIELD_TYPES; do\n    echo \"template\"\n    echo \"object_restriction_predicate<$T> create_predicate_function( \"\n    echo \"    restriction_function func, restriction_argument arg );\"\ndone\n */\n\nnamespace graphene { namespace protocol {\n\ntemplate\nobject_restriction_predicate<account_id_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<flat_set<account_id_type>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<public_key_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<authority> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<optional<authority>> create_predicate_function(\n    restriction_function func, restriction_argument arg );\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/create_predicate_fwd_3.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n\n/* This file contains explicit specializations of the create_predicate_function template.\n * Generate using the following shell code, and paste below.\n\nFWD_FIELD_TYPES=\"bool uint8_t uint16_t uint32_t unsigned_int extensions_type\"\n\nfor T in $FWD_FIELD_TYPES; do\n    echo \"template\"\n    echo \"object_restriction_predicate<$T> create_predicate_function( \"\n    echo \"    restriction_function func, restriction_argument arg );\"\ndone\n */\n\nnamespace graphene { namespace protocol {\n\ntemplate\nobject_restriction_predicate<bool> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<uint8_t> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<uint16_t> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<uint32_t> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<unsigned_int> create_predicate_function(\n    restriction_function func, restriction_argument arg );\ntemplate\nobject_restriction_predicate<extensions_type> create_predicate_function(\n    restriction_function func, restriction_argument arg );\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_1.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_1(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_1::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_10.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_10(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_10::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_11.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_11(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_11::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_12.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_12(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_12::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_2.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_2(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_2::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_3.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_3(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_3::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_4.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_4(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_4::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_5.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_5(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_5::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_6.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_6(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_6::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_7.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_7(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_7::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_8.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_8(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_8::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/list_9.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nusing result_type = object_restriction_predicate<operation>;\n\nresult_type get_restriction_predicate_list_9(size_t idx, vector<restriction> rs) {\n   return typelist::runtime::dispatch(operation_list_9::list(), idx, [&rs] (auto t) -> result_type {\n      using Op = typename decltype(t)::type;\n      return [p=restrictions_to_predicate<Op>(std::move(rs), true)] (const operation& op) {\n         FC_ASSERT(op.which() == operation::tag<Op>::value,\n                   \"Supplied operation is incorrect type for restriction predicate\");\n         return p(op.get<Op>());\n      };\n   });\n}\n} }\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/restriction_predicate.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include \"restriction_predicate.hxx\"\n#include \"sliced_lists.hxx\"\n\nnamespace graphene { namespace protocol {\n\nrestriction_predicate_function get_restriction_predicate(vector<restriction> rs, operation::tag_type op_type) {\n   auto f = typelist::runtime::dispatch(operation::list(), op_type, [&rs](auto t) -> restriction_predicate_function {\n      using Op = typename decltype(t)::type;\n      if (typelist::contains<operation_list_1::list, Op>())\n         return get_restriction_predicate_list_1(typelist::index_of<operation_list_1::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_2::list, Op>())\n         return get_restriction_predicate_list_2(typelist::index_of<operation_list_2::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_3::list, Op>())\n         return get_restriction_predicate_list_3(typelist::index_of<operation_list_3::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_4::list, Op>())\n         return get_restriction_predicate_list_4(typelist::index_of<operation_list_4::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_5::list, Op>())\n         return get_restriction_predicate_list_5(typelist::index_of<operation_list_5::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_6::list, Op>())\n         return get_restriction_predicate_list_6(typelist::index_of<operation_list_6::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_7::list, Op>())\n         return get_restriction_predicate_list_7(typelist::index_of<operation_list_7::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_8::list, Op>())\n         return get_restriction_predicate_list_8(typelist::index_of<operation_list_8::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_9::list, Op>())\n         return get_restriction_predicate_list_9(typelist::index_of<operation_list_9::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_10::list, Op>())\n         return get_restriction_predicate_list_10(typelist::index_of<operation_list_10::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_11::list, Op>())\n         return get_restriction_predicate_list_11(typelist::index_of<operation_list_11::list, Op>(), std::move(rs));\n      if (typelist::contains<operation_list_12::list, Op>())\n         return get_restriction_predicate_list_12(typelist::index_of<operation_list_12::list, Op>(), std::move(rs));\n      if (typelist::contains<virtual_operations_list::list, Op>())\n         FC_THROW_EXCEPTION( fc::assert_exception, \"Virtual operations not allowed!\" );\n\n      // Compile time check that we'll never get to the exception below\n      static_assert(typelist::contains<typelist::concat<operation_list_1::list, operation_list_2::list,\n                                                        operation_list_3::list, operation_list_4::list,\n                                                        operation_list_5::list, operation_list_6::list,\n                                                        operation_list_7::list, operation_list_8::list,\n                                                        operation_list_9::list, operation_list_10::list,\n                                                        operation_list_11::list, operation_list_12::list,\n                                                        virtual_operations_list::list>,\n                                       Op>(), \"\");\n      FC_THROW_EXCEPTION(fc::assert_exception,\n                         \"LOGIC ERROR: Operation type not handled by custom authorities implementation. \"\n                         \"Please report this error.\");\n   });\n\n   // Wrap function in a layer that, if the function returns an error, reverses the order of the rejection path. This\n   // is because the order the path is created in, from the top of the call stack to the bottom, is counterintuitive.\n   return [f=std::move(f)](const operation& op) { return f(op).reverse_path(); };\n}\n\npredicate_result& predicate_result::reverse_path() {\n   if (success == true)\n      return *this;\n   auto reverse_subpaths = [](rejection_indicator& indicator) {\n      if (indicator.is_type<vector<predicate_result>>()) {\n         auto& results = indicator.get<vector<predicate_result>>();\n         for (predicate_result& result : results) result.reverse_path();\n      }\n   };\n   std::reverse(rejection_path.begin(), rejection_path.end());\n   std::for_each(rejection_path.begin(), rejection_path.end(), reverse_subpaths);\n   return *this;\n}\n\n// These are some compile-time tests of the metafunctions and predicate type analysis. They are turned off to make\n// building faster; they only need to be enabled when making changes in restriction_predicate.hxx\n#if false\nstatic_assert(!is_container<int>, \"\");\nstatic_assert(is_container<vector<int>>, \"\");\nstatic_assert(is_container<flat_set<int>>, \"\");\nstatic_assert(is_container<string>, \"\");\nstatic_assert(is_flat_set<flat_set<int>>, \"\");\nstatic_assert(!is_flat_set<vector<int>>, \"\");\n\nstatic_assert(predicate_eq<int, int64_t>()(10, 20) == false, \"\");\nstatic_assert(predicate_eq<int, int64_t>()(10, 5) == false, \"\");\nstatic_assert(predicate_eq<int, int64_t>()(10, 10) == true, \"\");\n\nstatic_assert(predicate_eq<void_t, void_t>::valid == false, \"\");\nstatic_assert(predicate_eq<int, void_t>::valid == false, \"\");\nstatic_assert(predicate_eq<void_t, int64_t>::valid == false, \"\");\nstatic_assert(predicate_eq<int, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<long, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<vector<bool>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<flat_set<char>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<short, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<bool, int64_t>::valid == false, \"\");\nstatic_assert(predicate_eq<int, bool>::valid == false, \"\");\nstatic_assert(predicate_eq<fc::optional<int>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<fc::optional<long>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_eq<fc::optional<long>, void_t>::valid == true, \"\");\nstatic_assert(predicate_eq<flat_set<bool>, flat_set<bool>>::valid == true, \"\");\nstatic_assert(predicate_eq<flat_set<bool>, string>::valid == false, \"\");\nstatic_assert(predicate_eq<string, string>::valid == true, \"\");\nstatic_assert(predicate_ne<int, void_t>::valid == false, \"\");\nstatic_assert(predicate_ne<void_t, int64_t>::valid == false, \"\");\nstatic_assert(predicate_ne<int, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<long, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<vector<bool>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<flat_set<char>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<short, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<bool, int64_t>::valid == false, \"\");\nstatic_assert(predicate_ne<int, bool>::valid == false, \"\");\nstatic_assert(predicate_ne<fc::optional<int>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<fc::optional<long>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_ne<fc::optional<long>, void_t>::valid == true, \"\");\nstatic_assert(predicate_ne<string, string>::valid == true, \"\");\n\nstatic_assert(predicate_compare<int, int64_t>()(20, 10) == 1, \"\");\nstatic_assert(predicate_compare<int, int64_t>()(5, 10) == -1, \"\");\nstatic_assert(predicate_compare<int, int64_t>()(10, 10) == 0, \"\");\nstatic_assert(predicate_lt<int, int64_t>()(20, 10) == false, \"\");\nstatic_assert(predicate_lt<int, int64_t>()(5, 10) == true, \"\");\nstatic_assert(predicate_lt<int, int64_t>()(10, 10) == false, \"\");\nstatic_assert(predicate_le<int, int64_t>()(20, 10) == false, \"\");\nstatic_assert(predicate_le<int, int64_t>()(5, 10) == true, \"\");\nstatic_assert(predicate_le<int, int64_t>()(10, 10) == true, \"\");\nstatic_assert(predicate_gt<int, int64_t>()(20, 10) == true, \"\");\nstatic_assert(predicate_gt<int, int64_t>()(5, 10) == false, \"\");\nstatic_assert(predicate_gt<int, int64_t>()(10, 10) == false, \"\");\nstatic_assert(predicate_ge<int, int64_t>()(20, 10) == true, \"\");\nstatic_assert(predicate_ge<int, int64_t>()(5, 10) == false, \"\");\nstatic_assert(predicate_ge<int, int64_t>()(10, 10) == true, \"\");\n\nstatic_assert(predicate_compare<int, int64_t>::valid == true, \"\");\nstatic_assert(predicate_compare<short, int64_t>::valid == true, \"\");\nstatic_assert(predicate_compare<string, string>::valid == true, \"\");\nstatic_assert(predicate_compare<vector<int>, int64_t>::valid == false, \"\");\nstatic_assert(predicate_compare<fc::optional<int>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_compare<fc::optional<short>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_compare<fc::optional<string>, string>::valid == true, \"\");\nstatic_assert(predicate_lt<int, int64_t>::valid == true, \"\");\nstatic_assert(predicate_lt<short, int64_t>::valid == true, \"\");\nstatic_assert(predicate_lt<string, string>::valid == true, \"\");\nstatic_assert(predicate_lt<vector<int>, int64_t>::valid == false, \"\");\nstatic_assert(predicate_lt<fc::optional<int>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_lt<fc::optional<short>, int64_t>::valid == true, \"\");\nstatic_assert(predicate_lt<fc::optional<string>, string>::valid == true, \"\");\n\nstatic_assert(predicate_in<string, string>::valid == false, \"\");\nstatic_assert(predicate_in<int, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_in<string, flat_set<string>>::valid == true, \"\");\nstatic_assert(predicate_in<flat_set<string>, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_in<fc::optional<string>, flat_set<string>>::valid == true, \"\");\nstatic_assert(predicate_not_in<string, string>::valid == false, \"\");\nstatic_assert(predicate_not_in<int, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_not_in<string, flat_set<string>>::valid == true, \"\");\nstatic_assert(predicate_not_in<flat_set<string>, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_not_in<fc::optional<string>, flat_set<string>>::valid == true, \"\");\n\nstatic_assert(predicate_has_all<string, string>::valid == false, \"\");\nstatic_assert(predicate_has_all<int, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_has_all<string, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_has_all<flat_set<string>, flat_set<string>>::valid == true, \"\");\nstatic_assert(predicate_has_all<fc::optional<string>, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_has_all<fc::optional<flat_set<string>>, flat_set<string>>::valid == true, \"\");\nstatic_assert(predicate_has_none<string, string>::valid == false, \"\");\nstatic_assert(predicate_has_none<int, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_has_none<string, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_has_none<flat_set<string>, flat_set<string>>::valid == true, \"\");\nstatic_assert(predicate_has_none<fc::optional<string>, flat_set<string>>::valid == false, \"\");\nstatic_assert(predicate_has_none<fc::optional<flat_set<string>>, flat_set<string>>::valid == true, \"\");\n\n#endif\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/restriction_predicate.hxx",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include <fc/exception/exception.hpp>\n\n#include \"safe_compare.hpp\"\n\nnamespace graphene { namespace protocol {\nnamespace typelist = fc::typelist;\nusing std::declval;\nusing std::size_t;\nusing restriction_function = restriction::function_type;\nusing restriction_argument = restriction::argument_type;\n\n// Make our own std::void_t since the real one isn't available in C++14\ntemplate<typename...> using make_void = void;\n\n// Metafunction to check if type is some instantiation of fc::safe\ntemplate<typename> constexpr static bool is_safe = false;\ntemplate<typename I> constexpr static bool is_safe<fc::safe<I>> = true;\n\n// Metafunction to check if type is a flat_set of any element type\ntemplate<typename> struct is_flat_set_impl : std::false_type {};\ntemplate<typename T> struct is_flat_set_impl<flat_set<T>> : std::true_type {};\ntemplate<typename T> constexpr static bool is_flat_set = is_flat_set_impl<T>::value;\n\n// We use our own is_integral which does not consider bools integral (to disallow comparison between bool and ints)\ntemplate<typename T> constexpr static bool is_integral = !std::is_same<T, bool>::value &&\n                                                         !std::is_same<T, safe<bool>>::value &&\n                                                         (is_safe<T> || std::is_integral<T>::value);\n\n// Metafunction to check if two types are comparable, which means not void_t, and either the same or both integral\ntemplate<typename T, typename U>\nconstexpr static bool comparable_types = !std::is_same<T, void_t>::value &&\n                                         (std::is_same<T, U>::value || (is_integral<T> && is_integral<U>));\n\n// Metafunction to check if type is a container\ntemplate<typename, typename = void>\nstruct is_container_impl : std::false_type {};\ntemplate<typename T>\nstruct is_container_impl<T, make_void<typename T::value_type, decltype(declval<T>().size())>> : std::true_type {};\ntemplate<typename T> constexpr static bool is_container = is_container_impl<T>::value;\n\n// Type alias for a predicate on a particular field type\ntemplate<typename Field>\nusing object_restriction_predicate = std::function<predicate_result(const Field&)>;\n\n// Get the actual number when type might be a safe<I>\ntemplate<typename I, typename=std::enable_if_t<std::is_integral<I>::value>>\nconst auto& to_num(const I& i) { return i; }\ntemplate<typename I>\nconst auto& to_num(const fc::safe<I>& i) { return i.value; }\ninline auto to_num(const fc::time_point_sec& t) { return t.sec_since_epoch(); }\n\nnamespace safenum = boost::safe_numerics::safe_compare;\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// *** Restriction Predicate Logic ***\n//\n// This file implements the core logic of Custom Active Authorities. A CAA is an authority which is permitted by an\n// account to execute a particular authority on that account's behalf, with some restrictions on the content of that\n// operation. This file implements the logic to validate those restrictions, and create a predicate function which\n// takes a particular operation and determines whether it complies with the restrictions or not.\n//\n// The restrictions are a recursive structure, which applies to a particular operation struct, but may recurse to\n// specify restrictions on fields or subfields of that struct. This file explores the restriction structure in tandem\n// with the operation struct to verify that all of the restrictions are valid and to produce a predicate function.\n// Note that this file operates primarily on restriction data, but only operation *types*, meaning the actual\n// operation value does not appear until the predicate returned by this file is run.\n//\n// As a result, this file is very template heavy, and does a good deal of type manipulation. Its contents are\n// organized as a series of layers, which recursively examine the restrictions and types they apply to, and finally,\n// once all the types have been resolved, a predicate function is created which evaluates the restrictions on an\n// operation.\n//\n// To give an overview of the logic, the layers stack up like so, from beginning (bottom of file) to end:\n//  - restrictions_to_predicate<Object>() -- takes a vector<restriction> and creates a predicate for each of them,\n//    but returns a single predicate that returns true only if all sub-predicates return true\n//    - create_field_predicate<Object>() -- Resolves which field of Object the restriction is referencing by indexing\n//      into the object's reflected fields with the predicate's member_index\n//    - create_logical_or_predicate<Object>() -- If the predicate is a logical OR function, the predicate does not\n//      specify a field to examine; rather, the predicates in its branches do. Thus this function recurses into\n//      restrictions_to_predicate for each branch of the OR, and combines the resulting predicates in a predicate\n//      which returns true if any branch of the OR passes\n//  - create_predicate_function<Field>() -- switches on restriction type to determine which predicate template to use\n//    going forward\n//    - make_predicate<Predicate, Field, ArgVariant> -- Determines what type the restriction argument is and creates\n//      a predicate functor for that type\n//    - attribute_assertion<Field> -- If the restriction is an attribute assertion, instead of using make_predicate\n//      to create a predicate function, we first recurse into restrictions_to_predicate with Field as the Object\n//    - variant_assertion<Field> -- If the restriction is a variant assertion, instead of using make_predicate, we\n//      recurse into restrictions_to_predicate with the variant value as the Object\n//  - embed_argument<Field, Predicate, Argument>() -- Embeds the argument into the predicate if it is a valid type\n//    for the predicate, and throws otherwise.\n//  - predicate_xyz<Argument> -- These are functors implementing the various predicate function types\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n// These typelists contain the argument types legal for various function types:\n\n// Valid for magnitude comparisons and equality comparisons\nusing comparable_types_list = typelist::list<int64_t, string, time_point_sec, account_id_type, asset_id_type,\n                                             force_settlement_id_type, committee_member_id_type, witness_id_type,\n                                             limit_order_id_type, call_order_id_type, custom_id_type,\n                                             proposal_id_type, withdraw_permission_id_type,\n                                             vesting_balance_id_type, worker_id_type, balance_id_type>;\n// Valid for list functions (in, not_in, has_all, has_none)\nstruct make_flat_set { template<typename T> struct transform { using type = flat_set<T>; }; };\nusing list_types_list = typelist::transform<typelist::concat<typelist::list<bool, public_key_type, fc::sha256>,\n                                                             comparable_types_list>,\n                                            make_flat_set>;\n// Valid for equality comparisons but not necessarily magnitude comparisons\nusing equality_types_list = typename typelist::concat<typelist::list<void_t, bool, public_key_type, fc::sha256>,\n                                                      comparable_types_list, list_types_list>;\n// Valid for attritube assertions\nusing attr_types_list = typelist::list<vector<restriction>>;\n// Valid for logical or assertions\nusing or_types_list = typelist::list<vector<vector<restriction>>>;\n\n//////////////////////////////////////////////// PREDICATE FUNCTORS ////////////////////////////////////////////////\n// An invalid predicate which throws upon construction. Inherited by other predicates when arg types are incompatible\ntemplate<typename A, typename B>\nstruct predicate_invalid {\n   constexpr static bool valid = false;\n   predicate_invalid() { FC_THROW_EXCEPTION(fc::assert_exception, \"Invalid types for predicate\"); }\n   bool operator()(const A&, const B&) const { return false; }\n};\n// Equality comparison\ntemplate<typename A, typename B, typename = void> struct predicate_eq : predicate_invalid<A, B> {};\ntemplate<typename Field, typename Argument>\nstruct predicate_eq<Field, Argument, std::enable_if_t<std::is_same<Field, Argument>::value>> {\n   // Simple comparison, same type\n   constexpr static bool valid = true;\n   constexpr bool operator()(const Field& f, const Argument& a) const { return f == a; }\n};\ntemplate<typename Field, typename Argument>\nstruct predicate_eq<Field, Argument, std::enable_if_t<is_integral<Field> && is_integral<Argument> &&\n                                                      !std::is_same<Field, Argument>::value>> {\n   // Simple comparison, integral types\n   constexpr static bool valid = true;\n   constexpr bool operator()(const Field& f, const Argument& a) const { return safenum::equal(to_num(f), to_num(a)); }\n};\ntemplate<typename Field, typename Argument>\nstruct predicate_eq<Field, Argument, std::enable_if_t<is_container<Field> && is_integral<Argument>>> {\n   // Compare container size against int\n   constexpr static bool valid = true;\n   bool operator()(const Field& f, const Argument& a) const { return safenum::equal(f.size(), to_num(a)); }\n};\ntemplate<typename Field, typename Argument>\nstruct predicate_eq<fc::optional<Field>, Argument, std::enable_if_t<comparable_types<Field, Argument>>>\n   : predicate_eq<Field, Argument> {\n   // Compare optional value against comparable type\n   using base = predicate_eq<Field, Argument>;\n   bool operator()(const fc::optional<Field>& f, const Argument& a) const {\n      if (!f.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n      return (*this)(*f, a);\n   }\n};\ntemplate<typename Field>\nstruct predicate_eq<fc::optional<Field>, void_t, void> {\n   // Compare optional value against void_t (checks that optional is null)\n   constexpr static bool valid = true;\n   bool operator()(const fc::optional<Field>& f, const void_t&) const { return !f.valid(); }\n};\n// Not-equal is just an equality comparison wrapped in a negator\ntemplate<typename Field, typename Argument> struct predicate_ne : predicate_eq<Field, Argument> {\n   using equal = predicate_eq<Field, Argument>;\n   bool operator()(const Field& f, const Argument& a) const { return !equal::operator()(f, a); }\n};\n\n// Shared implementation for all inequality comparisons\ntemplate<typename A, typename B, typename = void> struct predicate_compare : predicate_invalid<A, B> {};\ntemplate<typename Field, typename Argument>\nstruct predicate_compare<Field, Argument, std::enable_if_t<std::is_same<Field, Argument>::value>> {\n   // Simple comparison, same types\n   constexpr static bool valid = true;\n   constexpr int8_t operator()(const Field& f, const Argument& a) const {\n      return f<a? -1 : (f>a? 1 : 0);\n   }\n};\ntemplate<typename Field, typename Argument>\nstruct predicate_compare<Field, Argument, std::enable_if_t<is_integral<Field> && is_integral<Argument> &&\n                                                           !std::is_same<Field, Argument>::value>> {\n   // Simple comparison, integral types\n   constexpr static bool valid = true;\n   constexpr int8_t operator()(const Field& f, const Argument& a) const {\n      auto nf = to_num(f);\n      auto na = to_num(a);\n      return safenum::less_than(nf, na)? -1 : (safenum::greater_than(nf, na)? 1 : 0);\n   }\n};\ntemplate<typename Field, typename Argument>\nstruct predicate_compare<fc::optional<Field>, Argument, void> : predicate_compare<Field, Argument> {\n   // Compare optional value against comparable type\n   constexpr static bool valid = true;\n   constexpr int8_t operator()(const fc::optional<Field>& f, const Argument& a) const {\n      if (!f.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n       return (*this)(*f, a);\n   }\n};\n// The actual inequality predicates\ntemplate<typename Field, typename Argument> struct predicate_lt : predicate_compare<Field, Argument> {\n   using base = predicate_compare<Field, Argument>;\n   constexpr bool operator()(const Field& f, const Argument& a) const { return base::operator()(f, a) < 0; }\n};\ntemplate<typename Field, typename Argument> struct predicate_le : predicate_compare<Field, Argument> {\n   using base = predicate_compare<Field, Argument>;\n   constexpr bool operator()(const Field& f, const Argument& a) const { return base::operator()(f, a) <= 0; }\n};\ntemplate<typename Field, typename Argument> struct predicate_gt : predicate_compare<Field, Argument> {\n   using base = predicate_compare<Field, Argument>;\n   constexpr bool operator()(const Field& f, const Argument& a) const { return base::operator()(f, a) > 0; }\n};\ntemplate<typename Field, typename Argument> struct predicate_ge : predicate_compare<Field, Argument> {\n   using base = predicate_compare<Field, Argument>;\n   constexpr bool operator()(const Field& f, const Argument& a) const { return base::operator()(f, a) >= 0; }\n};\n\n// Field-in-list predicate\ntemplate<typename F, typename C, typename = void> struct predicate_in : predicate_invalid<F, C> {};\ntemplate<typename Field, typename Element>\nstruct predicate_in<Field, flat_set<Element>, std::enable_if_t<comparable_types<Field, Element> && !is_safe<Field>>> {\n   // Simple inclusion check\n   constexpr static bool valid = true;\n   bool operator()(const Field& f, const flat_set<Element>& c) const { return c.count(f) != 0; }\n};\ntemplate<typename Field, typename Element>\nstruct predicate_in<fc::safe<Field>, flat_set<Element>, std::enable_if_t<comparable_types<Field, Element>>> {\n   // Check for safe value\n   constexpr static bool valid = true;\n   bool operator()(const fc::safe<Field>& f, const flat_set<Element>& c) const { return c.count(f.value) != 0; }\n};\ntemplate<typename Field, typename Element>\nstruct predicate_in<fc::optional<Field>, flat_set<Element>, std::enable_if_t<comparable_types<Field, Element>>> {\n   // Check for optional value\n   constexpr static bool valid = true;\n   bool operator()(const fc::optional<Field>& f, const flat_set<Element>& c) const {\n      if (!f.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n       return c.count(*f) != 0;\n   }\n};\ntemplate<typename Container, typename Element>\nstruct predicate_in<Container, flat_set<Element>,\n                    std::enable_if_t<is_container<Container> &&\n                                     comparable_types<typename Container::value_type, Element>>> {\n   // Check all values in container are in argument\n   constexpr static bool valid = true;\n   // Unsorted container\n   template<typename C = Container, std::enable_if_t<!is_flat_set<C>, bool> = true>\n   bool operator()(const Container& c, const flat_set<Element>& a) const {\n      return std::all_of(c.begin(), c.end(), [&a](const auto& ce) { return a.count(ce) > 0; });\n   }\n   // Sorted container\n   template<typename C = Container, std::enable_if_t<is_flat_set<C>, bool> = true>\n   bool operator()(const Container& c, const flat_set<Element>& a) const {\n      return std::includes(a.begin(), a.end(), c.begin(), c.end());\n   }\n};\n// Field-not-in-list is just field-in-list wrapped in a negator\ntemplate<typename Field, typename Container, typename=void> struct predicate_not_in : predicate_in<Field, Container> {\n   using base = predicate_in<Field, Container>;\n   bool operator()(const Field& f, const Container& c) const { return !base::operator()(f, c); }\n};\n// Container-field-not-in-list is not a simple negation of predicate_in, specialize here\ntemplate<typename Container, typename Element>\nstruct predicate_not_in<Container, flat_set<Element>,\n                        std::enable_if_t<is_container<Container> &&\n                                         comparable_types<typename Container::value_type, Element>>> {\n   constexpr static bool valid = true;\n   // Unsorted container\n   template<typename C = Container, std::enable_if_t<!is_flat_set<C>, bool> = true>\n   bool operator()(const Container& c, const flat_set<Element>& a) const {\n      return std::none_of(c.begin(), c.end(), [&a](const auto& ce) { return a.count(ce) > 0; });\n   }\n   // Sorted container\n   template<typename C = Container, std::enable_if_t<is_flat_set<C>, bool> = true>\n   bool operator()(const Container& c, const flat_set<Element>& a) const {\n      flat_set<typename Container::value_type> intersection;\n      std::set_intersection(c.begin(), c.end(), a.begin(), a.end(),\n                            std::inserter(intersection, intersection.begin()));\n      return intersection.empty();\n   }\n};\n\n// List-contains-list predicate\ntemplate<typename C1, typename C2, typename = void> struct predicate_has_all : predicate_invalid<C1, C2> {};\ntemplate<typename FieldElement, typename ArgumentElement>\nstruct predicate_has_all<flat_set<FieldElement>, flat_set<ArgumentElement>,\n                         std::enable_if_t<comparable_types<FieldElement, ArgumentElement>>> {\n   // Field is already flat_set\n   constexpr static bool valid = true;\n   bool operator()(const flat_set<FieldElement>& f, const flat_set<ArgumentElement>& a) const {\n      if (f.size() < a.size()) return false;\n      return std::includes(f.begin(), f.end(), a.begin(), a.end());\n   }\n};\ntemplate<typename FieldContainer, typename ArgumentElement>\nstruct predicate_has_all<FieldContainer, flat_set<ArgumentElement>,\n                         std::enable_if_t<is_container<FieldContainer> && !is_flat_set<FieldContainer> &&\n                                          comparable_types<typename FieldContainer::value_type, ArgumentElement>>> {\n   // Field is other container; convert to flat_set\n   constexpr static bool valid = true;\n   bool operator()(const FieldContainer& f, const flat_set<ArgumentElement>& a) const {\n      if (f.size() < a.size()) return false;\n      std::set<typename FieldContainer::value_type> fs(f.begin(), f.end());\n      return std::includes(fs.begin(), fs.end(), a.begin(), a.end());\n   }\n};\ntemplate<typename OptionalType, typename Argument>\nstruct predicate_has_all<fc::optional<OptionalType>, Argument, void> : predicate_has_all<OptionalType, Argument> {\n   // Field is optional container\n   bool operator()(const fc::optional<OptionalType>& f, const Argument& a) const {\n      if (!f.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n      return (*this)(*f, a);\n   }\n};\n\n// List contains none of list predicate\ntemplate<typename C1, typename C2, typename = void> struct predicate_has_none : predicate_invalid<C1, C2> {};\ntemplate<typename FieldElement, typename ArgumentElement>\nstruct predicate_has_none<flat_set<FieldElement>, flat_set<ArgumentElement>,\n                          std::enable_if_t<comparable_types<FieldElement, ArgumentElement>>> {\n   // Field is already flat_set\n   constexpr static bool valid = true;\n   bool operator()(const flat_set<FieldElement>& f, const flat_set<ArgumentElement>& a) const {\n      flat_set<FieldElement> intersection;\n      std::set_intersection(f.begin(), f.end(), a.begin(), a.end(),\n                            std::inserter(intersection, intersection.begin()));\n      return intersection.empty();\n   }\n};\ntemplate<typename FieldContainer, typename ArgumentElement>\nstruct predicate_has_none<FieldContainer, flat_set<ArgumentElement>,\n                          std::enable_if_t<is_container<FieldContainer> && !is_flat_set<FieldContainer> &&\n                                           comparable_types<typename FieldContainer::value_type, ArgumentElement>>> {\n   // Field is other container\n   constexpr static bool valid = true;\n   bool operator()(const FieldContainer& f, const flat_set<ArgumentElement>& a) const {\n      return !std::any_of(f.begin(), f.end(), [&a](const auto& fe) { return a.count(fe) > 0; });\n   }\n};\ntemplate<typename OptionalType, typename Argument>\nstruct predicate_has_none<fc::optional<OptionalType>, Argument, void> : predicate_has_all<OptionalType, Argument> {\n   // Field is optional container\n   bool operator()(const fc::optional<OptionalType>& f, const Argument& a) const {\n      if (!f.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n      return (*this)(*f, a);\n   }\n};\n////////////////////////////////////////////// END PREDICATE FUNCTORS //////////////////////////////////////////////\n\n// Forward declaration of restrictions_to_predicate, because attribute assertions and logical ORs recurse into it\ntemplate<typename Object> object_restriction_predicate<Object> restrictions_to_predicate(vector<restriction>, bool);\n\ntemplate<typename Field>\nstruct attribute_assertion {\n   static object_restriction_predicate<Field> create(vector<restriction>&& rs) {\n      return restrictions_to_predicate<Field>(std::move(rs), false);\n   }\n};\ntemplate<typename Field>\nstruct attribute_assertion<fc::optional<Field>> {\n   static object_restriction_predicate<fc::optional<Field>> create(vector<restriction>&& rs) {\n      return [p=restrictions_to_predicate<Field>(std::move(rs), false)](const fc::optional<Field>& f) {\n         if (!f.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n         return p(*f);\n      };\n   }\n};\ntemplate<typename Extension>\nstruct attribute_assertion<extension<Extension>> {\n   static object_restriction_predicate<extension<Extension>> create(vector<restriction>&& rs) {\n      return [p=restrictions_to_predicate<Extension>(std::move(rs), false)](const extension<Extension>& x) {\n         return p(x.value);\n      };\n   }\n};\n\ntemplate<typename Variant>\nstruct variant_assertion {\n   static object_restriction_predicate<Variant> create(restriction::variant_assert_argument_type&&) {\n      FC_THROW_EXCEPTION(fc::assert_exception, \"Invalid variant assertion on non-variant field\",\n                         (\"Field\", fc::get_typename<Variant>::name()));\n   }\n};\ntemplate<typename... Types>\nstruct variant_assertion<static_variant<Types...>> {\n   using Variant = static_variant<Types...>;\n\n   template<typename Value>\n   static auto make_predicate(vector<restriction>&& rs) {\n      return [p=restrictions_to_predicate<Value>(std::move(rs), true)](const Variant& v) {\n         if (v.which() == Variant::template tag<Value>::value)\n            return p(v.template get<Value>());\n         return predicate_result::Rejection(predicate_result::incorrect_variant_type);\n      };\n   }\n   static object_restriction_predicate<Variant> create(restriction::variant_assert_argument_type&& arg) {\n      return typelist::runtime::dispatch(typelist::list<Types...>(), arg.first,\n                                         [&arg](auto t) -> object_restriction_predicate<Variant> {\n         using Value = typename decltype(t)::type;\n         return variant_assertion::make_predicate<Value>(std::move(arg.second));\n      });\n   }\n};\ntemplate<typename... Types>\nstruct variant_assertion<fc::optional<static_variant<Types...>>> {\n   using Variant = static_variant<Types...>;\n   using Optional = fc::optional<Variant>;\n   static object_restriction_predicate<Optional> create(restriction::variant_assert_argument_type&& arg) {\n      return typelist::runtime::dispatch(typelist::list<Types...>(), arg.first,\n                                         [&arg](auto t) -> object_restriction_predicate<Optional> {\n         using Value = typename decltype(t)::type;\n         auto pred = variant_assertion<Variant>::template make_predicate<Value>(std::move(arg.second));\n         return [p=std::move(pred)](const Optional& opt) {\n            if (!opt.valid()) return predicate_result::Rejection(predicate_result::null_optional);\n            return p(*opt);\n         };\n      });\n   }\n};\n\n// Embed the argument into the predicate functor\ntemplate<typename F, typename P, typename A, typename = std::enable_if_t<P::valid>>\nobject_restriction_predicate<F> embed_argument(P p, A a, short) {\n   return [p=std::move(p), a=std::move(a)](const F& f) {\n      if (p(f, a)) return predicate_result::Success();\n      return predicate_result::Rejection(predicate_result::predicate_was_false);\n   };\n}\ntemplate<typename F, typename P, typename A>\nobject_restriction_predicate<F> embed_argument(P, A, long) {\n   FC_THROW_EXCEPTION(fc::assert_exception, \"Invalid types for predicate\");\n}\n\n// Resolve the argument type and make a predicate for it\ntemplate<template<typename...> class Predicate, typename Field, typename ArgVariant>\nobject_restriction_predicate<Field> make_predicate(ArgVariant arg) {\n   return typelist::runtime::dispatch(typename ArgVariant::list(), arg.which(),\n                                      [&arg](auto t) mutable -> object_restriction_predicate<Field> {\n      using Arg = typename decltype(t)::type;\n      return embed_argument<Field>(Predicate<Field, Arg>(), std::move(arg.template get<Arg>()), short());\n   });\n}\n\ntemplate<typename Field>\nobject_restriction_predicate<Field> create_predicate_function(restriction_function func, restriction_argument arg) {\n   try {\n      switch(func) {\n      case restriction::func_eq:\n         return make_predicate<predicate_eq, Field>(static_variant<equality_types_list>::import_from(std::move(arg)));\n      case restriction::func_ne:\n         return make_predicate<predicate_ne, Field>(static_variant<equality_types_list>::import_from(std::move(arg)));\n      case restriction::func_lt:\n         return make_predicate<predicate_lt, Field>(static_variant<comparable_types_list>\n                                                    ::import_from(std::move(arg)));\n      case restriction::func_le:\n         return make_predicate<predicate_le, Field>(static_variant<comparable_types_list>\n                                                    ::import_from(std::move(arg)));\n      case restriction::func_gt:\n         return make_predicate<predicate_gt, Field>(static_variant<comparable_types_list>\n                                                    ::import_from(std::move(arg)));\n      case restriction::func_ge:\n         return make_predicate<predicate_ge, Field>(static_variant<comparable_types_list>\n                                                    ::import_from(std::move(arg)));\n      case restriction::func_in:\n         return make_predicate<predicate_in, Field>(static_variant<list_types_list>::import_from(std::move(arg)));\n      case restriction::func_not_in:\n         return make_predicate<predicate_not_in, Field>(static_variant<list_types_list>\n                                                        ::import_from(std::move(arg)));\n      case restriction::func_has_all:\n         return make_predicate<predicate_has_all, Field>(static_variant<list_types_list>\n                                                         ::import_from(std::move(arg)));\n      case restriction::func_has_none:\n         return make_predicate<predicate_has_none, Field>(static_variant<list_types_list>\n                                                          ::import_from(std::move(arg)));\n      case restriction::func_attr:\n         FC_ASSERT(arg.which() == restriction_argument::tag<vector<restriction>>::value,\n                   \"Argument type for attribute assertion must be restriction list\");\n         return attribute_assertion<Field>::create(std::move(arg.get<vector<restriction>>()));\n      case restriction::func_variant_assert:\n         FC_ASSERT(arg.which() == restriction_argument::tag<restriction::variant_assert_argument_type>::value,\n                   \"Argument type for attribute assertion must be pair of variant tag and restriction list\");\n         return variant_assertion<Field>::create(std::move(arg.get<restriction::variant_assert_argument_type>()));\n      default:\n          FC_THROW_EXCEPTION(fc::assert_exception, \"Invalid function type on restriction\");\n      }\n   } FC_CAPTURE_AND_RETHROW( (fc::get_typename<Field>::name())(func)(arg) )\n}\n\n#include \"create_predicate_fwd.hxx\"\n\n/**\n * @brief Create a predicate asserting on the field of the object a restriction is referencing\n *\n * @tparam Object The type the restriction restricts\n *\n * A restriction specifies requirements about a field of an object. This struct shifts the focus from the object type\n * the restriction references to the particular field type, creates a predicate on that field, and wraps that\n * predicate to accept the object type and invoke the inner predicate on the specified field.\n */\ntemplate<typename Object,\n         typename = std::enable_if_t<typelist::length<typename fc::reflector<Object>::native_members>() != 0>>\nobject_restriction_predicate<Object> create_field_predicate(restriction&& r, short) {\n   using member_list = typename fc::reflector<Object>::native_members;\n   FC_ASSERT( r.member_index < static_cast<uint64_t>(typelist::length<member_list>()),\n              \"Invalid member index ${I} for object ${O}\",\n              (\"I\", r.member_index)(\"O\", fc::get_typename<Object>::name()) );\n   auto predicator = [f=r.restriction_type, a=std::move(r.argument)](auto t) -> object_restriction_predicate<Object> {\n      using FieldReflection = typename decltype(t)::type;\n      using Field = typename FieldReflection::type;\n      auto p = create_predicate_function<Field>(static_cast<restriction_function>(f), std::move(a));\n      return [p=std::move(p)](const Object& o) { return p(FieldReflection::get(o)); };\n   };\n   return typelist::runtime::dispatch(member_list(), static_cast<size_t>(r.member_index.value), predicator);\n}\ntemplate<typename Object>\nobject_restriction_predicate<Object> create_field_predicate(restriction&&, long) {\n   FC_THROW_EXCEPTION(fc::assert_exception, \"Invalid restriction references member of non-object type: ${O}\",\n                      (\"O\", fc::get_typename<Object>::name()));\n}\n\ntemplate<typename Object>\nobject_restriction_predicate<Object> create_logical_or_predicate(vector<vector<restriction>> rs) {\n   FC_ASSERT(rs.size() > 1, \"Logical OR must have at least two branches\");\n   auto to_predicate = std::bind(restrictions_to_predicate<Object>, std::placeholders::_1, false);\n\n   vector<object_restriction_predicate<Object>> predicates;\n   std::transform(std::make_move_iterator(rs.begin()), std::make_move_iterator(rs.end()),\n                  std::back_inserter(predicates), to_predicate);\n\n   return [predicates=std::move(predicates)](const Object& obj) {\n      vector<predicate_result> rejections;\n      bool success = std::any_of(predicates.begin(), predicates.end(),\n                                 [o=std::cref(obj), &rejections](const auto& p) {\n         auto result = p(o);\n         if (!result) rejections.push_back(std::move(result));\n         return !!result;\n      });\n      if (success) return predicate_result::Success();\n      return predicate_result::Rejection(std::move(rejections));\n   };\n}\n\ntemplate<typename Object>\nobject_restriction_predicate<Object> restrictions_to_predicate(vector<restriction> rs, bool allow_empty) {\n   if (!allow_empty)\n      FC_ASSERT(!rs.empty(), \"Empty attribute assertions and logical OR branches are not permitted\");\n\n   vector<object_restriction_predicate<Object>> predicates;\n   std::transform(std::make_move_iterator(rs.begin()), std::make_move_iterator(rs.end()),\n                  std::back_inserter(predicates), [](restriction&& r) {\n      if (r.restriction_type.value == restriction::func_logical_or) {\n          FC_ASSERT(r.argument.which() == restriction_argument::tag<vector<vector<restriction>>>::value,\n                    \"Restriction argument for logical OR function type must be list of restriction lists.\");\n          return create_logical_or_predicate<Object>(std::move(r.argument.get<vector<vector<restriction>>>()));\n      }\n      return create_field_predicate<Object>(std::move(r), short());\n   });\n\n   return [predicates=std::move(predicates)](const Object& obj) {\n      for (size_t i = 0; i < predicates.size(); ++i) {\n         auto result = predicates[i](obj);\n         if (!result) {\n            result.rejection_path.push_back(i);\n            return result;\n         }\n      }\n      return predicate_result::Success();\n   };\n}\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/safe_compare.hpp",
    "content": "#ifndef BOOST_NUMERIC_SAFE_COMPARE_HPP\n#define BOOST_NUMERIC_SAFE_COMPARE_HPP\n\n// MS compatible compilers support #pragma once\n#if defined(_MSC_VER) && (_MSC_VER >= 1020)\n# pragma once\n#endif\n\n//  Copyright (c) 2012 Robert Ramey\n//\n// Distributed under the Boost Software License, Version 1.0. (See\n// accompanying file BOOST_LICENSE_1_0.txt or copy at\n// http://www.boost.org/LICENSE_1_0.txt)\n\n#include <type_traits>\n#include <limits>\n\nnamespace boost {\nnamespace safe_numerics {\nnamespace safe_compare {\n\n////////////////////////////////////////////////////\n// safe comparison on primitive integral types\nnamespace safe_compare_detail {\n    template<typename T>\n    using make_unsigned = typename std::conditional<\n        std::is_signed<T>::value,\n        std::make_unsigned<T>,\n        T\n    >::type;\n\n    // both arguments unsigned or signed\n    template<bool TS, bool US>\n    struct less_than {\n        template<class T, class U>\n        constexpr static bool invoke(const T & t, const U & u){\n            return t < u;\n        }\n    };\n\n    // T unsigned, U signed\n    template<>\n    struct less_than<false, true> {\n        template<class T, class U>\n        constexpr static bool invoke(const T & t, const U & u){\n            return\n                (u < 0) ?\n                    false\n                :\n                    less_than<false, false>::invoke(\n                        t,\n                        static_cast<const typename make_unsigned<U>::type &>(u)\n                    )\n                ;\n        }\n    };\n    // T signed, U unsigned\n    template<>\n    struct less_than<true, false> {\n        template<class T, class U>\n        constexpr static bool invoke(const T & t, const U & u){\n            return\n                (t < 0) ?\n                    true\n                :\n                    less_than<false, false>::invoke(\n                        static_cast<const typename make_unsigned<T>::type &>(t),\n                        u\n                    )\n                ;\n        }\n    };\n} // safe_compare_detail\n\ntemplate<class T, class U>\ntypename std::enable_if<\n    std::is_integral<T>::value && std::is_integral<U>::value,\n    bool\n>::type\nconstexpr less_than(const T & lhs, const U & rhs) {\n    return safe_compare_detail::less_than<\n        std::is_signed<T>::value,\n        std::is_signed<U>::value\n    >::template invoke(lhs, rhs);\n}\n\ntemplate<class T, class U>\ntypename std::enable_if<\n    std::is_floating_point<T>::value && std::is_floating_point<U>::value,\n    bool\n>::type\nconstexpr less_than(const T & lhs, const U & rhs) {\n    return lhs < rhs;\n}\n\ntemplate<class T, class U>\nconstexpr bool greater_than(const T & lhs, const U & rhs) {\n    return less_than(rhs, lhs);\n}\n\ntemplate<class T, class U>\nconstexpr bool less_than_equal(const T & lhs, const U & rhs) {\n    return ! greater_than(lhs, rhs);\n}\n\ntemplate<class T, class U>\nconstexpr bool greater_than_equal(const T & lhs, const U & rhs) {\n    return ! less_than(lhs, rhs);\n}\n\nnamespace safe_compare_detail {\n    // both arguments unsigned or signed\n    template<bool TS, bool US>\n    struct equal {\n        template<class T, class U>\n        constexpr static bool invoke(const T & t, const U & u){\n            return t == u;\n        }\n    };\n\n    // T unsigned, U signed\n    template<>\n    struct equal<false, true> {\n        template<class T, class U>\n        constexpr static bool invoke(const T & t, const U & u){\n            return\n                (u < 0) ?\n                    false\n                :\n                    equal<false, false>::invoke(\n                        t,\n                        static_cast<const typename make_unsigned<U>::type &>(u)\n                    )\n                ;\n        }\n    };\n    // T signed, U unsigned\n    template<>\n    struct equal<true, false> {\n        template<class T, class U>\n        constexpr static bool invoke(const T & t, const U & u){\n            return\n                (t < 0) ?\n                    false\n                :\n                    equal<false, false>::invoke(\n                        static_cast<const typename make_unsigned<T>::type &>(t),\n                        u\n                    )\n                ;\n        }\n    };\n} // safe_compare_detail\n\ntemplate<class T, class U>\ntypename std::enable_if<\n    std::is_integral<T>::value && std::is_integral<U>::value,\n    bool\n>::type\nconstexpr equal(const T & lhs, const U & rhs) {\n    return safe_compare_detail::equal<\n        std::numeric_limits<T>::is_signed,\n        std::numeric_limits<U>::is_signed\n    >::template invoke(lhs, rhs);\n}\n\ntemplate<class T, class U>\ntypename std::enable_if<\n    std::is_floating_point<T>::value && std::is_floating_point<U>::value,\n    bool\n>::type\nconstexpr equal(const T & lhs, const U & rhs) {\n    return lhs == rhs;\n}\n\ntemplate<class T, class U>\nconstexpr bool not_equal(const T & lhs, const U & rhs) {\n    return ! equal(lhs, rhs);\n}\n\n} // safe_compare\n} // safe_numerics\n} // boost\n\n#endif // BOOST_NUMERIC_SAFE_COMPARE_HPP\n"
  },
  {
    "path": "libraries/protocol/custom_authorities/sliced_lists.hxx",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/operations.hpp>\n\n#include <fc/reflect/typelist.hpp>\n\nnamespace graphene { namespace protocol {\nnamespace typelist = fc::typelist;\n\n// To make the build gentler on RAM, break the operation list into several pieces to build over several files\nusing operation_list_1 = static_variant<typelist::slice<operation::list, 0, 4>>;\nusing operation_list_2 = static_variant<typelist::slice<operation::list, 5, 9>>;\nusing operation_list_3 = static_variant<typelist::slice<operation::list, 9, 11>>;\nusing operation_list_4 = static_variant<typelist::slice<operation::list, 11, 12>>;\nusing operation_list_5 = static_variant<typelist::slice<operation::list, 12, 15>>;\nusing operation_list_6 = static_variant<typelist::slice<operation::list, 15, 22>>;\nusing operation_list_7 = static_variant<typelist::slice<operation::list, 22, 29>>;\nusing operation_list_8 = static_variant<typelist::slice<operation::list, 29, 32>>;\nusing operation_list_9 = static_variant<typelist::slice<operation::list, 32, 35>>;\nusing operation_list_10 = static_variant<typelist::slice<operation::list, 35, 42>>;\nusing operation_list_11 = static_variant<typelist::builder<>\n                                                ::add<asset_claim_fees_operation> // 43\n                                                ::add<bid_collateral_operation>   // 45\n                                                ::add_list<typelist::slice<operation::list, 47, 51>>\n                                                ::add<htlc_extend_operation>      // 52\n                                                ::finalize>;\nusing operation_list_12 = static_variant<typelist::slice<operation::list, 54>>;\nusing virtual_operations_list = static_variant<fill_order_operation,          // 4\n                                               asset_settle_cancel_operation, // 42\n                                               fba_distribute_operation,      // 44\n                                               execute_bid_operation,         // 46\n                                               htlc_redeemed_operation,       // 51\n                                               htlc_refund_operation          // 53\n                                              >;\n\nobject_restriction_predicate<operation> get_restriction_predicate_list_1(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_2(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_3(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_4(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_5(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_6(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_7(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_8(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_9(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_10(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_11(size_t idx, vector<restriction> rs);\nobject_restriction_predicate<operation> get_restriction_predicate_list_12(size_t idx, vector<restriction> rs);\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/custom_authority.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/custom_authority.hpp>\n#include <graphene/protocol/operations.hpp>\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nshare_type custom_authority_create_operation::calculate_fee(const fee_parameters_type& k)const {\n   share_type core_fee_required = k.basic_fee;\n   // Note: practically the `*` won't cause an integer overflow, because k.price_per_byte is 32 bit\n   //       and the results of pack_size() won't be too big\n   core_fee_required += k.price_per_byte * (fc::raw::pack_size(restrictions) + fc::raw::pack_size(auth));\n   return core_fee_required;\n}\n\nvoid custom_authority_create_operation::validate()const {\n   FC_ASSERT(fee.amount >= 0, \"Fee amount can not be negative\");\n\n   FC_ASSERT(account != GRAPHENE_TEMP_ACCOUNT\n             && account != GRAPHENE_COMMITTEE_ACCOUNT\n             && account != GRAPHENE_WITNESS_ACCOUNT\n             && account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,\n             \"Can not create custom authority for special accounts\");\n\n   FC_ASSERT(valid_from < valid_to, \"valid_from must be earlier than valid_to\");\n\n   // Note: The authentication authority can be empty, but it cannot be impossible to satisify. Disable the authority\n   // using the `enabled` boolean rather than setting an impossible authority.\n\n   FC_ASSERT(auth.address_auths.size() == 0, \"Address authorities are not supported\");\n   FC_ASSERT(!auth.is_impossible(), \"Cannot use an imposible authority threshold\");\n\n   // Validate restrictions by constructing a predicate for them; this throws if restrictions aren't valid\n   get_restriction_predicate(restrictions, operation_type);\n}\n\nshare_type custom_authority_update_operation::calculate_fee(const fee_parameters_type& k)const {\n   share_type core_fee_required = k.basic_fee;\n   // Note: practically the `*` won't cause an integer overflow, because k.price_per_byte is 32 bit\n   //       and the results of pack_size() won't be too big\n   core_fee_required += k.price_per_byte * fc::raw::pack_size(restrictions_to_add);\n   if (new_auth)\n      core_fee_required += k.price_per_byte * fc::raw::pack_size(*new_auth);\n   return core_fee_required;\n}\n\nvoid custom_authority_update_operation::validate()const {\n   FC_ASSERT(fee.amount >= 0, \"Fee amount can not be negative\");\n\n   FC_ASSERT(account != GRAPHENE_TEMP_ACCOUNT\n             && account != GRAPHENE_COMMITTEE_ACCOUNT\n             && account != GRAPHENE_WITNESS_ACCOUNT\n             && account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,\n             \"Can not create custom authority for special accounts\");\n   if (new_valid_from && new_valid_to)\n      FC_ASSERT(*new_valid_from < *new_valid_to, \"valid_from must be earlier than valid_to\");\n   if (new_auth) {\n      FC_ASSERT(!new_auth->is_impossible(), \"Cannot use an impossible authority threshold\");\n      FC_ASSERT(new_auth->address_auths.size() == 0, \"Address auth is not supported\");\n   }\n   FC_ASSERT( new_enabled.valid() || new_valid_from.valid() || new_valid_to.valid() || new_auth.valid()\n              || !restrictions_to_remove.empty() || !restrictions_to_add.empty(),\n              \"Must update something\" );\n}\n\nvoid custom_authority_delete_operation::validate()const {\n   FC_ASSERT(fee.amount >= 0, \"Fee amount can not be negative\");\n\n   FC_ASSERT(account != GRAPHENE_TEMP_ACCOUNT\n             && account != GRAPHENE_COMMITTEE_ACCOUNT\n             && account != GRAPHENE_WITNESS_ACCOUNT\n             && account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,\n             \"Can not delete custom authority for special accounts\");\n}\n\n} } // graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/fee_schedule.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <algorithm>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n\n#define MAX_FEE_STABILIZATION_ITERATION 4\n\nnamespace graphene { namespace protocol {\n\n   fee_schedule::fee_schedule()\n   {\n   }\n\n   fee_schedule fee_schedule::get_default()\n   {\n      fee_schedule result;\n      for( size_t i = 0; i < fee_parameters().count(); ++i )\n      {\n         fee_parameters x; x.set_which(i);\n         result.parameters.insert(x);\n      }\n      return result;\n   }\n\n   struct set_fee_visitor\n   {\n      typedef void result_type;\n      asset _fee;\n\n      set_fee_visitor( asset f ):_fee(f){}\n\n      template<typename OpType>\n      void operator()( OpType& op )const\n      {\n         op.fee = _fee;\n      }\n   };\n\n   struct zero_fee_visitor\n   {\n      typedef void result_type;\n\n      template<typename ParamType>\n      result_type operator()(  ParamType& op )const\n      {\n         memset( (char*)&op, 0, sizeof(op) );\n      }\n   };\n\n   void fee_schedule::zero_all_fees()\n   {\n      *this = get_default();\n      for( fee_parameters& i : parameters )\n         i.visit( zero_fee_visitor() );\n      this->scale = 0;\n   }\n\n   asset fee_schedule::set_fee( operation& op, const price& core_exchange_rate )const\n   {\n      auto f = calculate_fee( op, core_exchange_rate );\n      auto f_max = f;\n      for( size_t i=0; i<MAX_FEE_STABILIZATION_ITERATION; i++ )\n      {\n         op.visit( set_fee_visitor( f_max ) );\n         auto f2 = calculate_fee( op, core_exchange_rate );\n         if( f == f2 )\n            break;\n         f_max = std::max( f_max, f2 );\n         f = f2;\n         if( i == 0 )\n         {\n            // no need for warnings on later iterations\n            wlog( \"set_fee requires multiple iterations to stabilize with core_exchange_rate ${p} on operation ${op}\",\n               (\"p\", core_exchange_rate) (\"op\", op) );\n         }\n      }\n      return f_max;\n   }\n\n} } // graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/fee_schedule_calc.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/uint128.hpp>\n\n#define MAX_FEE_STABILIZATION_ITERATION 4\n\nnamespace graphene { namespace protocol {\n\n   struct calc_fee_visitor\n   {\n      typedef uint64_t result_type;\n\n      const fee_schedule& param;\n      const int current_op;\n      calc_fee_visitor( const fee_schedule& p, const operation& op ):param(p),current_op(op.which()){}\n\n      template<typename OpType>\n      result_type operator()( const OpType& op )const\n      {\n         try {\n            return op.calculate_fee( param.get<OpType>() ).value;\n         } catch (fc::assert_exception& e) {\n             fee_parameters params; params.set_which(current_op);\n             auto itr = param.parameters.find(params);\n             if( itr != param.parameters.end() ) params = *itr;\n             return op.calculate_fee( params.get<typename OpType::fee_parameters_type>() ).value;\n         }\n      }\n   };\n\n   template<>\n   uint64_t calc_fee_visitor::operator()(const htlc_create_operation& op)const\n   {\n      //TODO: refactor for performance (see https://github.com/bitshares/bitshares-core/issues/2150)\n      transfer_operation::fee_parameters_type t;\n      if (param.exists<transfer_operation>())\n         t = param.get<transfer_operation>();\n      return op.calculate_fee( param.get<htlc_create_operation>(), t.price_per_kbyte).value;\n   }\n\n   template<>\n   uint64_t calc_fee_visitor::operator()(const asset_create_operation& op)const\n   {\n      //TODO: refactor for performance (see https://github.com/bitshares/bitshares-core/issues/2150)\n      optional<uint64_t> sub_asset_creation_fee;\n      if( param.exists<account_transfer_operation>() && param.exists<ticket_create_operation>() )\n         sub_asset_creation_fee = param.get<account_transfer_operation>().fee;\n      asset_create_operation::fee_parameters_type old_asset_creation_fee_params;\n      if( param.exists<asset_create_operation>() )\n         old_asset_creation_fee_params = param.get<asset_create_operation>();\n      return op.calculate_fee( old_asset_creation_fee_params, sub_asset_creation_fee ).value;\n   }\n\n   asset fee_schedule::calculate_fee( const operation& op )const\n   {\n      uint64_t required_fee = op.visit( calc_fee_visitor( *this, op ) );\n      if( scale != GRAPHENE_100_PERCENT )\n      {\n         auto scaled = fc::uint128_t(required_fee) * scale;\n         scaled /= GRAPHENE_100_PERCENT;\n         FC_ASSERT( scaled <= GRAPHENE_MAX_SHARE_SUPPLY,\n                    \"Required fee after scaling would exceed maximum possible supply\" );\n         required_fee = static_cast<uint64_t>(scaled);\n      }\n      return asset( required_fee );\n   }\n\n   asset fee_schedule::calculate_fee( const operation& op, const price& core_exchange_rate )const\n   {\n      return calculate_fee( op ).multiply_and_round_up( core_exchange_rate );\n   }\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::fee_schedule )\n"
  },
  {
    "path": "libraries/protocol/htlc.cpp",
    "content": "/*\n * Copyright (c) 2018 jmjatlanta and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/htlc.hpp>\n\n#include <fc/io/raw.hpp>\n\n#define SECONDS_PER_DAY (60 * 60 * 24)\n\nnamespace graphene { namespace protocol {\n\n   void htlc_create_operation::validate()const {\n      FC_ASSERT( fee.amount >= 0, \"Fee amount should not be negative\" );\n      FC_ASSERT( amount.amount > 0, \"HTLC amount should be greater than zero\" );\n   }\n\n   share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params, \n         uint32_t fee_per_kb )const\n   {\n      uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY;\n      // multiply with overflow check\n      share_type total_fee = fee_params.fee; \n      total_fee += share_type(fee_params.fee_per_day) * days;\n      if (extensions.value.memo.valid())\n         total_fee += calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_per_kb);\n      return total_fee;\n   }\n\n   void htlc_redeem_operation::validate()const {\n      FC_ASSERT( fee.amount >= 0, \"Fee amount should not be negative\" );\n   }\n\n   share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const\n   {\n      uint64_t kb = ( preimage.size() + 1023 ) / 1024;\n      uint64_t product = kb * fee_params.fee_per_kb;\n      FC_ASSERT( kb == 0 || product / kb == fee_params.fee_per_kb, \"Fee calculation overflow\");\n      return fee_params.fee + product;\n   }\n\n   void htlc_extend_operation::validate()const {\n      FC_ASSERT( fee.amount >= 0 , \"Fee amount should not be negative\");\n   }\n\n   share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const\n   {\n      uint32_t days = ( seconds_to_add + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY;\n      uint64_t per_day_fee = fee_params.fee_per_day * days;\n      FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, \"Fee calculation overflow\" );\n      return fee_params.fee + per_day_fee;\n   }\n} }\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::additional_options_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeem_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_extend_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeem_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeemed_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_extend_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_refund_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/README.md",
    "content": "Protocol Definition \n--------------------\n\nThe classes declared in these headers provide the complete definition of the \nGraphene protocol and are organized according to feature.   Nothing in this\ndirectory should depend upon anything other than fc or other types defined\nin the protocol directory.  \n\nTo be more specific, implementation details such as the objects defined in\nthe object database should not be required here.\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/account.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/authority.hpp>\n#include <graphene/protocol/buyback.hpp>\n#include <graphene/protocol/special_authority.hpp>\n#include <graphene/protocol/vote.hpp>\n\nnamespace graphene { namespace protocol {\n\n   bool is_valid_name( const string& s );\n   bool is_cheap_name( const string& n );\n\n   /// These are the fields which can be updated by the active authority.\n   struct account_options\n   {\n      /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-\n      /// validated account activities. This field is here to prevent confusion if the active authority has zero or\n      /// multiple keys in it.\n      public_key_type  memo_key;\n      /// If this field is set to an account ID other than GRAPHENE_PROXY_TO_SELF_ACCOUNT,\n      /// then this account's votes will be ignored; its stake\n      /// will be counted as voting for the referenced account's selected votes instead.\n      account_id_type voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n\n      /// The number of active witnesses this account votes the blockchain should appoint\n      /// Must not exceed the actual number of witnesses voted for in @ref votes\n      uint16_t num_witness = 0;\n      /// The number of active committee members this account votes the blockchain should appoint\n      /// Must not exceed the actual number of committee members voted for in @ref votes\n      uint16_t num_committee = 0;\n      /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this\n      /// account's balance of core asset.\n      flat_set<vote_id_type> votes;\n      extensions_type        extensions;\n\n      /// Whether this account is voting\n      inline bool is_voting() const\n      {\n         return ( voting_account != GRAPHENE_PROXY_TO_SELF_ACCOUNT || !votes.empty() );\n      }\n\n      uint16_t num_committee_voted() const\n      {\n         if( voting_account != GRAPHENE_PROXY_TO_SELF_ACCOUNT )\n            return 0;\n         return std::count_if( votes.begin(), votes.end(),\n                               [](vote_id_type v){ return v.type() == vote_id_type::vote_type::committee; } );\n      }\n\n      void validate()const;\n   };\n\n   /**\n    *  @ingroup operations\n    */\n   struct account_create_operation : public base_operation\n   {\n      struct ext\n      {\n         optional< void_t >            null_ext;\n         optional< special_authority > owner_special_authority;\n         optional< special_authority > active_special_authority;\n         optional< buyback_account_options > buyback_options;\n      };\n\n      struct fee_parameters_type\n      {\n         uint64_t basic_fee      = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account\n         uint64_t premium_fee    = 2000*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account\n         uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset           fee;\n      /// This account pays the fee. Must be a lifetime member.\n      account_id_type registrar;\n\n      /// This account receives a portion of the fee split between registrar and referrer. Must be a member.\n      account_id_type referrer;\n      /// Of the fee split between registrar and referrer, this percentage goes to the referrer. The rest goes to the\n      /// registrar.\n      uint16_t        referrer_percent = 0;\n\n      string          name;\n      authority       owner;\n      authority       active;\n\n      account_options options;\n      extension< ext > extensions;\n\n      account_id_type fee_payer()const { return registrar; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& )const;\n\n      void get_required_active_authorities( flat_set<account_id_type>& a )const\n      {\n         // registrar should be required anyway as it is the fee_payer(), but we insert it here just to be sure\n         a.insert( registrar );\n         if( extensions.value.buyback_options.valid() )\n            a.insert( extensions.value.buyback_options->asset_to_buy_issuer );\n      }\n   };\n\n   /**\n    * @ingroup operations\n    * @brief Update an existing account\n    *\n    * This operation is used to update an existing account. It can be used to update the authorities, or adjust the options on the account.\n    * See @ref account_object::options_type for the options which may be updated.\n    */\n   struct account_update_operation : public base_operation\n   {\n      struct ext\n      {\n         optional< void_t >            null_ext;\n         optional< special_authority > owner_special_authority;\n         optional< special_authority > active_special_authority;\n      };\n\n      struct fee_parameters_type\n      {\n         share_type fee             = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t   price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset fee;\n      /// The account to update\n      account_id_type account;\n\n      /// New owner authority. If set, this operation requires owner authority to execute.\n      optional<authority> owner;\n      /// New active authority. This can be updated by the current active authority.\n      optional<authority> active;\n\n      /// New account options\n      optional<account_options> new_options;\n      extension< ext > extensions;\n\n      account_id_type fee_payer()const { return account; }\n      void       validate()const;\n      share_type calculate_fee( const fee_parameters_type& k )const;\n\n      bool is_owner_update()const\n      { return owner || extensions.value.owner_special_authority.valid(); }\n\n      void get_required_owner_authorities( flat_set<account_id_type>& a )const\n      { if( is_owner_update() ) a.insert( account ); }\n\n      void get_required_active_authorities( flat_set<account_id_type>& a )const\n      { if( !is_owner_update() ) a.insert( account ); }\n   };\n\n   /**\n    * @brief This operation is used to whitelist and blacklist accounts, primarily for transacting in whitelisted assets\n    * @ingroup operations\n    *\n    * Accounts can freely specify opinions about other accounts, in the form of either whitelisting or blacklisting\n    * them. This information is used in chain validation only to determine whether an account is authorized to transact\n    * in an asset type which enforces a whitelist, but third parties can use this information for other uses as well,\n    * as long as it does not conflict with the use of whitelisted assets.\n    *\n    * An asset which enforces a whitelist specifies a list of accounts to maintain its whitelist, and a list of\n    * accounts to maintain its blacklist. In order for a given account A to hold and transact in a whitelisted asset S,\n    * A must be whitelisted by at least one of S's whitelist_authorities and blacklisted by none of S's\n    * blacklist_authorities. If A receives a balance of S, and is later removed from the whitelist(s) which allowed it\n    * to hold S, or added to any blacklist S specifies as authoritative, A's balance of S will be frozen until A's\n    * authorization is reinstated.\n    *\n    * This operation requires authorizing_account's signature, but not account_to_list's. The fee is paid by\n    * authorizing_account.\n    */\n   struct account_whitelist_operation : public base_operation\n   {\n      struct fee_parameters_type { share_type fee = 300000; };\n      enum account_listing {\n         no_listing = 0x0, ///< No opinion is specified about this account\n         white_listed = 0x1, ///< This account is whitelisted, but not blacklisted\n         black_listed = 0x2, ///< This account is blacklisted, but not whitelisted\n         white_and_black_listed = white_listed | black_listed ///< This account is both whitelisted and blacklisted\n      };\n\n      /// Paid by authorizing_account\n      asset           fee;\n      /// The account which is specifying an opinion of another account\n      account_id_type authorizing_account;\n      /// The account being opined about\n      account_id_type account_to_list;\n      /// The new white and blacklist status of account_to_list, as determined by authorizing_account\n      /// This is a bitfield using values defined in the account_listing enum\n      uint8_t new_listing = no_listing;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return authorizing_account; }\n      void validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT(new_listing < 0x4); }\n   };\n\n   /**\n    * @brief Manage an account's membership status\n    * @ingroup operations\n    *\n    * This operation is used to upgrade an account to a member, or renew its subscription. If an account which is an\n    * unexpired annual subscription member publishes this operation with @ref upgrade_to_lifetime_member set to false,\n    * the account's membership expiration date will be pushed backward one year. If a basic account publishes it with\n    * @ref upgrade_to_lifetime_member set to false, the account will be upgraded to a subscription member with an\n    * expiration date one year after the processing time of this operation.\n    *\n    * Any account may use this operation to become a lifetime member by setting @ref upgrade_to_lifetime_member to\n    * true. Once an account has become a lifetime member, it may not use this operation anymore.\n    */\n   struct account_upgrade_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         uint64_t membership_annual_fee   =  2000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint64_t membership_lifetime_fee = 10000 * GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to upgrade to a lifetime member\n      };\n\n      asset             fee;\n      /// The account to upgrade; must not already be a lifetime member\n      account_id_type   account_to_upgrade;\n      /// If true, the account will be upgraded to a lifetime member; otherwise, it will add a year to the subscription\n      bool              upgrade_to_lifetime_member = false;\n      extensions_type   extensions;\n\n      account_id_type fee_payer()const { return account_to_upgrade; }\n      void       validate()const;\n      share_type calculate_fee( const fee_parameters_type& k )const;\n   };\n\n   /**\n    * @brief transfers the account to another account while clearing the white list\n    * @ingroup operations\n    *\n    * In theory an account can be transferred by simply updating the authorities, but that kind\n    * of transfer lacks semantic meaning and is more often done to rotate keys without transferring\n    * ownership.   This operation is used to indicate the legal transfer of title to this account and\n    * a break in the operation history.\n    *\n    * The account_id's owner/active/voting/memo authority should be set to new_owner\n    *\n    * This operation will clear the account's whitelist statuses, but not the blacklist statuses.\n    */\n   struct account_transfer_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;\n      account_id_type account_id;\n      account_id_type new_owner;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return account_id; }\n      void        validate()const;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT(graphene::protocol::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))\nFC_REFLECT_ENUM( graphene::protocol::account_whitelist_operation::account_listing,\n                (no_listing)(white_listed)(black_listed)(white_and_black_listed))\n\nFC_REFLECT(graphene::protocol::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(buyback_options) )\nFC_REFLECT_TYPENAME(graphene::protocol::extension<graphene::protocol::account_create_operation::ext>)\nFC_REFLECT( graphene::protocol::account_create_operation,\n            (fee)(registrar)\n            (referrer)(referrer_percent)\n            (name)(owner)(active)(options)(extensions)\n          )\n\nFC_REFLECT(graphene::protocol::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) )\nFC_REFLECT_TYPENAME(graphene::protocol::extension<graphene::protocol::account_update_operation::ext>)\nFC_REFLECT( graphene::protocol::account_update_operation,\n            (fee)(account)(owner)(active)(new_options)(extensions)\n          )\n\nFC_REFLECT( graphene::protocol::account_upgrade_operation,\n            (fee)(account_to_upgrade)(upgrade_to_lifetime_member)(extensions) )\n\nFC_REFLECT( graphene::protocol::account_whitelist_operation, (fee)(authorizing_account)(account_to_list)(new_listing)(extensions))\n\nFC_REFLECT( graphene::protocol::account_create_operation::fee_parameters_type, (basic_fee)(premium_fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::account_whitelist_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::account_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::account_upgrade_operation::fee_parameters_type, (membership_annual_fee)(membership_lifetime_fee) )\nFC_REFLECT( graphene::protocol::account_transfer_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_options )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/address.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n\n#include <fc/crypto/elliptic.hpp>\n#include <fc/crypto/ripemd160.hpp>\n\nnamespace graphene { namespace protocol {\n   struct pts_address;\n\n   /**\n    *  @brief a 160 bit hash of a public key\n    *\n    *  An address can be converted to or from a base58 string with 32 bit checksum.\n    *\n    *  An address is calculated as ripemd160( sha512( compressed_ecc_public_key ) )\n    *\n    *  When converted to a string, checksum calculated as the first 4 bytes ripemd160( address ) is\n    *  appended to the binary address before converting to base58.\n    */\n   class address\n   {\n      public:\n       address(){} ///< constructs empty / null address\n       explicit address( const std::string& base58str );   ///< converts to binary, validates checksum\n       address( const fc::ecc::public_key& pub ); ///< converts to binary\n       explicit address( const fc::ecc::public_key_data& pub ); ///< converts to binary\n       address( const pts_address& pub ); ///< converts to binary\n       address( const public_key_type& pubkey );\n\n       static bool is_valid( const std::string& base58str, const std::string& prefix = GRAPHENE_ADDRESS_PREFIX );\n\n       explicit operator std::string()const; ///< converts to base58 + checksum\n\n       fc::ripemd160 addr;\n   };\n   inline bool operator == ( const address& a, const address& b ) { return a.addr == b.addr; }\n   inline bool operator != ( const address& a, const address& b ) { return a.addr != b.addr; }\n   inline bool operator <  ( const address& a, const address& b ) { return a.addr <  b.addr; }\n\n} } // namespace graphene::protocol\n\nnamespace fc\n{\n   void to_variant( const graphene::protocol::address& var,  fc::variant& vo, uint32_t max_depth = 1 );\n   void from_variant( const fc::variant& var,  graphene::protocol::address& vo, uint32_t max_depth = 1 );\n}\n\nFC_REFLECT( graphene::protocol::address, (addr) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::address )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/assert.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol { \n\n   /**\n    *  Used to verify that account_id->name is equal to the given string literal.\n    */\n   struct account_name_eq_lit_predicate\n   {\n      account_id_type account_id;\n      string          name;\n\n      /**\n       *  Perform state-independent checks.  Verify\n       *  account_name is a valid account name.\n       */\n      bool validate()const;\n   };\n\n   /**\n    *  Used to verify that asset_id->symbol is equal to the given string literal.\n    */\n   struct asset_symbol_eq_lit_predicate\n   {\n      asset_id_type   asset_id;\n      string          symbol;\n\n      /**\n       *  Perform state independent checks.  Verify symbol is a\n       *  valid asset symbol.\n       */\n      bool validate()const;\n\n   };\n\n   /**\n    * Used to verify that a specific block is part of the\n    * blockchain history.  This helps protect some high-value\n    * transactions to newly created IDs\n    *\n    * The block ID must be within the last 2^16 blocks.\n    */\n   struct block_id_predicate\n   {\n      block_id_type id;\n      bool validate()const{ return true; }\n   };\n\n   /**\n    *  When defining predicates do not make the protocol dependent upon\n    *  implementation details.\n    */\n   typedef static_variant<\n      account_name_eq_lit_predicate,\n      asset_symbol_eq_lit_predicate,\n      block_id_predicate\n     > predicate;\n\n\n   /**\n    *  @brief assert that some conditions are true.\n    *  @ingroup operations\n    *\n    *  This operation performs no changes to the database state, but can but used to verify\n    *  pre or post conditions for other operations.\n    */\n   struct assert_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                      fee;\n      account_id_type            fee_paying_account;\n      vector<predicate>          predicates;\n      flat_set<account_id_type>  required_auths;\n      extensions_type            extensions;\n\n      account_id_type fee_payer()const { return fee_paying_account; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::assert_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::account_name_eq_lit_predicate, (account_id)(name) )\nFC_REFLECT( graphene::protocol::asset_symbol_eq_lit_predicate, (asset_id)(symbol) )\nFC_REFLECT( graphene::protocol::block_id_predicate, (id) )\nFC_REFLECT_TYPENAME( graphene::protocol::predicate )\nFC_REFLECT( graphene::protocol::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) )\n \nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::assert_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::assert_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/asset.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace protocol {\n\n   extern const int64_t scaled_precision_lut[];\n\n   struct price;\n\n   struct asset\n   {\n      asset( share_type a = 0, asset_id_type id = asset_id_type() )\n      :amount(a),asset_id(id){}\n\n      share_type    amount;\n      asset_id_type asset_id;\n\n      asset& operator += ( const asset& o )\n      {\n         FC_ASSERT( asset_id == o.asset_id );\n         amount += o.amount;\n         return *this;\n      }\n      asset& operator -= ( const asset& o )\n      {\n         FC_ASSERT( asset_id == o.asset_id );\n         amount -= o.amount;\n         return *this;\n      }\n      asset operator -()const { return asset( -amount, asset_id ); }\n\n      friend bool operator == ( const asset& a, const asset& b )\n      {\n         return std::tie( a.asset_id, a.amount ) == std::tie( b.asset_id, b.amount );\n      }\n      friend bool operator < ( const asset& a, const asset& b )\n      {\n         FC_ASSERT( a.asset_id == b.asset_id );\n         return a.amount < b.amount;\n      }\n      friend inline bool operator <= ( const asset& a, const asset& b )\n      {\n         return !(b < a);\n      }\n\n      friend inline bool operator != ( const asset& a, const asset& b )\n      {\n         return !(a == b);\n      }\n      friend inline bool operator > ( const asset& a, const asset& b )\n      {\n         return (b < a);\n      }\n      friend inline bool operator >= ( const asset& a, const asset& b )\n      {\n         return !(a < b);\n      }\n\n      friend asset operator - ( const asset& a, const asset& b )\n      {\n         FC_ASSERT( a.asset_id == b.asset_id );\n         return asset( a.amount - b.amount, a.asset_id );\n      }\n      friend asset operator + ( const asset& a, const asset& b )\n      {\n         FC_ASSERT( a.asset_id == b.asset_id );\n         return asset( a.amount + b.amount, a.asset_id );\n      }\n\n      static share_type scaled_precision( uint8_t precision )\n      {\n         FC_ASSERT( precision < 19 );\n         return scaled_precision_lut[ precision ];\n      }\n\n      asset multiply_and_round_up( const price& p )const; ///< Multiply and round up\n   };\n\n   /**\n    * @brief The price struct stores asset prices in the BitShares system.\n    *\n    * A price is defined as a ratio between two assets, and represents a possible exchange rate between those two\n    * assets. prices are generally not stored in any simplified form, i.e. a price of (1000 CORE)/(20 USD) is perfectly\n    * normal.\n    *\n    * The assets within a price are labeled base and quote. Throughout the BitShares code base, the convention used is\n    * that the base asset is the asset being sold, and the quote asset is the asset being purchased, where the price is\n    * represented as base/quote, so in the example price above the seller is looking to sell CORE asset and get USD in\n    * return.\n    */\n   struct price\n   {\n      explicit price(const asset& _base = asset(), const asset& _quote = asset())\n         : base(_base),quote(_quote){}\n\n      asset base;\n      asset quote;\n\n      static price max(asset_id_type base, asset_id_type quote );\n      static price min(asset_id_type base, asset_id_type quote );\n\n      static price call_price(const asset& debt, const asset& collateral, uint16_t collateral_ratio);\n\n      /// The unit price for an asset type A is defined to be a price such that for any asset m, m*A=m\n      static price unit_price(asset_id_type a = asset_id_type()) { return price(asset(1, a), asset(1, a)); }\n\n      price max()const { return price::max( base.asset_id, quote.asset_id ); }\n      price min()const { return price::min( base.asset_id, quote.asset_id ); }\n\n      double to_real()const { return double(base.amount.value)/double(quote.amount.value); }\n\n      bool is_null()const;\n      void validate()const;\n   };\n\n   price operator / ( const asset& base, const asset& quote );\n   inline price operator~( const price& p ) { return price{p.quote,p.base}; }\n\n   bool  operator <  ( const price& a, const price& b );\n   bool  operator == ( const price& a, const price& b );\n\n   inline bool  operator >  ( const price& a, const price& b ) { return (b < a); }\n   inline bool  operator <= ( const price& a, const price& b ) { return !(b < a); }\n   inline bool  operator >= ( const price& a, const price& b ) { return !(a < b); }\n   inline bool  operator != ( const price& a, const price& b ) { return !(a == b); }\n\n   asset operator *  ( const asset& a, const price& b ); ///< Multiply and round down\n\n   price operator *  ( const price& p, const ratio_type& r );\n   price operator /  ( const price& p, const ratio_type& r );\n\n   inline price& operator *=  ( price& p, const ratio_type& r )\n   { return p = p * r; }\n   inline price& operator /=  ( price& p, const ratio_type& r )\n   { return p = p / r; }\n\n   /**\n    *  @class price_feed\n    *  @brief defines market parameters for margin positions\n    */\n   struct price_feed\n   {\n      /**\n       *  Required maintenance collateral is defined\n       *  as a fixed point number with a maximum value of 10.000\n       *  and a minimum value of 1.000.  (denominated in GRAPHENE_COLLATERAL_RATIO_DENOM)\n       *\n       *  A black swan event occurs when value_of_collateral equals\n       *  value_of_debt * MSSR.  To avoid a black swan a margin call is\n       *  executed when value_of_debt * required_maintenance_collateral\n       *  equals value_of_collateral using rate.\n       *\n       *  Default requirement is $1.75 of collateral per $1 of debt\n       *\n       *  BlackSwan ---> SQR ---> MCR ----> SP\n       */\n      ///@{\n      /**\n       * Forced settlements will evaluate using this price, defined as BITASSET / COLLATERAL\n       */\n      price settlement_price;\n\n      /// Price at which automatically exchanging this asset for CORE from fee pool occurs (used for paying fees)\n      price core_exchange_rate;\n\n      /** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */\n      uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;\n\n      /** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */\n      uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;\n\n      /**\n       * This is the price at which a call order will relinquish COLLATERAL when margin called. It is\n       * also the price that establishes the minimum amount of collateral per debt that call orders must\n       * maintain to avoid possibility of black swan.  A call order maintaining less collateral per debt\n       * than this price is unable to meet the combined obligation to sell collateral at the Margin Call\n       * Offer Price (MCOP) *AND* to pay the margin call fee. The MSSP is related to the MCOP, but the\n       * MSSP accounts for the need to reserve extra collateral to pay the margin call fee, whereas the\n       * MCOP only accounts for the collateral to be traded to the call buyer.  Prior to the\n       * introduction of the Margin Call Fee Ratio (MCFR) with BSIP-74, the two prices (MSSP and MCOP)\n       * were identical, and MSSP could be thought of as \"the price at which you are forced to sell\n       * collateral if margin called,\" but this latter concept is now embodied by the MCOP.\n       *\n       * The Maximum Short Squeeze Price is computed as follows, in units of DEBT per COLLATERAL:\n       *\n       *   MSSP = settlement_price / MSSR;\n       *\n       * @return The MSSP in units of DEBT per COLLATERAL.\n       */\n      price max_short_squeeze_price()const;\n      /**\n       * Older implementation of max_short_squeeze_price() due to hardfork changes. It came with\n       * the following commentary:\n       *\n       * When selling collateral to pay off debt, the least amount of debt to receive should be\n       *  min_usd = max_short_squeeze_price() * collateral\n       *\n       *  This is provided to ensure that a black swan cannot be trigged due to poor liquidity alone, it\n       *  must be confirmed by having the max_short_squeeze_price() move below the black swan price.\n       * @returns the Maximum Short Squeeze price for this asset\n       */\n      price max_short_squeeze_price_before_hf_1270()const;\n\n      /**\n       * Compute price at which margin calls offer to sell collateral.\n       *\n       * Margin calls offer a greater amount of COLLATERAL asset to the market to buy back DEBT\n       * asset than would otherwise be required in a fair exchange at the settlement_price.\n       * (I.e. they sell collateral \"cheaper\" than its price feed value.) This is done to attract a\n       * quick buyer of the call in order to preserve healthy collateralization of the DEBT asset\n       * overall.  The price at which the call is offered, in comparison to the settlement price, is\n       * determined by the Maximum Short Squeeze Ratio (MSSR) and the Margin Call Fee Ratio (MCFR)\n       * as follows, in units of DEBT per COLLATERAL:\n       *\n       *   MCOP = settlement_price / (MSSR - MCFR);\n       *\n       * Compare with Maximum Short Squeeze Price (MSSP), which is computed as follows:\n       *\n       *   MSSP = settlement_price / MSSR;\n       *\n       * Since BSIP-74, we distinguish between Maximum Short Squeeze Price (MSSP) and Margin Call\n       * Order Price (MCOP). Margin calls previously offered collateral at the MSSP, but now they\n       * offer slightly less collateral per debt if Margin Call Fee Ratio (MCFR) is set, because\n       * the call order must reserve some collateral to pay the fee.  We must still retain the\n       * concept of MSSP, as it communicates the minimum collateralization before black swan may be\n       * triggered, but we add this new method to calculate MCOP.\n       *\n       * Note that when we calculate the MCOP, we enact a price floor to ensure the margin call never\n       * offers LESS collateral than the DEBT is worth. As such, it's important to calculate the\n       * realized fee, when trading at the offer price, as a delta between total relinquished collateral\n       * (DEBT*MSSP) and collateral sold to the buyer (DEBT*MCOP).  If you instead try to calculate the\n       * fee by direct multiplication of MCFR, you will get the wrong answer if the price was\n       * floored. (Fee is truncated when price is floored.)\n       *\n       * @param margin_call_fee_ratio MCFR value currently in effect. If zero or unset, returns\n       *    same result as @ref max_short_squeeze_price().\n       *\n       * @return The MCOP in units of DEBT per COLLATERAL.\n       */\n      price margin_call_order_price(const fc::optional<uint16_t> margin_call_fee_ratio)const;\n\n      /**\n       * Ratio between max_short_squeeze_price and margin_call_order_price.\n       *\n       * This ratio, if it multiplied margin_call_order_price (expressed in DEBT/COLLATERAL), would\n       * yield the max_short_squeeze_price, apart perhaps for truncation (rounding) error.\n       *\n       * When a margin call is taker, matching an existing order on the books, it is possible the call\n       * gets a better realized price than the order price that it offered at.  In this case, the margin\n       * call fee is proportionaly reduced. This ratio is used to calculate the price at which the call\n       * relinquishes collateral (to meet both trade and fee obligations) based on actual realized match\n       * price.\n       *\n       * This function enacts the same flooring as margin_call_order_price() (MSSR - MCFR is floored at\n       * 1.00).  This ensures we apply the same fee truncation in the taker case as the maker case.\n       *\n       * @return (MSSR - MCFR) / MSSR\n       */\n      ratio_type margin_call_pays_ratio(const fc::optional<uint16_t> margin_call_fee_ratio)const;\n\n      /// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call\n      /// territory.\n      /// Calculation: ~settlement_price * maintenance_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM\n      price maintenance_collateralization()const;\n\n      /// Whether the parameters that affect margin calls in this price feed object are the same as the parameters\n      /// in the passed-in object\n      bool margin_call_params_equal( const price_feed& b ) const\n      {\n         if( this == &b )\n            return true;\n         return std::tie(   settlement_price,   maintenance_collateral_ratio,   maximum_short_squeeze_ratio ) ==\n                std::tie( b.settlement_price, b.maintenance_collateral_ratio, b.maximum_short_squeeze_ratio );\n      }\n      ///@}\n\n      void validate() const;\n      bool is_for( asset_id_type asset_id ) const;\n   };\n\n} }\n\nFC_REFLECT( graphene::protocol::asset, (amount)(asset_id) )\nFC_REFLECT( graphene::protocol::price, (base)(quote) )\n\nFC_REFLECT( graphene::protocol::price_feed,\n            (settlement_price)(maintenance_collateral_ratio)(maximum_short_squeeze_ratio)(core_exchange_rate) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::price )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::price_feed )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/asset_ops.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/memo.hpp>\n\nnamespace graphene { namespace protocol { \n\n   struct additional_asset_options\n   {\n      fc::optional<uint16_t>                  reward_percent;\n      fc::optional<flat_set<account_id_type>> whitelist_market_fee_sharing;\n      // After BSIP81 activation, taker_fee_percent is the taker fee\n      fc::optional<uint16_t>                  taker_fee_percent;\n   };\n   typedef extension<additional_asset_options> additional_asset_options_t;\n\n   bool is_valid_symbol( const string& symbol );\n\n   /**\n    * @brief The asset_options struct contains options available on all assets in the network\n    *\n    * @note Changes to this struct will break protocol compatibility\n    */\n   struct asset_options {\n      /// The maximum supply of this asset which may exist at any given time. This can be as large as\n      /// GRAPHENE_MAX_SHARE_SUPPLY\n      share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      /// When this asset is traded on the markets, this percentage of the total traded will be exacted and paid\n      /// to the issuer. This is a fixed point value, representing hundredths of a percent, i.e. a value of 100\n      /// in this field means a 1% fee is charged on market trades of this asset.\n      // BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders\n      // After BSIP81 activation, market_fee_percent is the maker fee\n      uint16_t market_fee_percent = 0;\n      /// Market fees calculated as @ref market_fee_percent of the traded volume are capped to this value\n      share_type max_market_fee = GRAPHENE_MAX_SHARE_SUPPLY;\n\n      /// The flags which the issuer has permission to update. See @ref asset_issuer_permission_flags\n      uint16_t issuer_permissions = DEFAULT_UIA_ASSET_ISSUER_PERMISSION;\n      /// The currently active flags on this permission. See @ref asset_issuer_permission_flags\n      uint16_t flags = 0;\n\n      /// @return the bits in @ref flags which are allowed to be updated according to data in @ref issuer_permissions\n      uint16_t get_enabled_issuer_permissions_mask() const;\n\n      /// When a non-core asset is used to pay a fee, the blockchain must convert that asset to core asset in\n      /// order to accept the fee. If this asset's fee pool is funded, the chain will automatically deposite fees\n      /// in this asset to its accumulated fees, and withdraw from the fee pool the same amount as converted at\n      /// the core exchange rate.\n      price core_exchange_rate = price(asset(), asset(0, asset_id_type(1)));\n\n      /// A set of accounts which maintain whitelists to consult for this asset. If whitelist_authorities\n      /// is non-empty, then only accounts in whitelist_authorities are allowed to hold, use, or transfer the asset.\n      flat_set<account_id_type> whitelist_authorities;\n      /// A set of accounts which maintain blacklists to consult for this asset. If flags & white_list is set,\n      /// an account may only send, receive, trade, etc. in this asset if none of these accounts appears in\n      /// its account_object::blacklisting_accounts field. If the account is blacklisted, it may not transact in\n      /// this asset even if it is also whitelisted.\n      flat_set<account_id_type> blacklist_authorities;\n\n      /** defines the assets that this asset may be traded against in the market */\n      flat_set<asset_id_type>   whitelist_markets;\n      /** defines the assets that this asset may not be traded against in the market, must not overlap whitelist */\n      flat_set<asset_id_type>   blacklist_markets;\n\n      /**\n       * data that describes the meaning/purpose of this asset, fee will be charged proportional to\n       * size of description.\n       */\n      string description;\n      additional_asset_options_t extensions;\n\n      /// Perform internal consistency checks.\n      /// @throws fc::exception if any check fails\n      void validate()const;\n\n      /// Perform checks about @ref flags.\n      /// @throws fc::exception if any check fails\n      void validate_flags( bool is_market_issued )const;\n   };\n\n   /**\n    * @brief The bitasset_options struct contains configurable options available only to BitAssets.\n    *\n    * @note Changes to this struct will break protocol compatibility\n    */\n   struct bitasset_options {\n\n      struct ext\n      {\n         /// After BSIP77, when creating a new debt position or updating an existing position,\n         /// the position will be checked against this parameter.\n         /// Unused for prediction markets, although we allow it to be set for simpler implementation\n         fc::optional<uint16_t> initial_collateral_ratio;  // BSIP-77\n         /// After BSIP75, the asset owner can update MCR directly\n         fc::optional<uint16_t> maintenance_collateral_ratio; // BSIP-75\n         /// After BSIP75, the asset owner can update MSSR directly\n         fc::optional<uint16_t> maximum_short_squeeze_ratio;  // BSIP-75\n         fc::optional<uint16_t> margin_call_fee_ratio; // BSIP 74\n         fc::optional<uint16_t> force_settle_fee_percent;  // BSIP-87\n      };\n\n      /// Time before a price feed expires\n      uint32_t feed_lifetime_sec = GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME;\n      /// Minimum number of unexpired feeds required to extract a median feed from\n      uint8_t minimum_feeds = 1;\n      /// This is the delay between the time a long requests settlement and the chain evaluates the settlement\n      uint32_t force_settlement_delay_sec = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY;\n      /// This is the percent to adjust the feed price in the short's favor in the event of a forced settlement\n      uint16_t force_settlement_offset_percent = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET;\n      /// Force settlement volume can be limited such that only a certain percentage of the total existing supply\n      /// of the asset may be force-settled within any given chain maintenance interval. This field stores the\n      /// percentage of the current supply which may be force settled within the current maintenance interval. If\n      /// force settlements come due in an interval in which the maximum volume has already been settled, the new\n      /// settlements will be enqueued and processed at the beginning of the next maintenance interval.\n      uint16_t maximum_force_settlement_volume = GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME;\n      /// This speicifies which asset type is used to collateralize short sales\n      /// This field may only be updated if the current supply of the asset is zero.\n      asset_id_type short_backing_asset;\n\n      extension<ext> extensions;\n\n      /// Perform internal consistency checks.\n      /// @throws fc::exception if any check fails\n      void validate()const;\n   };\n\n\n   /**\n    * @ingroup operations\n    */\n   struct asset_create_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         uint64_t symbol3        = 500000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint64_t symbol4        = 300000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint64_t long_symbol    = 5000   * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t price_per_kbyte = 10; /// only required for large memos.\n      };\n\n      asset                   fee;\n      /// This account must sign and pay the fee for this operation. Later, this account may update the asset\n      account_id_type         issuer;\n      /// The ticker symbol of this asset\n      string                  symbol;\n      /// Number of digits to the right of decimal point, must be less than or equal to 12\n      uint8_t                 precision = 0;\n\n      /// Options common to all assets.\n      ///\n      /// @note common_options.core_exchange_rate technically needs to store the asset ID of this new asset. Since this\n      /// ID is not known at the time this operation is created, create this price as though the new asset has instance\n      /// ID 1, and the chain will overwrite it with the new asset's ID.\n      asset_options              common_options;\n      /// Options only available for BitAssets. MUST be non-null if and only if the @ref market_issued flag is set in\n      /// common_options.flags\n      optional<bitasset_options> bitasset_opts;\n      /// For BitAssets, set this to true if the asset implements a @ref prediction_market; false otherwise\n      bool is_prediction_market = false;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n      share_type      calculate_fee( const fee_parameters_type& k, optional<uint64_t> sub_asset_creation_fee )const;\n   };\n\n   /**\n    *  @brief allows global settling of bitassets (black swan or prediction markets)\n    *\n    *  In order to use this operation, @ref asset_to_settle must have the global_settle flag set\n    *\n    *  When this operation is executed all balances are converted into the backing asset at the\n    *  settle_price and all open margin positions are called at the settle price.  If this asset is\n    *  used as backing for other bitassets, those bitassets will be force settled at their current\n    *  feed price.\n    */\n   struct asset_global_settle_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;\n      account_id_type issuer; ///< must equal @ref asset_to_settle->issuer\n      asset_id_type   asset_to_settle;\n      price           settle_price;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Schedules a market-issued asset for automatic settlement\n    * @ingroup operations\n    *\n    * Holders of market-issued assests may request a forced settlement for some amount of their asset. This means that\n    * the specified sum will be locked by the chain and held for the settlement period, after which time the chain will\n    * choose a margin posision holder and buy the settled asset using the margin's collateral. The price of this sale\n    * will be based on the feed price for the market-issued asset being settled. The exact settlement price will be the\n    * feed price at the time of settlement with an offset in favor of the margin position, where the offset is a\n    * blockchain parameter set in the global_property_object.\n    *\n    * The fee is paid by @ref account, and @ref account must authorize this operation\n    */\n   struct asset_settle_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         /** this fee should be high to encourage small settlement requests to\n          * be performed on the market rather than via forced settlement. \n          *\n          * Note that in the event of a black swan or prediction market close out\n          * everyone will have to pay this fee.\n          */\n         uint64_t fee      = 100 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset           fee;\n      /// Account requesting the force settlement. This account pays the fee\n      account_id_type account;\n      /// Amount of asset to force settle. This must be a market-issued asset\n      asset           amount;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n   /**\n    * Virtual op generated when force settlement is cancelled.\n    */\n   struct asset_settle_cancel_operation : public base_operation\n   {\n      struct fee_parameters_type { };\n\n      asset           fee;\n      force_settlement_id_type settlement;\n      /// Account requesting the force settlement. This account pays the fee\n      account_id_type account;\n      /// Amount of asset to force settle. This must be a market-issued asset\n      asset           amount;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return account; }\n      /***\n       * This is a virtual operation and should never be placed in a block\n       * (i.e. in a proposal)\n       */\n      void validate() const { FC_ASSERT( !\"Virtual operation\"); }\n\n      share_type calculate_fee(const fee_parameters_type& params)const\n      { return 0; }\n   };\n\n   /**\n    * @ingroup operations\n    */\n   struct asset_fund_fee_pool_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee =  GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee; ///< core asset\n      account_id_type from_account;\n      asset_id_type   asset_id;\n      share_type      amount; ///< core asset\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return from_account; }\n      void       validate()const;\n   };\n\n   /**\n    * @brief Update options common to all assets\n    * @ingroup operations\n    *\n    * There are a number of options which all assets in the network use. These options are enumerated in the @ref\n    * asset_options struct. This operation is used to update these options for an existing asset.\n    *\n    * @note This operation cannot be used to update BitAsset-specific options. For these options, use @ref\n    * asset_update_bitasset_operation instead.\n    *\n    * @pre @ref issuer SHALL be an existing account and MUST match asset_object::issuer on @ref asset_to_update\n    * @pre @ref fee SHALL be nonnegative, and @ref issuer MUST have a sufficient balance to pay it\n    * @pre @ref new_options SHALL be internally consistent, as verified by @ref validate()\n    * @post @ref asset_to_update will have options matching those of new_options\n    */\n   struct asset_update_operation : public base_operation\n   {\n      struct ext\n      {\n         /// After BSIP48, the precision of an asset can be updated if no supply is available\n         /// @note The parties involved still need to be careful\n         fc::optional<uint8_t> new_precision;\n         /// After BSIP48, if this option is set to true, the asset's core_exchange_rate won't be updated.\n         /// This is especially useful for committee-owned bitassets which can not be updated quickly.\n         fc::optional<bool> skip_core_exchange_rate;\n      };\n\n      struct fee_parameters_type { \n         uint64_t fee            = 500 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t price_per_kbyte = 10;\n      };\n\n      asset_update_operation(){}\n\n      asset           fee;\n      account_id_type issuer;\n      asset_id_type   asset_to_update;\n\n      /// If the asset is to be given a new issuer, specify his ID here.\n      optional<account_id_type>   new_issuer;\n      asset_options               new_options;\n      extension<ext>              extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n   /**\n    * @brief Update options specific to BitAssets\n    * @ingroup operations\n    *\n    * BitAssets have some options which are not relevant to other asset types. This operation is used to update those\n    * options an an existing BitAsset.\n    *\n    * @pre @ref issuer MUST be an existing account and MUST match asset_object::issuer on @ref asset_to_update\n    * @pre @ref asset_to_update MUST be a BitAsset, i.e. @ref asset_object::is_market_issued() returns true\n    * @pre @ref fee MUST be nonnegative, and @ref issuer MUST have a sufficient balance to pay it\n    * @pre @ref new_options SHALL be internally consistent, as verified by @ref validate()\n    * @post @ref asset_to_update will have BitAsset-specific options matching those of new_options\n    */\n   struct asset_update_bitasset_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;\n      account_id_type issuer;\n      asset_id_type   asset_to_update;\n\n      bitasset_options new_options;\n      extensions_type  extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Update the set of feed-producing accounts for a BitAsset\n    * @ingroup operations\n    *\n    * BitAssets have price feeds selected by taking the median values of recommendations from a set of feed producers.\n    * This operation is used to specify which accounts may produce feeds for a given BitAsset.\n    *\n    * @pre @ref issuer MUST be an existing account, and MUST match asset_object::issuer on @ref asset_to_update\n    * @pre @ref issuer MUST NOT be the committee account\n    * @pre @ref asset_to_update MUST be a BitAsset, i.e. @ref asset_object::is_market_issued() returns true\n    * @pre @ref fee MUST be nonnegative, and @ref issuer MUST have a sufficient balance to pay it\n    * @pre Cardinality of @ref new_feed_producers MUST NOT exceed @ref chain_parameters::maximum_asset_feed_publishers\n    * @post @ref asset_to_update will have a set of feed producers matching @ref new_feed_producers\n    * @post All valid feeds supplied by feed producers in @ref new_feed_producers, which were already feed producers\n    * prior to execution of this operation, will be preserved\n    */\n   struct asset_update_feed_producers_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;\n      account_id_type issuer;\n      asset_id_type   asset_to_update;\n\n      flat_set<account_id_type> new_feed_producers;\n      extensions_type           extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Publish price feeds for market-issued assets\n    * @ingroup operations\n    *\n    * Price feed providers use this operation to publish their price feeds for market-issued assets. A price feed is\n    * used to tune the market for a particular market-issued asset. For each value in the feed, the median across all\n    * committee_member feeds for that asset is calculated and the market for the asset is configured with the median of that\n    * value.\n    *\n    * The feed in the operation contains three prices: a call price limit, a short price limit, and a settlement price.\n    * The call limit price is structured as (collateral asset) / (debt asset) and the short limit price is structured\n    * as (asset for sale) / (collateral asset). Note that the asset IDs are opposite to eachother, so if we're\n    * publishing a feed for USD, the call limit price will be CORE/USD and the short limit price will be USD/CORE. The\n    * settlement price may be flipped either direction, as long as it is a ratio between the market-issued asset and\n    * its collateral.\n    */\n   struct asset_publish_feed_operation : public base_operation\n   {\n      struct ext\n      {\n         /// After BSIP77, price feed producers can feed ICR too\n         fc::optional<uint16_t> initial_collateral_ratio;  // BSIP-77\n      };\n\n      struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                  fee; ///< paid for by publisher\n      account_id_type        publisher;\n      asset_id_type          asset_id; ///< asset for which the feed is published\n      price_feed             feed;\n      extension<ext>         extensions;\n\n      account_id_type fee_payer()const { return publisher; }\n      void            validate()const;\n   };\n\n   /**\n    * @ingroup operations\n    */\n   struct asset_issue_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; \n         uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset            fee;\n      account_id_type  issuer; ///< Must be asset_to_issue->asset_id->issuer\n      asset            asset_to_issue;\n      account_id_type  issue_to_account;\n\n\n      /** user provided data encrypted to the memo key of the \"to\" account */\n      optional<memo_data>  memo;\n      extensions_type      extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n   /**\n    * @brief used to take an asset out of circulation, returning to the issuer\n    * @ingroup operations\n    *\n    * @note You cannot use this operation on market-issued assets.\n    */\n   struct asset_reserve_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset             fee;\n      account_id_type   payer;\n      asset             amount_to_reserve;\n      extensions_type   extensions;\n\n      account_id_type fee_payer()const { return payer; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief used to transfer accumulated fees back to the issuer's balance.\n    */\n   struct asset_claim_fees_operation : public base_operation\n   {\n      struct fee_parameters_type {\n         uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      struct additional_options_type\n      {\n         /// Which asset to claim fees from. This is needed, e.g., to claim collateral-\n         /// denominated fees from a collateral-backed smart asset. If unset, assumed to be same\n         /// asset as amount_to_claim is denominated in, such as would be the case when claiming\n         /// market fees. If set, validation requires it to be a different asset_id than\n         /// amount_to_claim (else there would exist two ways to form the same request).\n         fc::optional<asset_id_type> claim_from_asset_id;\n      };\n\n      asset           fee;\n      account_id_type issuer; ///< must match issuer of asset from which we claim fees\n      asset           amount_to_claim;\n\n      extension<additional_options_type> extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Update issuer of an asset\n    * @ingroup operations\n    *\n    * An issuer has general administrative power of an asset and in some cases\n    * also its shares issued to individuals. Thus, changing the issuer today\n    * requires the use of a separate operation that needs to be signed by the\n    * owner authority.\n    *\n    */\n   struct asset_update_issuer_operation : public base_operation\n   {\n      struct fee_parameters_type {\n         uint64_t fee            = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset           fee;\n      account_id_type issuer;\n      asset_id_type   asset_to_update;\n      account_id_type new_issuer;\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n\n      void get_required_owner_authorities( flat_set<account_id_type>& a )const\n      { a.insert( issuer ); }\n\n      void get_required_active_authorities( flat_set<account_id_type>& a )const\n      {  }\n\n   };\n\n   /**\n    * @brief Transfers BTS from the fee pool of a specified asset back to the issuer's balance\n\n    * @param fee Payment for the operation execution\n    * @param issuer Account which will be used for transfering BTS\n    * @param asset_id Id of the asset whose fee pool is going to be drained\n    * @param amount_to_claim Amount of BTS to claim from the fee pool\n    * @param extensions Field for future expansion\n\n    * @pre @ref fee must be paid in the asset other than the one whose pool is being drained\n    * @pre @ref amount_to_claim should be specified in the core asset\n    * @pre @ref amount_to_claim should be nonnegative\n    */\n   struct asset_claim_pool_operation : public base_operation\n   {\n      struct fee_parameters_type {\n         uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset           fee;\n      account_id_type issuer;\n      asset_id_type   asset_id;        /// fee.asset_id must != asset_id\n      asset           amount_to_claim; /// core asset\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::asset_claim_fees_operation, (fee)(issuer)(amount_to_claim)(extensions) )\nFC_REFLECT( graphene::protocol::asset_claim_fees_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_claim_fees_operation::additional_options_type, (claim_from_asset_id) )\n\nFC_REFLECT( graphene::protocol::asset_claim_pool_operation, (fee)(issuer)(asset_id)(amount_to_claim)(extensions) )\nFC_REFLECT( graphene::protocol::asset_claim_pool_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::asset_options,\n            (max_supply)\n            (market_fee_percent)\n            (max_market_fee)\n            (issuer_permissions)\n            (flags)\n            (core_exchange_rate)\n            (whitelist_authorities)\n            (blacklist_authorities)\n            (whitelist_markets)\n            (blacklist_markets)\n            (description)\n            (extensions)\n          )\n\nFC_REFLECT( graphene::protocol::bitasset_options::ext,\n            (initial_collateral_ratio)\n            (maintenance_collateral_ratio)\n            (maximum_short_squeeze_ratio)\n            (margin_call_fee_ratio)\n            (force_settle_fee_percent)\n          )\n\nFC_REFLECT( graphene::protocol::bitasset_options,\n            (feed_lifetime_sec)\n            (minimum_feeds)\n            (force_settlement_delay_sec)\n            (force_settlement_offset_percent)\n            (maximum_force_settlement_volume)\n            (short_backing_asset)\n            (extensions)\n          )\n\nFC_REFLECT( graphene::protocol::additional_asset_options,\n            (reward_percent)(whitelist_market_fee_sharing)(taker_fee_percent) )\n\nFC_REFLECT( graphene::protocol::asset_update_operation::ext, (new_precision)(skip_core_exchange_rate) )\nFC_REFLECT( graphene::protocol::asset_publish_feed_operation::ext, (initial_collateral_ratio) )\n\nFC_REFLECT( graphene::protocol::asset_create_operation::fee_parameters_type,\n            (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )\n\nFC_REFLECT( graphene::protocol::asset_global_settle_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_settle_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_settle_cancel_operation::fee_parameters_type, )\nFC_REFLECT( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::asset_update_issuer_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_update_feed_producers_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_publish_feed_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::asset_issue_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::asset_reserve_operation::fee_parameters_type, (fee) )\n\n\nFC_REFLECT( graphene::protocol::asset_create_operation,\n            (fee)\n            (issuer)\n            (symbol)\n            (precision)\n            (common_options)\n            (bitasset_opts)\n            (is_prediction_market)\n            (extensions)\n          )\nFC_REFLECT( graphene::protocol::asset_update_operation,\n            (fee)\n            (issuer)\n            (asset_to_update)\n            (new_issuer)\n            (new_options)\n            (extensions)\n          )\nFC_REFLECT( graphene::protocol::asset_update_issuer_operation,\n            (fee)\n            (issuer)\n            (asset_to_update)\n            (new_issuer)\n            (extensions)\n          )\nFC_REFLECT( graphene::protocol::asset_update_bitasset_operation,\n            (fee)\n            (issuer)\n            (asset_to_update)\n            (new_options)\n            (extensions)\n          )\nFC_REFLECT( graphene::protocol::asset_update_feed_producers_operation,\n            (fee)(issuer)(asset_to_update)(new_feed_producers)(extensions)\n          )\nFC_REFLECT( graphene::protocol::asset_publish_feed_operation,\n            (fee)(publisher)(asset_id)(feed)(extensions) )\nFC_REFLECT( graphene::protocol::asset_settle_operation, (fee)(account)(amount)(extensions) )\nFC_REFLECT( graphene::protocol::asset_settle_cancel_operation, (fee)(settlement)(account)(amount)(extensions) )\nFC_REFLECT( graphene::protocol::asset_global_settle_operation, (fee)(issuer)(asset_to_settle)(settle_price)(extensions) )\nFC_REFLECT( graphene::protocol::asset_issue_operation,\n            (fee)(issuer)(asset_to_issue)(issue_to_account)(memo)(extensions) )\nFC_REFLECT( graphene::protocol::asset_reserve_operation,\n            (fee)(payer)(amount_to_reserve)(extensions) )\n\nFC_REFLECT( graphene::protocol::asset_fund_fee_pool_operation, (fee)(from_account)(asset_id)(amount)(extensions) );\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_options )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::bitasset_options::ext )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::bitasset_options )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::additional_asset_options )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::ext )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_publish_feed_operation::ext )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_global_settle_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::additional_options_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_feed_producers_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_publish_feed_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_issue_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_reserve_operation::fee_parameters_type )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_global_settle_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_cancel_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_feed_producers_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_publish_feed_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_issue_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_reserve_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/authority.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/types.hpp>\n#include <graphene/protocol/address.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    *  @class authority\n    *  @brief Identifies a weighted set of keys and accounts that must approve operations.\n    */\n   struct authority\n   {\n      authority(){}\n      template<class ...Args>\n      authority(uint32_t threshhold, Args... auths)\n         : weight_threshold(threshhold)\n      {\n         add_authorities(auths...);\n      }\n\n      enum classification\n      {\n         /** the key that is authorized to change owner, active, and voting keys */\n         owner  = 0,\n         /** the key that is able to perform normal operations */\n         active = 1,\n         key    = 2\n      };\n      void add_authority( const public_key_type& k, weight_type w )\n      {\n         key_auths[k] = w;\n      }\n      void add_authority( const address& k, weight_type w )\n      {\n         address_auths[k] = w;\n      }\n      void add_authority( account_id_type k, weight_type w )\n      {\n         account_auths[k] = w;\n      }\n      bool is_impossible()const\n      {\n         uint64_t auth_weights = 0;\n         for( const auto& item : account_auths ) auth_weights += item.second;\n         for( const auto& item : key_auths ) auth_weights += item.second;\n         for( const auto& item : address_auths ) auth_weights += item.second;\n         return auth_weights < weight_threshold;\n      }\n\n      template<typename AuthType>\n      void add_authorities(AuthType k, weight_type w)\n      {\n         add_authority(k, w);\n      }\n      template<typename AuthType, class ...Args>\n      void add_authorities(AuthType k, weight_type w, Args... auths)\n      {\n         add_authority(k, w);\n         add_authorities(auths...);\n      }\n\n      vector<public_key_type> get_keys() const\n      {\n         vector<public_key_type> result;\n         result.reserve( key_auths.size() );\n         for( const auto& k : key_auths )\n            result.push_back(k.first);\n         return result;\n      }\n      vector<address> get_addresses() const\n      {\n         vector<address> result;\n         result.reserve( address_auths.size() );\n         for( const auto& k : address_auths )\n            result.push_back(k.first);\n         return result;\n      }\n\n\n      friend bool operator == ( const authority& a, const authority& b )\n      {\n         return (a.weight_threshold == b.weight_threshold) &&\n                (a.account_auths == b.account_auths) &&\n                (a.key_auths == b.key_auths) &&\n                (a.address_auths == b.address_auths); \n      }\n      friend bool operator!= ( const authority& a, const authority& b ) { return !(a==b); }\n      uint32_t num_auths()const { return account_auths.size() + key_auths.size() + address_auths.size(); }\n      void     clear() { account_auths.clear(); key_auths.clear(); address_auths.clear(); weight_threshold = 0; }\n\n      static authority null_authority()\n      {\n         return authority( 1, GRAPHENE_NULL_ACCOUNT, 1 );\n      }\n\n      uint32_t                              weight_threshold = 0;\n      flat_map<account_id_type,weight_type> account_auths;\n      flat_map<public_key_type,weight_type> key_auths;\n      /** needed for backward compatibility only */\n      flat_map<address,weight_type>         address_auths;\n   };\n\n/**\n * Add all account members of the given authority to the given flat_set.\n */\nvoid add_authority_accounts(\n   flat_set<account_id_type>& result,\n   const authority& a\n   );\n\n} } // namespace graphene::protocol\n\nFC_REFLECT( graphene::protocol::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) )\nFC_REFLECT_ENUM( graphene::protocol::authority::classification, (owner)(active)(key) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::authority )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/balance.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/authority.hpp>\n\nnamespace graphene { namespace protocol { \n\n   /**\n    * @brief Claim a balance in a @ref balance_object\n    *\n    * This operation is used to claim the balance in a given @ref balance_object. If the balance object contains a\n    * vesting balance, @ref total_claimed must not exceed @ref balance_object::available at the time of evaluation. If\n    * the object contains a non-vesting balance, @ref total_claimed must be the full balance of the object.\n    */\n   struct balance_claim_operation : public base_operation\n   {\n      struct fee_parameters_type {};\n\n      asset             fee;\n      account_id_type   deposit_to_account;\n      balance_id_type   balance_to_claim;\n      public_key_type   balance_owner_key;\n      asset             total_claimed;\n\n      account_id_type fee_payer()const { return deposit_to_account; }\n      share_type      calculate_fee(const fee_parameters_type& )const { return 0; }\n      void            validate()const;\n      void            get_required_authorities( vector<authority>& a )const\n      {\n         a.push_back( authority( 1, balance_owner_key, 1 ) );\n      }\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::balance_claim_operation::fee_parameters_type,  )\nFC_REFLECT( graphene::protocol::balance_claim_operation,\n            (fee)(deposit_to_account)(balance_to_claim)(balance_owner_key)(total_claimed) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::balance_claim_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/base.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/ext.hpp>\n#include <graphene/protocol/types.hpp>\n\n#include <fc/thread/future.hpp>\n\nnamespace graphene { namespace protocol {\n   struct asset;\n   struct authority;\n\n   /**\n    *  @defgroup operations Operations\n    *  @ingroup transactions Transactions\n    *  @brief A set of valid comands for mutating the globally shared state.\n    *\n    *  An operation can be thought of like a function that will modify the global\n    *  shared state of the blockchain.  The members of each struct are like function\n    *  arguments and each operation can potentially generate a return value.\n    *\n    *  Operations can be grouped into transactions (@ref transaction) to ensure that they occur\n    *  in a particular order and that all operations apply successfully or\n    *  no operations apply.\n    *\n    *  Each operation is a fully defined state transition and can exist in a transaction on its own.\n    *\n    *  @section operation_design_principles Design Principles\n    *\n    *  Operations have been carefully designed to include all of the information necessary to\n    *  interpret them outside the context of the blockchain.   This means that information about\n    *  current chain state is included in the operation even though it could be inferred from\n    *  a subset of the data.   This makes the expected outcome of each operation well defined and\n    *  easily understood without access to chain state.\n    *\n    *  @subsection balance_calculation Balance Calculation Principle\n    *\n    *    We have stipulated that the current account balance may be entirely calculated from\n    *    just the subset of operations that are relevant to that account.  There should be\n    *    no need to process the entire blockchain inorder to know your account's balance.\n    *\n    *  @subsection fee_calculation Explicit Fee Principle\n    *\n    *    Blockchain fees can change from time to time and it is important that a signed\n    *    transaction explicitly agree to the fees it will be paying.  This aids with account\n    *    balance updates and ensures that the sender agreed to the fee prior to making the\n    *    transaction.\n    *\n    *  @subsection defined_authority Explicit Authority\n    *\n    *    Each operation shall contain enough information to know which accounts must authorize\n    *    the operation.  This principle enables authority verification to occur in a centralized,\n    *    optimized, and parallel manner.\n    *\n    *  @subsection relevancy_principle Explicit Relevant Accounts\n    *\n    *    Each operation contains enough information to enumerate all accounts for which the\n    *    operation should apear in its account history.  This principle enables us to easily\n    *    define and enforce the @balance_calculation. This is superset of the @ref defined_authority\n    *\n    *  @{\n    */\n\n   struct void_result{};\n\n   struct generic_operation_result\n   {\n      flat_set<object_id_type> new_objects;\n      flat_set<object_id_type> updated_objects;\n      flat_set<object_id_type> removed_objects;\n   };\n\n   struct generic_exchange_operation_result\n   {\n      vector<asset> paid;\n      vector<asset> received;\n      vector<asset> fees;\n   };\n\n   typedef fc::static_variant <\n         void_result,\n         object_id_type,\n         asset,\n         generic_operation_result,\n         generic_exchange_operation_result\n      > operation_result;\n\n   struct base_operation\n   {\n      template<typename T>\n      share_type calculate_fee(const T& params)const\n      {\n         return params.fee;\n      }\n      void get_required_authorities( vector<authority>& )const{}\n      void get_required_active_authorities( flat_set<account_id_type>& )const{}\n      void get_required_owner_authorities( flat_set<account_id_type>& )const{}\n      void validate()const{}\n      fc::optional< fc::future<void> > validate_parallel( uint32_t skip )const;\n\n      static uint64_t calculate_data_fee( uint64_t bytes, uint64_t price_per_kbyte );\n   };\n\n   /**\n    *  For future expansion many structus include a single member of type\n    *  extensions_type that can be changed when updating a protocol.  You can\n    *  always add new types to a static_variant without breaking backward\n    *  compatibility.   \n    */\n   typedef static_variant<void_t>      future_extensions;\n\n   /**\n    *  A flat_set is used to make sure that only one extension of\n    *  each type is added and that they are added in order.  \n    *  \n    *  @note static_variant compares only the type tag and not the \n    *  content.\n    */\n   using extensions_type = future_extensions::flat_set_type;\n\n   ///@}\n\n} } // graphene::protocol\n\nFC_REFLECT_TYPENAME( graphene::protocol::operation_result )\nFC_REFLECT_TYPENAME( graphene::protocol::future_extensions )\nFC_REFLECT( graphene::protocol::void_result, )\nFC_REFLECT( graphene::protocol::generic_operation_result, (new_objects)(updated_objects)(removed_objects) )\nFC_REFLECT( graphene::protocol::generic_exchange_operation_result, (paid)(received)(fees) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::generic_operation_result ) // impl in operations.cpp\n// impl in operations.cpp\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::generic_exchange_operation_result )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/block.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/transaction.hpp>\n\nnamespace graphene { namespace protocol {\n\n   class block_header\n   {\n   public:\n      digest_type                   digest()const;\n      block_id_type                 previous;\n      uint32_t                      block_num()const { return num_from_id(previous) + 1; }\n      fc::time_point_sec            timestamp;\n      witness_id_type               witness;\n      checksum_type                 transaction_merkle_root;\n      // Note: when we need to add data to `extensions`, remember to review `database::_generate_block()`.\n      //       More info in https://github.com/bitshares/bitshares-core/issues/1136\n      extensions_type               extensions;\n\n      static uint32_t num_from_id(const block_id_type& id);\n   };\n\n   class signed_block_header : public block_header\n   {\n   public:\n      const block_id_type&       id()const;\n      const fc::ecc::public_key& signee()const;\n      void                       sign( const fc::ecc::private_key& signer );\n      bool                       validate_signee( const fc::ecc::public_key& expected_signee )const;\n\n      signature_type             witness_signature;\n   protected:\n      mutable fc::ecc::public_key _signee;\n      mutable block_id_type       _block_id;\n   };\n\n   class signed_block : public signed_block_header\n   {\n   public:\n      const checksum_type& calculate_merkle_root()const;\n      vector<processed_transaction> transactions;\n   protected:\n      mutable checksum_type   _calculated_merkle_root;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::block_header, (previous)(timestamp)(witness)(transaction_merkle_root)(extensions) )\nFC_REFLECT_DERIVED( graphene::protocol::signed_block_header, (graphene::protocol::block_header), (witness_signature) )\nFC_REFLECT_DERIVED( graphene::protocol::signed_block, (graphene::protocol::signed_block_header), (transactions) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::block_header)\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::signed_block_header)\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::signed_block)\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/buyback.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace protocol {\n\nstruct buyback_account_options\n{\n   /**\n    * The asset to buy.\n    */\n   asset_id_type       asset_to_buy;\n\n   /**\n    * Issuer of the asset.  Must sign the transaction, must match issuer\n    * of specified asset.\n    */\n   account_id_type     asset_to_buy_issuer;\n\n   /**\n    * What assets the account is willing to buy with.\n    * Other assets will just sit there since the account has null authority.\n    */\n   flat_set< asset_id_type > markets;\n};\n\n} }\n\nFC_REFLECT( graphene::protocol::buyback_account_options, (asset_to_buy)(asset_to_buy_issuer)(markets) );\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::buyback_account_options )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/chain_parameters.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <memory>\n#include <graphene/protocol/base.hpp>\n\nnamespace graphene { namespace protocol {\n   struct fee_schedule;\n\n   struct htlc_options\n   {\n      uint32_t max_timeout_secs;\n      uint32_t max_preimage_size;\n   };\n\n   struct custom_authority_options_type\n   {\n      uint32_t max_custom_authority_lifetime_seconds = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS;\n      uint32_t max_custom_authorities_per_account = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT;\n      uint32_t max_custom_authorities_per_account_op = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT_OP;\n      uint32_t max_custom_authority_restrictions = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_RESTRICTIONS;\n   };\n\n   struct chain_parameters\n   {\n      /** using a shared_ptr breaks the circular dependency created between operations and the fee schedule */\n      std::shared_ptr<const fee_schedule> current_fees;                  ///< current schedule of fees\n      const fee_schedule& get_current_fees() const { FC_ASSERT(current_fees); return *current_fees; }\n      fee_schedule& get_mutable_fees() { FC_ASSERT(current_fees); return const_cast<fee_schedule&>(*current_fees); }\n\n      uint8_t                 block_interval                      = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks\n      uint32_t                maintenance_interval                = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL; ///< interval in sections between blockchain maintenance events\n      uint8_t                 maintenance_skip_slots              = GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS; ///< number of block_intervals to skip at maintenance time\n      uint32_t                committee_proposal_review_period    = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC; ///< minimum time in seconds that a proposed transaction requiring committee authority may not be signed, prior to expiration\n      uint32_t                maximum_transaction_size            = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE; ///< maximum allowable size in bytes for a transaction\n      uint32_t                maximum_block_size                  = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE; ///< maximum allowable size in bytes for a block\n      uint32_t                maximum_time_until_expiration       = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION; ///< maximum lifetime in seconds for transactions to be valid, before expiring\n      uint32_t                maximum_proposal_lifetime           = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC; ///< maximum lifetime in seconds for proposed transactions to be kept, before expiring\n      uint8_t                 maximum_asset_whitelist_authorities = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES; ///< maximum number of accounts which an asset may list as authorities for its whitelist OR blacklist\n      uint8_t                 maximum_asset_feed_publishers       = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset\n      uint16_t                maximum_witness_count               = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses\n      uint16_t                maximum_committee_count             = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active committee_members\n      uint16_t                maximum_authority_membership        = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have\n      uint16_t                reserve_percent_of_fee              = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation\n      uint16_t                network_percent_of_fee              = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network\n      uint16_t                lifetime_referrer_percent_of_fee    = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE; ///< percent of transaction fees paid to network\n      uint32_t                cashback_vesting_period_seconds     = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC; ///< time after cashback rewards are accrued before they become liquid\n      share_type              cashback_vesting_threshold          = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD; ///< the maximum cashback that can be received without vesting\n      bool                    count_non_member_votes              = true; ///< set to false to restrict voting privlegages to member accounts\n      bool                    allow_non_member_whitelists         = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise\n      share_type              witness_pay_per_block               = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block)\n      uint32_t                witness_pay_vesting_seconds         = GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS; ///< vesting_seconds parameter for witness VBO's\n      share_type              worker_budget_per_day               = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)\n      uint16_t                max_predicate_opcode                = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number\n      share_type              fee_liquidation_threshold           = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD; ///< value in CORE at which accumulated fees in blockchain-issued market assets should be liquidated\n      uint16_t                accounts_per_fee_scale              = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings\n      uint8_t                 account_fee_scale_bitshifts         = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling\n      uint8_t                 max_authority_depth                 = GRAPHENE_MAX_SIG_CHECK_DEPTH;\n\n      struct ext\n      {\n         optional< htlc_options > updatable_htlc_options;\n         optional< custom_authority_options_type > custom_authority_options;\n         optional< uint16_t > market_fee_network_percent;\n         optional< uint16_t > maker_fee_discount_percent;\n      };\n\n      extension<ext> extensions;\n\n      void validate()const;\n      \n      chain_parameters();\n      chain_parameters(const chain_parameters& other);\n      chain_parameters(chain_parameters&& other);\n      chain_parameters& operator=(const chain_parameters& other);\n      chain_parameters& operator=(chain_parameters&& other);\n\n      /// If @ref market_fee_network_percent is valid, return the value it contains, otherwise return 0\n      uint16_t get_market_fee_network_percent() const;\n\n      /// If @ref maker_fee_discount_percent is valid, return the value it contains, otherwise return 0\n      uint16_t get_maker_fee_discount_percent() const;\n\n      private:\n      static void safe_copy(chain_parameters& to, const chain_parameters& from);\n   };\n\n} }  // graphene::protocol\n\nFC_REFLECT( graphene::protocol::htlc_options,\n      (max_timeout_secs)\n      (max_preimage_size)\n)\n\nFC_REFLECT( graphene::protocol::custom_authority_options_type,\n      (max_custom_authority_lifetime_seconds)\n      (max_custom_authorities_per_account)\n      (max_custom_authorities_per_account_op)\n      (max_custom_authority_restrictions)\n)\n\nFC_REFLECT( graphene::protocol::chain_parameters::ext,\n      (updatable_htlc_options)\n      (custom_authority_options)\n      (market_fee_network_percent)\n      (maker_fee_discount_percent)\n)\n\nFC_REFLECT( graphene::protocol::chain_parameters,\n            (current_fees)\n            (block_interval)\n            (maintenance_interval)\n            (maintenance_skip_slots)\n            (committee_proposal_review_period)\n            (maximum_transaction_size)\n            (maximum_block_size)\n            (maximum_time_until_expiration)\n            (maximum_proposal_lifetime)\n            (maximum_asset_whitelist_authorities)\n            (maximum_asset_feed_publishers)\n            (maximum_witness_count)\n            (maximum_committee_count)\n            (maximum_authority_membership)\n            (reserve_percent_of_fee)\n            (network_percent_of_fee)\n            (lifetime_referrer_percent_of_fee)\n            (cashback_vesting_period_seconds)\n            (cashback_vesting_threshold)\n            (count_non_member_votes)\n            (allow_non_member_whitelists)\n            (witness_pay_per_block)\n            (worker_budget_per_day)\n            (max_predicate_opcode)\n            (fee_liquidation_threshold)\n            (accounts_per_fee_scale)\n            (account_fee_scale_bitshifts)\n            (max_authority_depth)\n            (extensions)\n          )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::chain_parameters )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/committee_member.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/chain_parameters.hpp>\n\nnamespace graphene { namespace protocol { \n\n   /**\n    * @brief Create a committee_member object, as a bid to hold a committee_member seat on the network.\n    * @ingroup operations\n    *\n    * Accounts which wish to become committee_members may use this operation to create a committee_member object which stakeholders may\n    * vote on to approve its position as a committee_member.\n    */\n   struct committee_member_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                                 fee;\n      /// The account which owns the committee_member. This account pays the fee for this operation.\n      account_id_type                       committee_member_account;\n      string                                url;\n\n      account_id_type fee_payer()const { return committee_member_account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Update a committee_member object.\n    * @ingroup operations\n    *\n    * Currently the only field which can be updated is the `url`\n    * field.\n    */\n   struct committee_member_update_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                                 fee;\n      /// The committee member to update.\n      committee_member_id_type              committee_member;\n      /// The account which owns the committee_member. This account pays the fee for this operation.\n      account_id_type                       committee_member_account;\n      optional< string >                    new_url;\n\n      account_id_type fee_payer()const { return committee_member_account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Used by committee_members to update the global parameters of the blockchain.\n    * @ingroup operations\n    *\n    * This operation allows the committee_members to update the global parameters on the blockchain. These control various\n    * tunable aspects of the chain, including block and maintenance intervals, maximum data sizes, the fees charged by\n    * the network, etc.\n    *\n    * This operation may only be used in a proposed transaction, and a proposed transaction which contains this\n    * operation must have a review period specified in the current global parameters before it may be accepted.\n    */\n   struct committee_member_update_global_parameters_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset             fee;\n      chain_parameters  new_parameters;\n\n      account_id_type fee_payer()const { return account_id_type(); }\n      void            validate()const;\n   };\n\n   /// TODO: committee_member_resign_operation : public base_operation\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::committee_member_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::committee_member_update_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::committee_member_update_global_parameters_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::committee_member_create_operation,\n            (fee)(committee_member_account)(url) )\nFC_REFLECT( graphene::protocol::committee_member_update_operation,\n            (fee)(committee_member)(committee_member_account)(new_url) )\nFC_REFLECT( graphene::protocol::committee_member_update_global_parameters_operation, (fee)(new_parameters) );\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_global_parameters_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::committee_member_update_global_parameters_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/confidential.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/authority.hpp>\n\nnamespace graphene { namespace protocol {\n\nusing fc::ecc::blind_factor_type;\n\n/**\n * @defgroup stealth Stealth Transfer\n * @brief Operations related to stealth transfer of value\n *\n * Stealth Transfers enable users to maintain their finanical privacy against even\n * though all transactions are public.  Every account has three balances:\n *\n * 1. Public Balance - everyone can see the balance changes and the parties involved\n * 2. Blinded Balance - everyone can see who is transacting but not the amounts involved\n * 3. Stealth Balance - both the amounts and parties involved are obscured\n *\n * Account owners may set a flag that allows their account to receive(or not) transfers of these kinds\n * Asset issuers can enable or disable the use of each of these types of accounts.\n *\n * Using the \"temp account\" which has no permissions required, users can transfer a\n * stealth balance to the temp account and then use the temp account to register a new\n * account.  In this way users can use stealth funds to create anonymous accounts with which\n * they can perform other actions that are not compatible with blinded balances (such as market orders)\n *\n * @section referral_program Referral Progam\n *\n * Stealth transfers that do not specify any account id cannot pay referral fees so 100% of the\n * transaction fee is paid to the network.\n *\n * @section transaction_fees Fees\n *\n * Stealth transfers can have an arbitrarylly large size and therefore the transaction fee for\n * stealth transfers is based purley on the data size of the transaction.\n */\n///@{\n\n/**\n *  @ingroup stealth\n *  This data is encrypted and stored in the\n *  encrypted memo portion of the blind output.\n */\nstruct blind_memo\n{\n   account_id_type     from;\n   share_type          amount;\n   string              message;\n   /** set to the first 4 bytes of the shared secret\n    * used to encrypt the memo.  Used to verify that\n    * decryption was successful.\n    */\n   uint32_t            check= 0;\n};\n\n/**\n *  @ingroup stealth\n */\nstruct blind_input\n{\n   fc::ecc::commitment_type      commitment;\n   /** provided to maintain the invariant that all authority\n    * required by an operation is explicit in the operation.  Must\n    * match blinded_balance_id->owner\n    */\n   authority                      owner;\n};\n\n/**\n *  When sending a stealth tranfer we assume users are unable to scan\n *  the full blockchain; therefore, payments require confirmation data\n *  to be passed out of band.   We assume this out-of-band channel is\n *  not secure and therefore the contents of the confirmation must be\n *  encrypted. \n */\nstruct stealth_confirmation\n{\n   struct memo_data\n   {\n      optional<public_key_type> from;\n      asset                     amount;\n      fc::sha256                blinding_factor;\n      fc::ecc::commitment_type  commitment;\n      uint32_t                  check = 0;\n   };\n\n   /**\n    *  Packs *this then encodes as base58 encoded string.\n    */\n   operator string()const;\n   /**\n    * Unpacks from a base58 string\n    */\n   stealth_confirmation( const std::string& base58 );\n   stealth_confirmation(){}\n\n   public_key_type           one_time_key;\n   optional<public_key_type> to;\n   vector<char>              encrypted_memo;\n};\n\n/**\n *  @class blind_output\n *  @brief Defines data required to create a new blind commitment\n *  @ingroup stealth\n *\n *  The blinded output that must be proven to be greater than 0\n */\nstruct blind_output\n{\n   fc::ecc::commitment_type                commitment;\n   /** only required if there is more than one blind output */\n   range_proof_type                        range_proof;\n   authority                               owner;\n   optional<stealth_confirmation>          stealth_memo;\n};\n\n\n/**\n *  @class transfer_to_blind_operation\n *  @ingroup stealth\n *  @brief Converts public account balance to a blinded or stealth balance\n */\nstruct transfer_to_blind_operation : public base_operation\n{\n   struct fee_parameters_type { \n      uint64_t fee              = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account\n      uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;\n   };\n\n\n   asset                 fee;\n   asset                 amount;\n   account_id_type       from;\n   blind_factor_type     blinding_factor;\n   vector<blind_output>  outputs;\n\n   account_id_type fee_payer()const { return from; }\n   void            validate()const;\n   share_type      calculate_fee(const fee_parameters_type& )const;\n};\n\n/**\n *  @ingroup stealth\n *  @brief Converts blinded/stealth balance to a public account balance\n */\nstruct transfer_from_blind_operation : public base_operation\n{\n   struct fee_parameters_type { \n      uint64_t fee              = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account\n   };\n\n   asset                 fee;\n   asset                 amount;\n   account_id_type       to;\n   blind_factor_type     blinding_factor;\n   vector<blind_input>   inputs;\n\n   account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }\n   void            validate()const;\n\n   void            get_required_authorities( vector<authority>& a )const\n   {\n      for( const auto& in : inputs )\n         a.push_back( in.owner ); \n   }\n};\n\n/**\n *  @ingroup stealth\n *  @brief Transfers from blind to blind\n *\n *  There are two ways to transfer value while maintaining privacy:\n *  1. account to account with amount kept secret\n *  2. stealth transfers with amount sender/receiver kept secret\n *\n *  When doing account to account transfers, everyone with access to the\n *  memo key can see the amounts, but they will not have access to the funds.\n *\n *  When using stealth transfers the same key is used for control and reading\n *  the memo.\n *\n *  This operation is more expensive than a normal transfer and has\n *  a fee proportional to the size of the operation.\n *\n *  All assets in a blind transfer must be of the same type: fee.asset_id\n *  The fee_payer is the temp account and can be funded from the blinded values.\n *\n *  Using this operation you can transfer from an account and/or blinded balances\n *  to an account and/or blinded balances.\n *\n *  Stealth Transfers:\n *\n *  Assuming Receiver has key pair R,r and has shared public key R with Sender\n *  Assuming Sender has key pair S,s\n *  Generate one time key pair  O,o  as s.child(nonce) where nonce can be inferred from transaction\n *  Calculate secret V = o*R\n *  blinding_factor = sha256(V)\n *  memo is encrypted via aes of V\n *  owner = R.child(sha256(blinding_factor))\n *\n *  Sender gives Receiver output ID to complete the payment.\n *\n *  This process can also be used to send money to a cold wallet without having to\n *  pre-register any accounts.\n *\n *  Outputs are assigned the same IDs as the inputs until no more input IDs are available,\n *  in which case a the return value will be the *first* ID allocated for an output.  Additional\n *  output IDs are allocated sequentially thereafter.   If there are fewer outputs than inputs\n *  then the input IDs are freed and never used again.\n */\nstruct blind_transfer_operation : public base_operation\n{\n   struct fee_parameters_type { \n      uint64_t fee              = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account\n      uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;\n   };\n\n   asset                 fee;\n   vector<blind_input>   inputs;\n   vector<blind_output>  outputs;\n    \n   /** graphene TEMP account */\n   account_id_type fee_payer()const;\n   void            validate()const;\n   share_type      calculate_fee( const fee_parameters_type& k )const;\n\n   void            get_required_authorities( vector<authority>& a )const\n   {\n      for( const auto& in : inputs )\n         a.push_back( in.owner ); \n   }\n};\n\n///@} endgroup stealth\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::stealth_confirmation,\n            (one_time_key)(to)(encrypted_memo) )\n\nFC_REFLECT( graphene::protocol::stealth_confirmation::memo_data,\n            (from)(amount)(blinding_factor)(commitment)(check) );\n\nFC_REFLECT( graphene::protocol::blind_memo,\n            (from)(amount)(message)(check) )\nFC_REFLECT( graphene::protocol::blind_input,\n            (commitment)(owner) )\nFC_REFLECT( graphene::protocol::blind_output,\n            (commitment)(range_proof)(owner)(stealth_memo) )\nFC_REFLECT( graphene::protocol::transfer_to_blind_operation,\n            (fee)(amount)(from)(blinding_factor)(outputs) )\nFC_REFLECT( graphene::protocol::transfer_from_blind_operation,\n            (fee)(amount)(to)(blinding_factor)(inputs) )\nFC_REFLECT( graphene::protocol::blind_transfer_operation,\n            (fee)(inputs)(outputs) )\nFC_REFLECT( graphene::protocol::transfer_to_blind_operation::fee_parameters_type, (fee)(price_per_output) )\nFC_REFLECT( graphene::protocol::transfer_from_blind_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::blind_transfer_operation::fee_parameters_type, (fee)(price_per_output) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_to_blind_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_from_blind_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::blind_transfer_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_to_blind_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_from_blind_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::blind_transfer_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/config.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#define GRAPHENE_SYMBOL \"NBS\"\n#define GRAPHENE_ADDRESS_PREFIX \"NBS\"\n\n#define GRAPHENE_BLOCKCHAIN_PRECISION        uint64_t( 100000 )\n#define GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS 5\n\n#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1\n#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63\n\n#define GRAPHENE_MIN_ASSET_SYMBOL_LENGTH 3\n#define GRAPHENE_MAX_ASSET_SYMBOL_LENGTH 16\n\n#define GRAPHENE_MAX_SHARE_SUPPLY int64_t(1000000000000000ll)\n\n#define GRAPHENE_MAX_WORKER_NAME_LENGTH                       63\n#define GRAPHENE_MAX_URL_LENGTH                               127\n\n#define GRAPHENE_MAX_SIG_CHECK_DEPTH 2\n\n#define GRAPHENE_IRREVERSIBLE_THRESHOLD                      (70 * GRAPHENE_1_PERCENT)\n\n/**\n * every second, the fraction of burned core asset which cycles is\n * GRAPHENE_CORE_ASSET_CYCLE_RATE / (1 << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS)\n */\n#define GRAPHENE_CORE_ASSET_CYCLE_RATE                        17\n#define GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS                   32\n\n/**\n * Don't allow the committee_members to publish a limit that would\n * make the network unable to operate.\n */\n#define GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT 1024\n#define GRAPHENE_MIN_BLOCK_INTERVAL   1 /* seconds */\n#define GRAPHENE_MAX_BLOCK_INTERVAL  30 /* seconds */\n\n#define GRAPHENE_DEFAULT_BLOCK_INTERVAL  5 /* seconds */\n#define GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE 2048\n#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE  (2*1000*1000) /* < 2 MiB (less than MAX_MESSAGE_SIZE in graphene/net/config.hpp) */\n#define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds,  aka: 1 day\n#define GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL  (60*60*24) // seconds, aka: 1 day\n#define GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS 3  // number of slots to skip for maintenance interval\n\n#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY                 (60*60*24) ///< 1 day\n#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET                0 ///< 1%\n#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME            (20* GRAPHENE_1_PERCENT) ///< 20%\n#define GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME                    (60*60*24) ///< 1 day\n#define GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP               10\n#define GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES        10\n#define GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS              10\n\n#define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT                    (11)\n#define GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT           (11)\n#define GRAPHENE_DEFAULT_MAX_WITNESSES                        (1001) // SHOULD BE ODD\n#define GRAPHENE_DEFAULT_MAX_COMMITTEE                        (1001) // SHOULD BE ODD\n#define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC            (60*60*24*7*4) // Four weeks\n#define GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks\n#define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE               (20*GRAPHENE_1_PERCENT)\n#define GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE     (30*GRAPHENE_1_PERCENT)\n#define GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC          (60*60*24*365) ///< 1 year\n#define GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD           (GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(100))\n#define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE                  (20*GRAPHENE_1_PERCENT)\n#define GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE                    1\n#define GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD            GRAPHENE_BLOCKCHAIN_PRECISION * 100;\n#define GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE               1000\n#define GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS          4\n#define GRAPHENE_DEFAULT_MAX_BUYBACK_MARKETS                  4\n\n#define GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK            (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t( 10) )\n#define GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS      (60*60*24)\n#define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY            (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 )\n#define GRAPHENE_DEFAULT_MINIMUM_FEEDS                       7\n\n#define GRAPHENE_MIN_BLOCK_SIZE_LIMIT (GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT*5) // 5 transactions per block\n\n/** percentage fields are fixed point with a denominator of 10,000 */\n#define GRAPHENE_100_PERCENT                                    10000\n#define GRAPHENE_1_PERCENT                                      (GRAPHENE_100_PERCENT/100)\n/** NOTE: making this a power of 2 (say 2^15) would greatly accelerate fee calcs */\n\n#define GRAPHENE_MAX_MARKET_FEE_PERCENT                         GRAPHENE_100_PERCENT\n/**\n *  These ratios are fixed point numbers with a denominator of GRAPHENE_COLLATERAL_RATIO_DENOM, the\n *  minimum maitenance collateral is therefore 1.001x and the default\n *  maintenance ratio is 1.75x\n */\n///@{\n#define GRAPHENE_COLLATERAL_RATIO_DENOM                 1000\n#define GRAPHENE_MIN_COLLATERAL_RATIO                   1001  ///< lower than this could result in divide by 0\n#define GRAPHENE_MAX_COLLATERAL_RATIO                   32000 ///< higher than this is unnecessary and may exceed int16 storage\n#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO   1750 ///< Call when collateral only pays off 175% the debt\n#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO        1500 ///< Stop calling when collateral only pays off 150% of the debt\n///@}\n\n/**\n *  Reserved Account IDs with special meaning\n */\n///@{\n/// Represents the current committee members, two-week review period\n#define GRAPHENE_COMMITTEE_ACCOUNT (graphene::protocol::account_id_type(0))\n/// Represents the current witnesses\n#define GRAPHENE_WITNESS_ACCOUNT (graphene::protocol::account_id_type(1))\n/// Represents the current committee members\n#define GRAPHENE_RELAXED_COMMITTEE_ACCOUNT (graphene::protocol::account_id_type(2))\n/// Represents the canonical account with NO authority (nobody can access funds in null account)\n#define GRAPHENE_NULL_ACCOUNT (graphene::protocol::account_id_type(3))\n/// Represents the canonical account with WILDCARD authority (anybody can access funds in temp account)\n#define GRAPHENE_TEMP_ACCOUNT (graphene::protocol::account_id_type(4))\n/// Represents the canonical account for specifying you will vote directly (as opposed to a proxy)\n#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::protocol::account_id_type(5))\n/// Sentinel value used in the scheduler.\n#define GRAPHENE_NULL_WITNESS (graphene::protocol::witness_id_type(0))\n///@}\n\n#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(743))\n\n/// Maximum duration before a custom authority can expire (1 month)\n#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS (60*60*24*30)\n/// Maximum number of custom authorities a particular account can set\n#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT 10\n/// Maximum number of custom authorities a particular account can set for a particular operation\n#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT_OP 3\n/// Maximum number of restrictions a custom authority can contain\n#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_RESTRICTIONS 10\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/custom.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol { \n\n   /**\n    * @brief provides a generic way to add higher level protocols on top of witness consensus\n    * @ingroup operations\n    *\n    * There is no validation for this operation other than that required auths are valid and a fee\n    * is paid that is appropriate for the data contained.\n    */\n   struct custom_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; \n         uint32_t price_per_kbyte = 10;\n      };\n\n      asset                     fee;\n      account_id_type           payer;\n      flat_set<account_id_type> required_auths;\n      uint16_t                  id = 0;\n      vector<char>              data;\n\n      account_id_type   fee_payer()const { return payer; }\n      void              validate()const;\n      share_type        calculate_fee(const fee_parameters_type& k)const;\n      void              get_required_active_authorities( flat_set<account_id_type>& auths )const {\n         auths.insert( required_auths.begin(), required_auths.end() );\n      }\n   };\n\n} } // namespace graphene::protocol\n\nFC_REFLECT( graphene::protocol::custom_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::custom_operation, (fee)(payer)(required_auths)(id)(data) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::custom_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::custom_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/custom_authority.hpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/authority.hpp>\n#include <graphene/protocol/restriction.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    * @brief Create a new custom authority\n    * @ingroup operations\n    */\n   struct custom_authority_create_operation : public base_operation {\n      struct fee_parameters_type {\n         uint64_t basic_fee = GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t price_per_byte = GRAPHENE_BLOCKCHAIN_PRECISION / 10;\n      };\n\n      /// Operation fee\n      asset fee;\n      /// Account which is setting the custom authority; also pays the fee\n      account_id_type account;\n      /// Whether the custom authority is enabled or not\n      bool enabled;\n      /// Date when custom authority becomes active\n      time_point_sec valid_from;\n      /// Expiration date for custom authority\n      time_point_sec valid_to;\n      /// Tag of the operation this custom authority can authorize\n      unsigned_int operation_type;\n      /// Authentication requirements for the custom authority\n      authority auth;\n      /// Restrictions on operations this custom authority can authenticate\n      vector<restriction> restrictions;\n\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return account; }\n      void validate()const;\n      share_type calculate_fee(const fee_parameters_type& k)const;\n   };\n\n   /**\n    * @brief Update a custom authority\n    * @ingroup operations\n    */\n   struct custom_authority_update_operation : public base_operation {\n      struct fee_parameters_type {\n         uint64_t basic_fee = GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t price_per_byte = GRAPHENE_BLOCKCHAIN_PRECISION / 10;\n      };\n\n      /// Operation fee\n      asset fee;\n      /// Account which owns the custom authority to update; also pays the fee\n      account_id_type account;\n      /// ID of the custom authority to update\n      custom_authority_id_type authority_to_update;\n      /// Change to whether the custom authority is enabled or not\n      optional<bool> new_enabled;\n      /// Change to the custom authority begin date\n      optional<time_point_sec> new_valid_from;\n      /// Change to the custom authority expiration date\n      optional<time_point_sec> new_valid_to;\n      /// Change to the authentication for the custom authority\n      optional<authority> new_auth;\n      /// Set of IDs of restrictions to remove\n      flat_set<uint16_t> restrictions_to_remove;\n      /// Vector of new restrictions\n      vector<restriction> restrictions_to_add;\n\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return account; }\n      void validate()const;\n      share_type calculate_fee(const fee_parameters_type& k)const;\n   };\n\n\n   /**\n    * @brief Delete a custom authority\n    * @ingroup operations\n    */\n   struct custom_authority_delete_operation : public base_operation {\n      struct fee_parameters_type { uint64_t fee =  GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      /// Operation fee\n      asset fee;\n      /// Account which owns the custom authority to update; also pays the fee\n      account_id_type account;\n      /// ID of the custom authority to delete\n      custom_authority_id_type authority_to_delete;\n\n      extensions_type extensions;\n\n      account_id_type fee_payer()const { return account; }\n      void validate()const;\n      share_type calculate_fee(const fee_parameters_type& k)const { return k.fee; }\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT(graphene::protocol::custom_authority_create_operation::fee_parameters_type, (basic_fee)(price_per_byte))\nFC_REFLECT(graphene::protocol::custom_authority_update_operation::fee_parameters_type, (basic_fee)(price_per_byte))\nFC_REFLECT(graphene::protocol::custom_authority_delete_operation::fee_parameters_type, (fee))\n\nFC_REFLECT(graphene::protocol::custom_authority_create_operation,\n           (fee)(account)(enabled)(valid_from)(valid_to)(operation_type)(auth)(restrictions)(extensions))\n\nFC_REFLECT(graphene::protocol::custom_authority_update_operation,\n           (fee)(account)(authority_to_update)(new_enabled)(new_valid_from)\n           (new_valid_to)(new_auth)(restrictions_to_remove)(restrictions_to_add)(extensions))\nFC_REFLECT(graphene::protocol::custom_authority_delete_operation, (fee)(account)(authority_to_delete)(extensions))\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/exceptions.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/exception/exception.hpp>\n\n#define GRAPHENE_ASSERT( expr, exc_type, FORMAT, ... )                \\\n   FC_MULTILINE_MACRO_BEGIN                                           \\\n   if( !(expr) )                                                      \\\n      FC_THROW_EXCEPTION( exc_type, FORMAT, __VA_ARGS__ );            \\\n   FC_MULTILINE_MACRO_END\n\nnamespace graphene { namespace protocol {\n\n   FC_DECLARE_EXCEPTION( protocol_exception, 4000000 )\n\n   FC_DECLARE_DERIVED_EXCEPTION( transaction_exception,      protocol_exception, 4010000 )\n\n   FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth,     transaction_exception, 4010001 )\n   FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth,      transaction_exception, 4010002 )\n   FC_DECLARE_DERIVED_EXCEPTION( tx_missing_other_auth,      transaction_exception, 4010003 )\n   FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_sig,          transaction_exception, 4010004 )\n   FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate_sig,           transaction_exception, 4010005 )\n   FC_DECLARE_DERIVED_EXCEPTION( invalid_committee_approval, transaction_exception, 4010006 )\n   FC_DECLARE_DERIVED_EXCEPTION( insufficient_fee,           transaction_exception, 4010007 )\n\n} } // graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/ext.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/io/varint.hpp>\n#include <fc/io/raw_fwd.hpp>\n#include <fc/reflect/reflect.hpp>\n#include <fc/exception/exception.hpp>\n\nnamespace graphene { namespace protocol {\n\nusing fc::unsigned_int;\n\ntemplate< typename T >\nstruct extension\n{\n   extension() {}\n\n   T value;\n};\n\ntemplate< typename T >\nstruct graphene_extension_pack_count_visitor\n{\n   graphene_extension_pack_count_visitor( const T& v ) : value(v) {}\n\n   template<typename Member, class Class, Member (Class::*member)>\n   void operator()( const char* name )const\n   {\n      count += ((value.*member).valid()) ? 1 : 0;\n   }\n\n   const T& value;\n   mutable uint32_t count = 0;\n};\n\ntemplate< typename Stream, typename T >\nstruct graphene_extension_pack_read_visitor\n{\n   graphene_extension_pack_read_visitor( Stream& s, const T& v, uint32_t _max_depth )\n   : stream(s), value(v), max_depth(_max_depth - 1)\n   {\n      FC_ASSERT( _max_depth > 0 );\n   }\n\n   template<typename Member, class Class, Member (Class::*member)>\n   void operator()( const char* name )const\n   {\n      if( (value.*member).valid() )\n      {\n         fc::raw::pack( stream, unsigned_int( which ), max_depth );\n         fc::raw::pack( stream, *(value.*member), max_depth );\n      }\n      ++which;\n   }\n\n   Stream& stream;\n   const T& value;\n   mutable uint32_t which = 0;\n   const uint32_t max_depth;\n};\n\n\ntemplate< typename Stream, typename T >\nstruct graphene_extension_unpack_visitor\n{\n   graphene_extension_unpack_visitor( Stream& s, T& v, uint32_t _max_depth )\n   : stream(s), value(v), max_depth(_max_depth - 1)\n   {\n      FC_ASSERT( _max_depth > 0 );\n      unsigned_int c;\n      fc::raw::unpack( stream, c, max_depth );\n      count_left = c.value;\n      maybe_read_next_which();\n   }\n\n   void maybe_read_next_which()const\n   {\n      if( count_left > 0 )\n      {\n         unsigned_int w;\n         fc::raw::unpack( stream, w, max_depth );\n         next_which = w.value;\n      }\n   }\n\n   template< typename Member, class Class, Member (Class::*member)>\n   void operator()( const char* name )const\n   {\n      if( (count_left > 0) && (which == next_which) )\n      {\n         typename Member::value_type temp;\n         fc::raw::unpack( stream, temp, max_depth );\n         (value.*member) = temp;\n         --count_left;\n         maybe_read_next_which();\n      }\n      else\n         (value.*member).reset();\n      ++which;\n   }\n\n   mutable uint32_t      which = 0;\n   mutable uint32_t next_which = 0;\n   mutable uint32_t count_left = 0;\n\n   Stream& stream;\n   T& value;\n   const uint32_t max_depth;\n};\n\n} } // graphene::protocol\n\nnamespace fc {\n\ntemplate< typename T >\nstruct graphene_extension_from_variant_visitor\n{\n   graphene_extension_from_variant_visitor( const variant_object& v, T& val, uint32_t max_depth )\n      : vo( v ), value( val ), _max_depth(max_depth - 1)\n   {\n      FC_ASSERT( max_depth > 0, \"Recursion depth exceeded!\" );\n      count_left = vo.size();\n   }\n\n   template<typename Member, class Class, Member (Class::*member)>\n   void operator()( const char* name )const\n   {\n      auto it = vo.find(name);\n      if( it != vo.end() )\n      {\n         from_variant( it->value(), (value.*member), _max_depth );\n         assert( count_left > 0 );    // x.find(k) returns true for n distinct values of k only if x.size() >= n\n         --count_left;\n      }\n   }\n\n   const variant_object& vo;\n   T& value;\n   const uint32_t _max_depth;\n   mutable uint32_t count_left = 0;\n};\n\ntemplate< typename T >\nvoid from_variant( const fc::variant& var, graphene::protocol::extension<T>& value, uint32_t max_depth )\n{\n   value = graphene::protocol::extension<T>();\n   if( var.is_null() )\n      return;\n   if( var.is_array() )\n   {\n      FC_ASSERT( var.size() == 0 );\n      return;\n   }\n\n   graphene_extension_from_variant_visitor<T> vtor( var.get_object(), value.value, max_depth );\n   fc::reflector<T>::visit( vtor );\n   FC_ASSERT( vtor.count_left == 0 );    // unrecognized extension throws here\n}\n\ntemplate< typename T >\nstruct graphene_extension_to_variant_visitor\n{\n   graphene_extension_to_variant_visitor( const T& v, uint32_t max_depth ) : value(v), mvo(max_depth) {}\n\n   template<typename Member, class Class, Member (Class::*member)>\n   void operator()( const char* name )const\n   {\n      if( (value.*member).valid() )\n         mvo( name, value.*member );\n   }\n\n   const T& value;\n   mutable limited_mutable_variant_object mvo;\n};\n\ntemplate< typename T >\nvoid to_variant( const graphene::protocol::extension<T>& value, fc::variant& var, uint32_t max_depth )\n{\n   graphene_extension_to_variant_visitor<T> vtor( value.value, max_depth );\n   fc::reflector<T>::visit( vtor );\n   var = vtor.mvo;\n}\n\nnamespace raw {\n\ntemplate< typename Stream, typename T >\nvoid pack( Stream& stream, const graphene::protocol::extension<T>& value, uint32_t _max_depth=FC_PACK_MAX_DEPTH )\n{\n   FC_ASSERT( _max_depth > 0 );\n   --_max_depth;\n   graphene::protocol::graphene_extension_pack_count_visitor<T> count_vtor( value.value );\n   fc::reflector<T>::visit( count_vtor );\n   fc::raw::pack( stream, unsigned_int( count_vtor.count ), _max_depth );\n   graphene::protocol::graphene_extension_pack_read_visitor<Stream,T> read_vtor( stream, value.value, _max_depth );\n   fc::reflector<T>::visit( read_vtor );\n}\n\n\ntemplate< typename Stream, typename T >\nvoid unpack( Stream& s, graphene::protocol::extension<T>& value, uint32_t _max_depth=FC_PACK_MAX_DEPTH )\n{\n   FC_ASSERT( _max_depth > 0 );\n   --_max_depth;\n   value = graphene::protocol::extension<T>();\n   graphene::protocol::graphene_extension_unpack_visitor<Stream, T> vtor( s, value.value, _max_depth );\n   fc::reflector<T>::visit( vtor );\n   FC_ASSERT( vtor.count_left == 0 ); // unrecognized extension throws here\n}\n\n} // fc::raw\n\ntemplate<typename T> struct get_typename< graphene::protocol::extension<T> >\n{ \n   static const char* name()\n   { \n      static std::string n = std::string(\"graphene::protocol::extension<\") \n         + fc::get_typename<T>::name() + std::string(\">\");\n      return n.c_str();\n   } \n};\n\n\n} // fc\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/fba.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol {\n\nstruct fba_distribute_operation : public base_operation\n{\n   struct fee_parameters_type {};\n\n   asset fee;   // always zero\n   account_id_type account_id;\n   // We use object_id_type because this is an implementaton object, and therefore is not known to the protocol library\n   object_id_type fba_id;\n   share_type amount;\n\n   account_id_type fee_payer()const { return account_id; }\n   void validate()const { FC_ASSERT( false ); }\n   share_type calculate_fee(const fee_parameters_type& k)const { return 0; }\n};\n\n} }\n\nFC_REFLECT( graphene::protocol::fba_distribute_operation::fee_parameters_type,  )\n\nFC_REFLECT( graphene::protocol::fba_distribute_operation, (fee)(account_id)(fba_id)(amount) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::fba_distribute_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/fee_schedule.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n\nnamespace graphene { namespace protocol {\n\n   template<typename T> struct transform_to_fee_parameters;\n   template<typename ...T>\n   struct transform_to_fee_parameters<fc::static_variant<T...>>\n   {\n      using type = fc::static_variant< typename T::fee_parameters_type... >;\n   };\n   using fee_parameters = transform_to_fee_parameters<operation>::type;\n\n   template<typename Operation>\n   class fee_helper {\n     public:\n      const typename Operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( typename Operation::fee_parameters_type() );\n         FC_ASSERT( itr != parameters.end() );\n         return itr->template get<typename Operation::fee_parameters_type>();\n      }\n   };\n\n   template<>\n   class fee_helper<account_create_operation> {\n     public:\n      const account_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( account_create_operation::fee_parameters_type() );\n         FC_ASSERT( itr != parameters.end() );\n         return itr->get<account_create_operation::fee_parameters_type>();\n      }\n      typename account_create_operation::fee_parameters_type& get(fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( account_create_operation::fee_parameters_type() );\n         FC_ASSERT( itr != parameters.end() );\n         return itr->get<account_create_operation::fee_parameters_type>();\n      }\n   };\n\n   template<>\n   class fee_helper<bid_collateral_operation> {\n     public:\n      const bid_collateral_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( bid_collateral_operation::fee_parameters_type() );\n         if ( itr != parameters.end() )\n            return itr->get<bid_collateral_operation::fee_parameters_type>();\n\n         static bid_collateral_operation::fee_parameters_type bid_collateral_dummy;\n         bid_collateral_dummy.fee = fee_helper<call_order_update_operation>().cget(parameters).fee;\n         return bid_collateral_dummy;\n      }\n   };\n\n   template<>\n   class fee_helper<asset_update_issuer_operation> {\n     public:\n      const asset_update_issuer_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( asset_update_issuer_operation::fee_parameters_type() );\n         if ( itr != parameters.end() )\n            return itr->get<asset_update_issuer_operation::fee_parameters_type>();\n\n         static asset_update_issuer_operation::fee_parameters_type dummy;\n         dummy.fee = fee_helper<asset_update_operation>().cget(parameters).fee;\n         return dummy;\n      }\n   };\n\n   template<>\n   class fee_helper<asset_claim_pool_operation> {\n     public:\n      const asset_claim_pool_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( asset_claim_pool_operation::fee_parameters_type() );\n         if ( itr != parameters.end() )\n            return itr->get<asset_claim_pool_operation::fee_parameters_type>();\n\n         static asset_claim_pool_operation::fee_parameters_type asset_claim_pool_dummy;\n         asset_claim_pool_dummy.fee = fee_helper<asset_fund_fee_pool_operation>().cget(parameters).fee;\n         return asset_claim_pool_dummy;\n      }\n   };\n\n   template<>\n   class fee_helper<ticket_create_operation> {\n     public:\n      const ticket_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         static ticket_create_operation::fee_parameters_type param;\n         return param;\n      }\n   };\n\n   template<>\n   class fee_helper<ticket_update_operation> {\n     public:\n      const ticket_update_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         static ticket_update_operation::fee_parameters_type param;\n         return param;\n      }\n   };\n\n   template<>\n   class fee_helper<htlc_create_operation> {\n     public:\n      const htlc_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( htlc_create_operation::fee_parameters_type() );\n         if ( itr != parameters.end() )\n            return itr->get<htlc_create_operation::fee_parameters_type>();\n\n         static htlc_create_operation::fee_parameters_type htlc_create_operation_fee_dummy;\n         return htlc_create_operation_fee_dummy;\n      }\n   };\n\n   template<>\n   class fee_helper<htlc_redeem_operation> {\n     public:\n      const htlc_redeem_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( htlc_redeem_operation::fee_parameters_type() );\n         if ( itr != parameters.end() )\n            return itr->get<htlc_redeem_operation::fee_parameters_type>();\n\n         static htlc_redeem_operation::fee_parameters_type htlc_redeem_operation_fee_dummy;\n         return htlc_redeem_operation_fee_dummy;\n      }\n   };\n   template<>\n   class fee_helper<htlc_extend_operation> {\n     public:\n      const htlc_extend_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const\n      {\n         auto itr = parameters.find( htlc_extend_operation::fee_parameters_type() );\n         if ( itr != parameters.end() )\n            return itr->get<htlc_extend_operation::fee_parameters_type>();\n\n         static htlc_extend_operation::fee_parameters_type htlc_extend_operation_fee_dummy;\n         return htlc_extend_operation_fee_dummy;\n      }\n   };\n   /**\n    *  @brief contains all of the parameters necessary to calculate the fee for any operation\n    */\n   struct fee_schedule\n   {\n      fee_schedule();\n\n      static fee_schedule get_default();\n\n      /**\n       *  Finds the appropriate fee parameter struct for the operation\n       *  and then calculates the appropriate fee in CORE asset.\n       */\n      asset calculate_fee( const operation& op )const;\n      /**\n       *  Finds the appropriate fee parameter struct for the operation\n       *  and then calculates the appropriate fee in an asset specified\n       *  implicitly by core_exchange_rate.\n       */\n      asset calculate_fee( const operation& op, const price& core_exchange_rate )const;\n      /**\n       *  Updates the operation with appropriate fee and returns the fee.\n       */\n      asset set_fee( operation& op, const price& core_exchange_rate = price::unit_price() )const;\n\n      void zero_all_fees();\n\n      /**\n       *  Validates all of the parameters are present and accounted for.\n       */\n      void validate()const {}\n\n      template<typename Operation>\n      const typename Operation::fee_parameters_type& get()const\n      {\n         return fee_helper<Operation>().cget(parameters);\n      }\n      template<typename Operation>\n      typename Operation::fee_parameters_type& get()\n      {\n         return fee_helper<Operation>().get(parameters);\n      }\n      template<typename Operation>\n      bool exists()const\n      {\n         auto itr = parameters.find(typename Operation::fee_parameters_type());\n         return itr != parameters.end();\n      }\n\n      /**\n       *  @note must be sorted by fee_parameters.which() and have no duplicates\n       */\n      fee_parameters::flat_set_type parameters;\n      uint32_t                 scale = GRAPHENE_100_PERCENT; ///< fee * scale / GRAPHENE_100_PERCENT\n      private:\n      static void set_fee_parameters(fee_schedule& sched);\n   };\n\n   typedef fee_schedule fee_schedule_type;\n\n} } // graphene::protocol\n\nFC_REFLECT_TYPENAME( graphene::protocol::fee_parameters )\nFC_REFLECT( graphene::protocol::fee_schedule, (parameters)(scale) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::fee_schedule )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/htlc.hpp",
    "content": "/*\n * Copyright (c) 2018 jmjatlanta and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/time.hpp>\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/memo.hpp>\n#include <algorithm> // std::max\n\nnamespace graphene { namespace protocol {\n      typedef fc::ripemd160    htlc_algo_ripemd160;\n      typedef fc::sha1         htlc_algo_sha1;\n      typedef fc::sha256       htlc_algo_sha256;\n      typedef fc::hash160      htlc_algo_hash160;\n\n      typedef fc::static_variant<\n         htlc_algo_ripemd160,\n         htlc_algo_sha1,\n         htlc_algo_sha256,\n         htlc_algo_hash160\n      > htlc_hash;\n\n      struct htlc_create_operation : public base_operation \n      {\n         struct fee_parameters_type {\n            uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;\n            uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         };\n\n         // paid to network\n         asset fee;\n         // where the held monies are to come from\n         account_id_type from;\n         // where the held monies will go if the preimage is provided\n         account_id_type to;\n         // the amount to hold\n         asset amount;\n         // the (typed) hash of the preimage\n         htlc_hash preimage_hash;\n         // the size of the preimage\n         uint16_t preimage_size;\n         // The time the funds will be returned to the source if not claimed\n         uint32_t claim_period_seconds;\n         \n         // additional extensions\n         struct additional_options_type\n         {\n            fc::optional<memo_data> memo;\n         };\n         extension<additional_options_type> extensions;\n\n         /***\n          * @brief Does simple validation of this object\n          */\n         void validate()const;\n         \n         /**\n          * @brief who will pay the fee\n          */\n         account_id_type fee_payer()const { return from; }\n\n         /****\n          * @brief calculates the fee to be paid for this operation\n          */\n         share_type calculate_fee(const fee_parameters_type& fee_params, uint32_t fee_per_kb)const;\n      };\n\n      struct htlc_redeem_operation : public base_operation\n      {\n         struct fee_parameters_type {\n            uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;\n            uint64_t fee_per_kb = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         };\n         \n         // paid to network\n         asset fee;\n         // the object we are attempting to update\n         htlc_id_type htlc_id;\n         // who is attempting to update the transaction\n         account_id_type redeemer;\n         // the preimage (not used if after epoch timeout)\n         std::vector<char> preimage;\n         // for future expansion\n         extensions_type extensions; \n\n         /***\n          * @brief Perform obvious checks to validate this object\n          */\n    \t   void validate()const;\n         \n         /**\n          * @brief Who is to pay the fee\n          */\n         account_id_type fee_payer()const { return redeemer; }\n\n         /****\n          * @brief calculates the fee to be paid for this operation\n          */\n         share_type calculate_fee(const fee_parameters_type& fee_params)const;\n      };\n\n      /**\n       * virtual op to assist with notifying related parties\n       */\n      struct htlc_redeemed_operation : public base_operation\n      {\n         struct fee_parameters_type {};\n\n         htlc_redeemed_operation() {}\n         htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to,\n               account_id_type redeemer, asset amount, const htlc_hash& preimage_hash, uint16_t preimage_size,\n               const std::vector<char>& preimage ) :\n               htlc_id(htlc_id), from(from), to(to), redeemer(redeemer), amount(amount),\n               htlc_preimage_hash(preimage_hash), htlc_preimage_size(preimage_size), preimage(preimage) {}\n\n         account_id_type fee_payer()const { return to; }\n         void validate()const { FC_ASSERT( !\"virtual operation\" ); }\n\n         share_type      calculate_fee(const fee_parameters_type& k)const { return 0; }\n\n         htlc_id_type htlc_id;\n         account_id_type from, to, redeemer;\n         asset amount;\n         htlc_hash htlc_preimage_hash;\n         uint16_t htlc_preimage_size;\n\n         asset fee;\n         std::vector<char> preimage;\n      };\n\n      struct htlc_extend_operation : public base_operation\n      {\n         struct fee_parameters_type {\n            uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;\n            uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         };\n         \n         // paid to network\n         asset fee;\n         // the object we are attempting to update\n         htlc_id_type htlc_id;\n         // who is attempting to update the transaction\n         account_id_type update_issuer;\n         // how much to add\n         uint32_t seconds_to_add;\n         // for future expansion\n         extensions_type extensions; \n\n         /***\n          * @brief Perform obvious checks to validate this object\n          */\n         void validate()const;\n         \n         /**\n          * @brief Who is to pay the fee\n          */\n         account_id_type fee_payer()const { return update_issuer; }\n\n         /****\n          * @brief calculates the fee to be paid for this operation\n          */\n         share_type calculate_fee(const fee_parameters_type& fee_params)const;\n      };\n\n      struct htlc_refund_operation : public base_operation\n      {\n         struct fee_parameters_type {};\n\n         htlc_refund_operation(){}\n         htlc_refund_operation( const htlc_id_type& htlc_id,\n               const account_id_type& htlc_from, const account_id_type& htlc_to, const asset& amount,\n               const htlc_hash& preimage_hash, uint16_t preimage_size ) :\n               htlc_id(htlc_id), to(htlc_from), original_htlc_recipient(htlc_to), htlc_amount(amount),\n               htlc_preimage_hash(preimage_hash), htlc_preimage_size(preimage_size) {}\n\n         account_id_type fee_payer()const { return to; }\n         void            validate()const { FC_ASSERT( !\"virtual operation\" ); }\n\n         /// This is a virtual operation; there is no fee\n         share_type      calculate_fee(const fee_parameters_type& k)const { return 0; }\n\n         asset fee;\n\n         htlc_id_type htlc_id; // of the associated htlc object; it is deleted during emittance of this operation\n         account_id_type to, original_htlc_recipient;\n         account_id_type htlc_from() const { return to; };\n         account_id_type htlc_to()   const { return original_htlc_recipient; };\n         asset htlc_amount;\n         htlc_hash htlc_preimage_hash;\n         uint16_t htlc_preimage_size;\n      };\n   } \n}\n\nFC_REFLECT_TYPENAME( graphene::protocol::htlc_hash )\n\nFC_REFLECT( graphene::protocol::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) )\nFC_REFLECT( graphene::protocol::htlc_create_operation::additional_options_type, (memo))\nFC_REFLECT( graphene::protocol::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) )\nFC_REFLECT( graphene::protocol::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL\nFC_REFLECT( graphene::protocol::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day))\nFC_REFLECT( graphene::protocol::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL\n\nFC_REFLECT( graphene::protocol::htlc_create_operation,\n      (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions))\nFC_REFLECT( graphene::protocol::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions))\nFC_REFLECT( graphene::protocol::htlc_redeemed_operation,\n      (fee)(htlc_id)(from)(to)(redeemer)(amount)(htlc_preimage_hash)(htlc_preimage_size)(preimage))\nFC_REFLECT( graphene::protocol::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions))\nFC_REFLECT( graphene::protocol::htlc_refund_operation,\n      (fee)(htlc_id)(to)(original_htlc_recipient)(htlc_amount)(htlc_preimage_hash)(htlc_preimage_size))\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::additional_options_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeem_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_extend_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeem_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeemed_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_extend_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_refund_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/liquidity_pool.hpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    * @brief Create a new liquidity pool\n    * @ingroup operations\n    */\n   struct liquidity_pool_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 50 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;                         ///< Operation fee\n      account_id_type account;                     ///< The account who creates the liquidity pool\n      asset_id_type   asset_a;                     ///< Type of the first asset in the pool\n      asset_id_type   asset_b;                     ///< Type of the second asset in the pool\n      asset_id_type   share_asset;                 ///< Type of the share asset aka the LP token\n      uint16_t        taker_fee_percent = 0;       ///< Taker fee percent\n      uint16_t        withdrawal_fee_percent = 0;  ///< Withdrawal fee percent\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Delete a liquidity pool\n    * @ingroup operations\n    */\n   struct liquidity_pool_delete_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 0; };\n\n      asset                    fee;                ///< Operation fee\n      account_id_type          account;            ///< The account who owns the liquidity pool\n      liquidity_pool_id_type   pool;               ///< ID of the liquidity pool\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Deposit to a liquidity pool\n    * @ingroup operations\n    */\n   struct liquidity_pool_deposit_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION / 10; };\n\n      asset                    fee;                ///< Operation fee\n      account_id_type          account;            ///< The account who deposits to the liquidity pool\n      liquidity_pool_id_type   pool;               ///< ID of the liquidity pool\n      asset                    amount_a;           ///< The amount of the first asset to deposit\n      asset                    amount_b;           ///< The amount of the second asset to deposit\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Withdraw from a liquidity pool\n    * @ingroup operations\n    */\n   struct liquidity_pool_withdraw_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 5 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                    fee;                ///< Operation fee\n      account_id_type          account;            ///< The account who withdraws from the liquidity pool\n      liquidity_pool_id_type   pool;               ///< ID of the liquidity pool\n      asset                    share_amount;       ///< The amount of the share asset to use\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Exchange with a liquidity pool\n    * @ingroup operations\n    */\n   struct liquidity_pool_exchange_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                    fee;                ///< Operation fee\n      account_id_type          account;            ///< The account who exchanges with the liquidity pool\n      liquidity_pool_id_type   pool;               ///< ID of the liquidity pool\n      asset                    amount_to_sell;     ///< The amount of one asset type to sell\n      asset                    min_to_receive;     ///< The minimum amount of the other asset type to receive\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::liquidity_pool_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::liquidity_pool_delete_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::liquidity_pool_deposit_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::liquidity_pool_withdraw_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::liquidity_pool_exchange_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::liquidity_pool_create_operation,\n            (fee)(account)(asset_a)(asset_b)(share_asset)\n            (taker_fee_percent)(withdrawal_fee_percent)(extensions) )\nFC_REFLECT( graphene::protocol::liquidity_pool_delete_operation,\n            (fee)(account)(pool)(extensions) )\nFC_REFLECT( graphene::protocol::liquidity_pool_deposit_operation,\n            (fee)(account)(pool)(amount_a)(amount_b)(extensions) )\nFC_REFLECT( graphene::protocol::liquidity_pool_withdraw_operation,\n            (fee)(account)(pool)(share_amount)(extensions) )\nFC_REFLECT( graphene::protocol::liquidity_pool_exchange_operation,\n            (fee)(account)(pool)(amount_to_sell)(min_to_receive)(extensions) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation::fee_parameters_type )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/market.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol { \n\n   /**\n    *  @class limit_order_create_operation\n    *  @brief instructs the blockchain to attempt to sell one asset for another\n    *  @ingroup operations\n    *\n    *  The blockchain will atempt to sell amount_to_sell.asset_id for as\n    *  much min_to_receive.asset_id as possible.  The fee will be paid by\n    *  the seller's account.  Market fees will apply as specified by the\n    *  issuer of both the selling asset and the receiving asset as\n    *  a percentage of the amount exchanged.\n    *\n    *  If either the selling asset or the receiving asset is white list\n    *  restricted, the order will only be created if the seller is on\n    *  the white list of the restricted asset type.\n    *\n    *  Market orders are matched in the order they are included\n    *  in the block chain.\n    */\n   struct limit_order_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 5 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;\n      account_id_type seller;\n      asset           amount_to_sell;\n      asset           min_to_receive;\n\n      /// The order will be removed from the books if not filled by expiration\n      /// Upon expiration, all unsold asset will be returned to seller\n      time_point_sec expiration = time_point_sec::maximum();\n\n      /// If this flag is set the entire order must be filled or the operation is rejected\n      bool fill_or_kill = false;\n      extensions_type   extensions;\n\n      pair<asset_id_type,asset_id_type> get_market()const\n      {\n         return amount_to_sell.asset_id < min_to_receive.asset_id ?\n                std::make_pair(amount_to_sell.asset_id, min_to_receive.asset_id) :\n                std::make_pair(min_to_receive.asset_id, amount_to_sell.asset_id);\n      }\n      account_id_type fee_payer()const { return seller; }\n      void            validate()const;\n      price           get_price()const { return amount_to_sell / min_to_receive; }\n   };\n\n   /**\n    *  @ingroup operations\n    *  Used to cancel an existing limit order. Both fee_pay_account and the\n    *  account to receive the proceeds must be the same as order->seller.\n    *\n    *  @return the amount actually refunded\n    */\n   struct limit_order_cancel_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 0; };\n\n      asset               fee;\n      limit_order_id_type order;\n      /** must be order->seller */\n      account_id_type     fee_paying_account;\n      extensions_type   extensions;\n\n      account_id_type fee_payer()const { return fee_paying_account; }\n      void            validate()const;\n   };\n\n   /**\n    *  @ingroup operations\n    *\n    *  This operation can be used to add collateral, cover, and adjust the margin call price for a particular user.\n    *\n    *  For prediction markets the collateral and debt must always be equal.\n    *\n    *  This operation will fail if it would trigger a margin call that couldn't be filled.  If the margin call hits\n    *  the call price limit then it will fail if the call price is above the settlement price.\n    *\n    *  @note this operation can be used to force a market order using the collateral without requiring outside funds.\n    */\n   struct call_order_update_operation : public base_operation\n   {\n      /**\n       * Options to be used in @ref call_order_update_operation.\n       *\n       * @note this struct can be expanded by adding more options in the end.\n       */\n      struct options_type\n      {\n         optional<uint16_t> target_collateral_ratio; ///< maximum CR to maintain when selling collateral on margin call\n      };\n\n      /** this is slightly more expensive than limit orders, this pricing impacts prediction markets */\n      struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset               fee;\n      account_id_type     funding_account; ///< pays fee, collateral, and cover\n      asset               delta_collateral; ///< the amount of collateral to add to the margin position\n      asset               delta_debt; ///< the amount of the debt to be paid off, may be negative to issue new debt\n\n      typedef extension<options_type> extensions_type; // note: this will be jsonified to {...} but no longer [...]\n      extensions_type     extensions;\n\n      account_id_type fee_payer()const { return funding_account; }\n      void            validate()const;\n   };\n\n   /**\n    * @ingroup operations\n    *\n    * @note This is a virtual operation that is created while matching orders and\n    * emitted for the purpose of accurately tracking account history, accelerating\n    * a reindex.\n    */\n   struct fill_order_operation : public base_operation\n   {\n      struct fee_parameters_type {};\n\n      fill_order_operation(){}\n      fill_order_operation( object_id_type o, account_id_type a, asset p, asset r, asset f, price fp, bool m )\n         :order_id(o),account_id(a),pays(p),receives(r),fee(f),fill_price(fp),is_maker(m) {}\n\n      object_id_type      order_id;\n      account_id_type     account_id;\n      asset               pays;\n      asset               receives;\n      asset               fee; // paid by receiving account\n      price               fill_price;\n      bool                is_maker;\n\n      pair<asset_id_type,asset_id_type> get_market()const\n      {\n         return pays.asset_id < receives.asset_id ?\n                std::make_pair( pays.asset_id, receives.asset_id ) :\n                std::make_pair( receives.asset_id, pays.asset_id );\n      }\n      account_id_type fee_payer()const { return account_id; }\n      void            validate()const { FC_ASSERT( !\"virtual operation\" ); }\n\n      /// This is a virtual operation; there is no fee\n      share_type      calculate_fee(const fee_parameters_type& k)const { return 0; }\n   };\n\n   /**\n    *  @ingroup operations\n    *\n    *  This operation can be used after a black swan to bid collateral for\n    *  taking over part of the debt and the settlement_fund (see BSIP-0018).\n    */\n   struct bid_collateral_operation : public base_operation\n   {\n      /** should be equivalent to call_order_update fee */\n      struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset               fee;\n      account_id_type     bidder; ///< pays fee and additional collateral\n      asset               additional_collateral; ///< the amount of collateral to bid for the debt\n      asset               debt_covered; ///< the amount of debt to take over\n      extensions_type     extensions;\n\n      account_id_type fee_payer()const { return bidder; }\n      void            validate()const;\n   };\n\n   /**\n    * @ingroup operations\n    *\n    * @note This is a virtual operation that is created while reviving a\n    * bitasset from collateral bids.\n    */\n   struct execute_bid_operation : public base_operation\n   {\n      struct fee_parameters_type {};\n\n      execute_bid_operation(){}\n      execute_bid_operation( account_id_type a, asset d, asset c )\n         : bidder(a), debt(d), collateral(c) {}\n\n      account_id_type     bidder;\n      asset               debt;\n      asset               collateral;\n      asset               fee;\n\n      account_id_type fee_payer()const { return bidder; }\n      void            validate()const { FC_ASSERT( !\"virtual operation\" ); }\n\n      /// This is a virtual operation; there is no fee\n      share_type      calculate_fee(const fee_parameters_type& k)const { return 0; }\n   };\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::limit_order_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::limit_order_cancel_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::call_order_update_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::bid_collateral_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::fill_order_operation::fee_parameters_type,  ) // VIRTUAL\nFC_REFLECT( graphene::protocol::execute_bid_operation::fee_parameters_type,  ) // VIRTUAL\n\nFC_REFLECT( graphene::protocol::call_order_update_operation::options_type, (target_collateral_ratio) )\n\nFC_REFLECT( graphene::protocol::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions))\nFC_REFLECT( graphene::protocol::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) )\nFC_REFLECT( graphene::protocol::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(extensions) )\nFC_REFLECT( graphene::protocol::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives)(fill_price)(is_maker) )\nFC_REFLECT( graphene::protocol::bid_collateral_operation, (fee)(bidder)(additional_collateral)(debt_covered)(extensions) )\nFC_REFLECT( graphene::protocol::execute_bid_operation, (fee)(bidder)(debt)(collateral) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::call_order_update_operation::options_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_cancel_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::call_order_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::bid_collateral_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_cancel_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::call_order_update_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::bid_collateral_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::fill_order_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::execute_bid_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/memo.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    *  @brief defines the keys used to derive the shared secret\n    *\n    *  Because account authorities and keys can change at any time, each memo must\n    *  capture the specific keys used to derive the shared secret.  In order to read\n    *  the cipher message you will need one of the two private keys.\n    *\n    *  If @ref from == @ref to and @ref from == 0 then no encryption is used, the memo is public.\n    *  If @ref from == @ref to and @ref from != 0 then invalid memo data\n    *\n    */\n   struct memo_data\n   {\n      public_key_type from;\n      public_key_type to;\n      /**\n       * 64 bit nonce format:\n       * [  8 bits | 56 bits   ]\n       * [ entropy | timestamp ]\n       * Timestamp is number of microseconds since the epoch\n       * Entropy is a byte taken from the hash of a new private key\n       *\n       * This format is not mandated or verified; it is chosen to ensure uniqueness of key-IV pairs only. This should\n       * be unique with high probability as long as the generating host has a high-resolution clock OR a strong source\n       * of entropy for generating private keys.\n       */\n      uint64_t nonce = 0;\n      /**\n       * This field contains the AES encrypted packed @ref memo_message\n       */\n      vector<char> message;\n\n      /// @note custom_nonce is for debugging only; do not set to a nonzero value in production\n      void        set_message(const fc::ecc::private_key& priv,\n                              const fc::ecc::public_key& pub, const string& msg, uint64_t custom_nonce = 0);\n\n      std::string get_message(const fc::ecc::private_key& priv,\n                              const fc::ecc::public_key& pub)const;\n   };\n\n   /**\n    * @brief defines a message and checksum to enable validation of successful decryption\n    *\n    * When encrypting/decrypting a checksum is required to determine whether or not\n    * decryption was successful.\n    */\n   struct memo_message\n   {\n      memo_message(){}\n      memo_message( uint32_t checksum, const std::string& text )\n      :checksum(checksum),text(text){}\n\n      uint32_t    checksum = 0;\n      std::string text;\n\n      string serialize() const;\n      static memo_message deserialize(const string& serial);\n   };\n\n} } // namespace graphene::protocol\n\nFC_REFLECT( graphene::protocol::memo_message, (checksum)(text) )\nFC_REFLECT( graphene::protocol::memo_data, (from)(to)(nonce)(message) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::memo_message )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::memo_data )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/object_id.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <fc/exception/exception.hpp>\n#include <fc/io/varint.hpp>\n#include <memory>\n#define GRAPHENE_DB_MAX_INSTANCE_ID  (uint64_t(-1)>>16)\n\nnamespace graphene { namespace db {\n   using  std::shared_ptr;\n   using  std::unique_ptr;\n   using  std::vector;\n   using  fc::flat_map;\n   using  fc::variant;\n   using  fc::unsigned_int;\n\n   struct object_id_type\n   {\n      object_id_type( uint8_t s, uint8_t t, uint64_t i )\n      {\n         FC_ASSERT( i >> 48 == 0, \"instance overflow\", (\"instance\",i) );\n         number = (uint64_t(s)<<56) | (uint64_t(t)<<48) | i;\n      }\n      object_id_type(){ number = 0; }\n\n      uint8_t  space()const       { return number >> 56;              }\n      uint8_t  type()const        { return number >> 48 & 0x00ff;     }\n      uint16_t space_type()const { return number >> 48;              }\n      uint64_t instance()const { return number & GRAPHENE_DB_MAX_INSTANCE_ID; }\n      bool     is_null()const { return number == 0; }\n      explicit operator uint64_t()const { return number; }\n\n      friend bool  operator == ( const object_id_type& a, const object_id_type& b ) { return a.number == b.number; }\n      friend bool  operator != ( const object_id_type& a, const object_id_type& b ) { return a.number != b.number; }\n      friend bool  operator < ( const object_id_type& a, const object_id_type& b ) { return a.number < b.number; }\n      friend bool  operator > ( const object_id_type& a, const object_id_type& b ) { return a.number > b.number; }\n\n      object_id_type& operator++(int) { ++number; return *this; }\n      object_id_type& operator++()    { ++number; return *this; }\n\n      friend object_id_type operator+(const object_id_type& a, int delta ) {\n         return object_id_type( a.space(), a.type(), a.instance() + delta );\n      }\n      friend object_id_type operator+(const object_id_type& a, int64_t delta ) {\n         return object_id_type( a.space(), a.type(), a.instance() + delta );\n      }\n      friend size_t hash_value( object_id_type v ) { return std::hash<uint64_t>()(v.number); }\n\n      template< typename T >\n      bool is() const\n      {\n         return (number >> 48) == ((T::space_id << 8) | (T::type_id));\n      }\n\n      template< typename T >\n      T as() const\n      {\n         FC_ASSERT( is<T>() );\n         return T( *this );\n      }\n\n      explicit operator std::string() const\n      {\n          return fc::to_string(space()) + \".\" + fc::to_string(type()) + \".\" + fc::to_string(instance());\n      }\n\n      uint64_t                   number;\n   };\n\n   class object;\n   class object_database;\n\n   /// This template is used to downcast a generic object type to a specific xyz_object type.\n   template<typename ObjectID>\n   struct object_downcast { using type = object; };\n   // This macro specializes the above template for a specific xyz_object type\n#define MAP_OBJECT_ID_TO_TYPE(OBJECT) \\\n   namespace graphene { namespace db { \\\n   template<> \\\n   struct object_downcast<graphene::db::object_id<OBJECT::space_id, \\\n                                                  OBJECT::type_id>> { using type = OBJECT; }; \\\n   } }\n   template<typename ObjectID>\n   using object_downcast_t = typename object_downcast<ObjectID>::type;\n\n   template<uint8_t SpaceID, uint8_t TypeID>\n   struct object_id\n   {\n      static const uint8_t space_id = SpaceID;\n      static const uint8_t type_id = TypeID;\n\n      object_id() = default;\n      object_id( unsigned_int i ):instance(i){}\n      explicit object_id( uint64_t i ):instance(i)\n      {\n         FC_ASSERT( (i >> 48) == 0 );\n      }\n      object_id( object_id_type id ):instance(id.instance())\n      {\n      }\n\n      friend object_id operator+(const object_id a, int64_t delta ) { return object_id( uint64_t(a.instance.value+delta) ); }\n      friend object_id operator+(const object_id a, int delta ) { return object_id( uint64_t(a.instance.value+delta) ); }\n\n      operator object_id_type()const { return object_id_type( SpaceID, TypeID, instance.value ); }\n      explicit operator uint64_t()const { return object_id_type( *this ).number; }\n\n      template<typename DB>\n      auto operator()(const DB& db)const -> const decltype(db.get(*this))& { return db.get(*this); }\n\n      friend bool  operator == ( const object_id& a, const object_id& b ) { return a.instance == b.instance; }\n      friend bool  operator != ( const object_id& a, const object_id& b ) { return a.instance != b.instance; }\n      friend bool  operator == ( const object_id_type& a, const object_id& b ) { return a == object_id_type(b); }\n      friend bool  operator != ( const object_id_type& a, const object_id& b ) { return a != object_id_type(b); }\n      friend bool  operator == ( const object_id& a, const object_id_type& b ) { return object_id_type(a) == b; }\n      friend bool  operator != ( const object_id& a, const object_id_type& b ) { return object_id_type(a) != b; }\n      friend bool  operator == ( const object_id& a, const fc::unsigned_int& b ) { return a.instance == b; }\n      friend bool  operator != ( const object_id& a, const fc::unsigned_int& b ) { return a.instance != b; }\n      friend bool  operator == ( const fc::unsigned_int& a, const object_id& b ) { return a == b.instance; }\n      friend bool  operator != ( const fc::unsigned_int& a, const object_id& b ) { return a != b.instance; }\n\n      friend bool  operator < ( const object_id& a, const object_id& b ) { return a.instance.value < b.instance.value; }\n      friend bool  operator > ( const object_id& a, const object_id& b ) { return a.instance.value > b.instance.value; }\n\n      friend size_t hash_value( object_id v ) { return std::hash<uint64_t>()(v.instance.value); }\n\n      unsigned_int instance;\n   };\n\n} } // graphene::db\n\nFC_REFLECT( graphene::db::object_id_type, (number) )\n\n// REFLECT object_id manually because it has 2 template params\nnamespace fc {\ntemplate<uint8_t SpaceID, uint8_t TypeID>\nstruct get_typename<graphene::db::object_id<SpaceID,TypeID>>\n{\n   static const char* name() {\n      return typeid(get_typename).name();\n      static std::string _str = string(\"graphene::db::object_id<\")+fc::to_string(SpaceID) + \":\" + fc::to_string(TypeID)+\">\";\n      return _str.c_str();\n   }\n};\n\ntemplate<uint8_t SpaceID, uint8_t TypeID>\nstruct reflector<graphene::db::object_id<SpaceID,TypeID> >\n{\n    typedef graphene::db::object_id<SpaceID,TypeID> type;\n    typedef std::true_type is_defined;\n    using native_members = typelist::list<fc::field_reflection<0, type, unsigned_int, &type::instance>>;\n    using inherited_members = typelist::list<>;\n    using members = native_members;\n    using base_classes = typelist::list<>;\n    enum  member_count_enum {\n      local_member_count = 1,\n      total_member_count = 1\n    };\n    template<typename Visitor>\n    static inline void visit( const Visitor& visitor )\n    {\n       typedef decltype(((type*)nullptr)->instance) member_type;\n       visitor.TEMPLATE operator()<member_type,type,&type::instance>( \"instance\" );\n    }\n};\nnamespace member_names {\ntemplate<uint8_t S, uint8_t T>\nstruct member_name<graphene::db::object_id<S,T>, 0> { static constexpr const char* value = \"instance\"; };\n}\n\n\n inline void to_variant( const graphene::db::object_id_type& var,  fc::variant& vo, uint32_t max_depth = 1 )\n {\n    vo = std::string( var );\n }\n\n inline void from_variant( const fc::variant& var,  graphene::db::object_id_type& vo, uint32_t max_depth = 1 )\n { try {\n    vo.number = 0;\n    const auto& s = var.get_string();\n    auto first_dot = s.find('.');\n    auto second_dot = s.find('.',first_dot+1);\n    FC_ASSERT( first_dot != second_dot );\n    FC_ASSERT( first_dot != 0 && first_dot != std::string::npos );\n    vo.number = fc::to_uint64(s.substr( second_dot+1 ));\n    FC_ASSERT( vo.number <= GRAPHENE_DB_MAX_INSTANCE_ID );\n    auto space_id = fc::to_uint64( s.substr( 0, first_dot ) );\n    FC_ASSERT( space_id <= 0xff );\n    auto type_id =  fc::to_uint64( s.substr( first_dot+1, second_dot-first_dot-1 ) );\n    FC_ASSERT( type_id <= 0xff );\n    vo.number |= (space_id << 56) | (type_id << 48);\n } FC_CAPTURE_AND_RETHROW( (var) ) }\n template<uint8_t SpaceID, uint8_t TypeID>\n void to_variant( const graphene::db::object_id<SpaceID,TypeID>& var,  fc::variant& vo, uint32_t max_depth = 1 )\n {\n    vo = fc::to_string(SpaceID) + \".\" + fc::to_string(TypeID) + \".\" + fc::to_string(var.instance.value);\n }\n template<uint8_t SpaceID, uint8_t TypeID>\n void from_variant( const fc::variant& var,  graphene::db::object_id<SpaceID,TypeID>& vo, uint32_t max_depth = 1 )\n { try {\n    const auto& s = var.get_string();\n    auto first_dot = s.find('.');\n    auto second_dot = s.find('.',first_dot+1);\n    FC_ASSERT( first_dot != second_dot );\n    FC_ASSERT( first_dot != 0 && first_dot != std::string::npos );\n    FC_ASSERT( fc::to_uint64( s.substr( 0, first_dot ) ) == SpaceID &&\n               fc::to_uint64( s.substr( first_dot+1, second_dot-first_dot-1 ) ) == TypeID,\n               \"Space.Type.0 (${SpaceID}.${TypeID}.0) doesn't match expected value ${var}\", (\"TypeID\",TypeID)(\"SpaceID\",SpaceID)(\"var\",var) );\n    vo.instance = fc::to_uint64(s.substr( second_dot+1 ));\n } FC_CAPTURE_AND_RETHROW( (var) ) }\n\n} // namespace fc\n\nnamespace std {\n     template <> struct hash<graphene::db::object_id_type>\n     {\n          size_t operator()(const graphene::db::object_id_type& x) const\n          {\n              return std::hash<uint64_t>()(x.number);\n          }\n     };\n}\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/operations.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/account.hpp>\n#include <graphene/protocol/assert.hpp>\n#include <graphene/protocol/asset_ops.hpp>\n#include <graphene/protocol/balance.hpp>\n#include <graphene/protocol/custom.hpp>\n#include <graphene/protocol/committee_member.hpp>\n#include <graphene/protocol/confidential.hpp>\n#include <graphene/protocol/custom_authority.hpp>\n#include <graphene/protocol/fba.hpp>\n#include <graphene/protocol/liquidity_pool.hpp>\n#include <graphene/protocol/market.hpp>\n#include <graphene/protocol/proposal.hpp>\n#include <graphene/protocol/ticket.hpp>\n#include <graphene/protocol/transfer.hpp>\n#include <graphene/protocol/vesting.hpp>\n#include <graphene/protocol/withdraw_permission.hpp>\n#include <graphene/protocol/witness.hpp>\n#include <graphene/protocol/worker.hpp>\n#include <graphene/protocol/htlc.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    * @ingroup operations\n    *\n    * Defines the set of valid operations as a discriminated union type.\n    */\n   typedef fc::static_variant<\n            transfer_operation,\n            limit_order_create_operation,\n            limit_order_cancel_operation,\n            call_order_update_operation,\n            fill_order_operation,           // VIRTUAL\n            account_create_operation,\n            account_update_operation,\n            account_whitelist_operation,\n            account_upgrade_operation,\n            account_transfer_operation,\n            asset_create_operation,\n            asset_update_operation,\n            asset_update_bitasset_operation,\n            asset_update_feed_producers_operation,\n            asset_issue_operation,\n            asset_reserve_operation,\n            asset_fund_fee_pool_operation,\n            asset_settle_operation,\n            asset_global_settle_operation,\n            asset_publish_feed_operation,\n            witness_create_operation,\n            witness_update_operation,\n            proposal_create_operation,\n            proposal_update_operation,\n            proposal_delete_operation,\n            withdraw_permission_create_operation,\n            withdraw_permission_update_operation,\n            withdraw_permission_claim_operation,\n            withdraw_permission_delete_operation,\n            committee_member_create_operation,\n            committee_member_update_operation,\n            committee_member_update_global_parameters_operation,\n            vesting_balance_create_operation,\n            vesting_balance_withdraw_operation,\n            worker_create_operation,\n            custom_operation,\n            assert_operation,\n            balance_claim_operation,\n            override_transfer_operation,\n            transfer_to_blind_operation,\n            blind_transfer_operation,\n            transfer_from_blind_operation,\n            asset_settle_cancel_operation,  // VIRTUAL\n            asset_claim_fees_operation,\n            fba_distribute_operation,       // VIRTUAL\n            bid_collateral_operation,\n            execute_bid_operation,          // VIRTUAL\n            asset_claim_pool_operation,\n            asset_update_issuer_operation,\n            htlc_create_operation,\n            htlc_redeem_operation,\n            htlc_redeemed_operation,         // VIRTUAL\n            htlc_extend_operation,\n            htlc_refund_operation,           // VIRTUAL\n            custom_authority_create_operation,\n            custom_authority_update_operation,\n            custom_authority_delete_operation,\n            ticket_create_operation,\n            ticket_update_operation,\n            liquidity_pool_create_operation,\n            liquidity_pool_delete_operation,\n            liquidity_pool_deposit_operation,\n            liquidity_pool_withdraw_operation,\n            liquidity_pool_exchange_operation\n         > operation;\n\n   /// @} // operations group\n\n   /**\n    *  Appends required authorites to the result vector.  The authorities appended are not the\n    *  same as those returned by get_required_auth \n    *\n    *  @return a set of required authorities for @ref op\n    */\n   void operation_get_required_authorities( const operation& op,\n                                            flat_set<account_id_type>& active,\n                                            flat_set<account_id_type>& owner,\n                                            vector<authority>& other,\n                                            bool ignore_custom_operation_required_auths );\n\n   void operation_validate( const operation& op );\n\n   /**\n    *  @brief necessary to support nested operations inside the proposal_create_operation\n    */\n   struct op_wrapper\n   {\n      public:\n         op_wrapper(const operation& op = operation()):op(op){}\n         operation op;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT_TYPENAME( graphene::protocol::operation )\nFC_REFLECT( graphene::protocol::op_wrapper, (op) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::op_wrapper )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/proposal.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol { \n   /**\n     * @defgroup proposed_transactions  The BitShares Transaction Proposal Protocol\n     * @ingroup operations\n     *\n     * BitShares allows users to propose a transaction which requires approval of multiple accounts in order to execute.\n     * The user proposes a transaction using proposal_create_operation, then signatory accounts use\n     * proposal_update_operations to add or remove their approvals from this operation. When a sufficient number of\n     * approvals have been granted, the operations in the proposal are used to create a virtual transaction which is\n     * subsequently evaluated. Even if the transaction fails, the proposal will be kept until the expiration time, at\n     * which point, if sufficient approval is granted, the transaction will be evaluated a final time. This allows\n     * transactions which will not execute successfully until a given time to still be executed through the proposal\n     * mechanism. The first time the proposed transaction succeeds, the proposal will be regarded as resolved, and all\n     * future updates will be invalid.\n     *\n     * The proposal system allows for arbitrarily complex or recursively nested authorities. If a recursive authority\n     * (i.e. an authority which requires approval of 'nested' authorities on other accounts) is required for a\n     * proposal, then a second proposal can be used to grant the nested authority's approval. That is, a second\n     * proposal can be created which, when sufficiently approved, adds the approval of a nested authority to the first\n     * proposal. This multiple-proposal scheme can be used to acquire approval for an arbitrarily deep authority tree.\n     *\n     * Note that at any time, a proposal can be approved in a single transaction if sufficient signatures are available\n     * on the proposal_update_operation, as long as the authority tree to approve the proposal does not exceed the\n     * maximum recursion depth. In practice, however, it is easier to use proposals to acquire all approvals, as this\n     * leverages on-chain notification of all relevant parties that their approval is required. Off-chain\n     * multi-signature approval requires some off-chain mechanism for acquiring several signatures on a single\n     * transaction. This off-chain synchronization can be avoided using proposals.\n     * @{\n     */\n   /**\n    * op_wrapper is used to get around the circular definition of operation and proposals that contain them.\n    */\n   struct op_wrapper;\n   /**\n    * @brief The proposal_create_operation creates a transaction proposal, for use in multi-sig scenarios\n    * @ingroup operations\n    *\n    * Creates a transaction proposal. The operations which compose the transaction are listed in order in proposed_ops,\n    * and expiration_time specifies the time by which the proposal must be accepted or it will fail permanently. The\n    * expiration_time cannot be farther in the future than the maximum expiration time set in the global properties\n    * object.\n    */\n   struct proposal_create_operation : public base_operation\n   {\n       struct fee_parameters_type { \n          uint64_t fee            = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; \n          uint32_t price_per_kbyte = 10;\n       };\n\n       asset              fee;\n       account_id_type    fee_paying_account;\n       vector<op_wrapper> proposed_ops;\n       time_point_sec     expiration_time;\n       optional<uint32_t> review_period_seconds;\n       extensions_type    extensions;\n\n       /**\n        * Constructs a proposal_create_operation suitable for committee\n        * proposals, with expiration time and review period set\n        * appropriately.  No proposed_ops are added.  When used to\n        * create a proposal to change chain parameters, this method\n        * expects to receive the currently effective parameters, not\n        * the proposed parameters.  (The proposed parameters will go\n        * in proposed_ops, and proposed_ops is untouched by this\n        * function.)\n        */\n       static proposal_create_operation committee_proposal(const chain_parameters& param, fc::time_point_sec head_block_time );\n\n       account_id_type fee_payer()const { return fee_paying_account; }\n       void            validate()const;\n       share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n   /**\n    * @brief The proposal_update_operation updates an existing transaction proposal\n    * @ingroup operations\n    *\n    * This operation allows accounts to add or revoke approval of a proposed transaction. Signatures sufficient to\n    * satisfy the authority of each account in approvals are required on the transaction containing this operation.\n    *\n    * If an account with a multi-signature authority is listed in approvals_to_add or approvals_to_remove, either all\n    * required signatures to satisfy that account's authority must be provided in the transaction containing this\n    * operation, or a secondary proposal must be created which contains this operation.\n    *\n    * NOTE: If the proposal requires only an account's active authority, the account must not update adding its owner\n    * authority's approval. This is considered an error. An owner approval may only be added if the proposal requires\n    * the owner's authority.\n    *\n    * If an account's owner and active authority are both required, only the owner authority may approve. An attempt to\n    * add or remove active authority approval to such a proposal will fail.\n    */\n   struct proposal_update_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         uint64_t fee            = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; \n         uint32_t price_per_kbyte = 10;\n      };\n\n      account_id_type            fee_paying_account;\n      asset                      fee;\n      proposal_id_type           proposal;\n      flat_set<account_id_type>  active_approvals_to_add;\n      flat_set<account_id_type>  active_approvals_to_remove;\n      flat_set<account_id_type>  owner_approvals_to_add;\n      flat_set<account_id_type>  owner_approvals_to_remove;\n      flat_set<public_key_type>  key_approvals_to_add;\n      flat_set<public_key_type>  key_approvals_to_remove;\n      extensions_type            extensions;\n\n      account_id_type fee_payer()const { return fee_paying_account; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n      void get_required_authorities( vector<authority>& )const;\n      void get_required_active_authorities( flat_set<account_id_type>& )const;\n      void get_required_owner_authorities( flat_set<account_id_type>& )const;\n   };\n\n   /**\n    * @brief The proposal_delete_operation deletes an existing transaction proposal\n    * @ingroup operations\n    *\n    * This operation allows the early veto of a proposed transaction. It may be used by any account which is a required\n    * authority on the proposed transaction, when that account's holder feels the proposal is ill-advised and he decides\n    * he will never approve of it and wishes to put an end to all discussion of the issue. Because he is a required\n    * authority, he could simply refuse to add his approval, but this would leave the topic open for debate until the\n    * proposal expires. Using this operation, he can prevent any further breath from being wasted on such an absurd\n    * proposal.\n    */\n   struct proposal_delete_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee =  GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      account_id_type   fee_paying_account;\n      bool              using_owner_authority = false;\n      asset             fee;\n      proposal_id_type  proposal;\n      extensions_type   extensions;\n\n      account_id_type fee_payer()const { return fee_paying_account; }\n      void       validate()const;\n   };\n   ///@}\n   \n}} // graphene::protocol\n\nFC_REFLECT( graphene::protocol::proposal_create_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::proposal_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::proposal_delete_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::proposal_create_operation, (fee)(fee_paying_account)(expiration_time)\n            (proposed_ops)(review_period_seconds)(extensions) )\nFC_REFLECT( graphene::protocol::proposal_update_operation, (fee)(fee_paying_account)(proposal)\n            (active_approvals_to_add)(active_approvals_to_remove)(owner_approvals_to_add)(owner_approvals_to_remove)\n            (key_approvals_to_add)(key_approvals_to_remove)(extensions) )\nFC_REFLECT( graphene::protocol::proposal_delete_operation, (fee)(fee_paying_account)(using_owner_authority)(proposal)(extensions) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_delete_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_update_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_delete_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/pts_address.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <array>\n#include <cstring>\n#include <string>\n\n#include <fc/io/datastream.hpp>\n#include <fc/io/raw_fwd.hpp>\n#include <fc/variant.hpp>\n\nnamespace fc { namespace ecc { class public_key; } }\n\nnamespace graphene { namespace protocol {\n\n   /**\n    *  Implements address stringification and validation from PTS\n    */\n   struct pts_address\n   {\n       pts_address(); ///< constructs empty / null address\n       pts_address( const std::string& base58str );   ///< converts to binary, validates checksum\n       pts_address( const fc::ecc::public_key& pub, bool compressed = true, uint8_t version=56 ); ///< converts to binary\n\n       uint8_t version()const { return addr.at(0); }\n       bool is_valid()const;\n\n       operator std::string()const; ///< converts to base58 + checksum\n\n       std::array<char,25> addr{}; ///< binary representation of address, 0-initialized\n   };\n\n   inline bool operator == ( const pts_address& a, const pts_address& b ) { return a.addr == b.addr; }\n   inline bool operator != ( const pts_address& a, const pts_address& b ) { return a.addr != b.addr; }\n   inline bool operator <  ( const pts_address& a, const pts_address& b ) { return a.addr <  b.addr; }\n\n} } // namespace graphene::protocol\n\nnamespace std\n{\n   template<>\n   struct hash<graphene::protocol::pts_address> \n   {\n       public:\n         size_t operator()(const graphene::protocol::pts_address &a) const \n         {\n            size_t s;\n            std::memcpy( (char*)&s, a.addr.data() + a.addr.size() - sizeof(s), sizeof(s) );\n            return s;\n         }\n   };\n}\n\n#include <fc/reflect/reflect.hpp>\nFC_REFLECT( graphene::protocol::pts_address, (addr) )\n\nnamespace fc \n{ \n   void to_variant( const graphene::protocol::pts_address& var,  fc::variant& vo, uint32_t max_depth = 1 );\n   void from_variant( const fc::variant& var,  graphene::protocol::pts_address& vo, uint32_t max_depth = 1 );\n\nnamespace raw {\n   extern template void pack( datastream<size_t>& s, const graphene::protocol::pts_address& tx,\n                              uint32_t _max_depth );\n   extern template void pack( datastream<char*>& s, const graphene::protocol::pts_address& tx,\n                              uint32_t _max_depth );\n   extern template void unpack( datastream<const char*>& s, graphene::protocol::pts_address& tx,\n                                uint32_t _max_depth );\n} } // fc::raw\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/restriction.hpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace protocol {\n\n/**\n  * Defines the set of valid operation restritions as a discriminated union type.\n  */\nstruct restriction {\n   enum function_type {\n      func_eq,\n      func_ne,\n      func_lt,\n      func_le,\n      func_gt,\n      func_ge,\n      func_in,\n      func_not_in,\n      func_has_all,\n      func_has_none,\n      func_attr,\n      func_logical_or,\n      func_variant_assert,\n      FUNCTION_TYPE_COUNT ///< Sentry value which contains the number of different types\n   };\n\n   // A variant assertion argument is a pair of the tag expected to be in the variant, and the restrictions to apply\n   // to the value\n   using variant_assert_argument_type = pair<int64_t, vector<restriction>>;\n\n#define GRAPHENE_OP_RESTRICTION_ARGUMENTS_VARIADIC \\\n   /*  0 */ void_t, \\\n   /*  1 */ bool, \\\n   /*  2 */ int64_t, \\\n   /*  3 */ string, \\\n   /*  4 */ time_point_sec, \\\n   /*  5 */ public_key_type, \\\n   /*  6 */ fc::sha256, \\\n   /*  7 */ account_id_type, \\\n   /*  8 */ asset_id_type, \\\n   /*  9 */ force_settlement_id_type, \\\n   /* 10 */ committee_member_id_type, \\\n   /* 11 */ witness_id_type, \\\n   /* 12 */ limit_order_id_type, \\\n   /* 13 */ call_order_id_type, \\\n   /* 14 */ custom_id_type, \\\n   /* 15 */ proposal_id_type, \\\n   /* 16 */ withdraw_permission_id_type, \\\n   /* 17 */ vesting_balance_id_type, \\\n   /* 18 */ worker_id_type, \\\n   /* 19 */ balance_id_type, \\\n   /* 20 */ flat_set<bool>, \\\n   /* 21 */ flat_set<int64_t>, \\\n   /* 22 */ flat_set<string>, \\\n   /* 23 */ flat_set<time_point_sec>, \\\n   /* 24 */ flat_set<public_key_type>, \\\n   /* 25 */ flat_set<fc::sha256>, \\\n   /* 26 */ flat_set<account_id_type>, \\\n   /* 27 */ flat_set<asset_id_type>, \\\n   /* 28 */ flat_set<force_settlement_id_type>, \\\n   /* 29 */ flat_set<committee_member_id_type>, \\\n   /* 30 */ flat_set<witness_id_type>, \\\n   /* 31 */ flat_set<limit_order_id_type>, \\\n   /* 32 */ flat_set<call_order_id_type>, \\\n   /* 33 */ flat_set<custom_id_type>, \\\n   /* 34 */ flat_set<proposal_id_type>, \\\n   /* 35 */ flat_set<withdraw_permission_id_type>, \\\n   /* 36 */ flat_set<vesting_balance_id_type>, \\\n   /* 37 */ flat_set<worker_id_type>, \\\n   /* 38 */ flat_set<balance_id_type>, \\\n   /* 39 */ vector<restriction>, \\\n   /* 40 */ vector<vector<restriction>>, \\\n   /* 41 */ variant_assert_argument_type\n\n   using argument_type = fc::static_variant<GRAPHENE_OP_RESTRICTION_ARGUMENTS_VARIADIC>;\n\n   unsigned_int member_index;\n   unsigned_int restriction_type;\n   argument_type argument;\n\n   extensions_type extensions;\n\n   restriction() = default;\n   restriction(const unsigned_int& member_index, function_type type, const argument_type& argument)\n      : member_index(member_index), restriction_type(type), argument(argument) {}\n\n   static size_t restriction_count(const vector<restriction>& restrictions);\n   size_t restriction_count() const;\n};\n\n} } // graphene::protocol\n\nFC_REFLECT_ENUM(graphene::protocol::restriction::function_type,\n                (func_eq)\n                (func_ne)\n                (func_lt)\n                (func_le)\n                (func_gt)\n                (func_ge)\n                (func_in)\n                (func_not_in)\n                (func_has_all)\n                (func_has_none)\n                (func_attr)\n                (func_logical_or)\n                (func_variant_assert)\n                (FUNCTION_TYPE_COUNT))\n\nFC_REFLECT(graphene::protocol::restriction,\n           (member_index)\n           (restriction_type)\n           (argument)\n           (extensions))\n\n\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/restriction_predicate.hpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/restriction.hpp>\n#include <graphene/protocol/operations.hpp>\n\n#include <functional>\n\nnamespace graphene { namespace protocol {\n\n/// A type describing the result of a restriction predicate\nstruct predicate_result {\n   /// Whether or not the operation complied with the restrictions or not\n   bool success = false;\n\n   /// Enumeration of the general reasons a predicate may reject\n   enum rejection_reason {\n      predicate_was_false,\n      null_optional,\n      incorrect_variant_type\n   };\n\n   /// An indicator of what rejection occurred at a particular restriction -- either an index to a sub-restriction, a\n   /// list of rejection results from the branches of a logical OR, or the immediate reason for rejection\n   using rejection_indicator = static_variant<size_t, vector<predicate_result>, rejection_reason>;\n   /// Failure indicators, ordered from the outermost restriction to the innermost (the location of the rejection)\n   vector<rejection_indicator> rejection_path;\n\n   static predicate_result Rejection(rejection_reason reason) { return {false, {reason}}; }\n   static predicate_result Rejection(vector<predicate_result> branches) { return {false, {std::move(branches)}}; }\n   static predicate_result Success() { return {true, {}}; }\n\n   operator bool() const { return success; }\n\n   /// Reverse the order of the rejection path. Returns a reference to this object\n   predicate_result& reverse_path();\n};\n\n/// A restriction predicate is a function accepting an operation and returning a predicate_result\nusing restriction_predicate_function = std::function<predicate_result(const operation&)>;\n\n/**\n * @brief get_restriction_predicate Get a predicate function for the supplied restriction\n * @param rs The restrictions to evaluate operations against\n * @param op_type The tag specifying which operation type the restrictions apply to\n * @return A predicate function which evaluates an operation to determine whether it complies with the restriction\n */\nrestriction_predicate_function get_restriction_predicate(vector<restriction> rs, operation::tag_type op_type);\n\n} } // namespace graphene::protocol\n\nFC_REFLECT_ENUM(graphene::protocol::predicate_result::rejection_reason,\n                (predicate_was_false)(null_optional)(incorrect_variant_type))\nFC_REFLECT_TYPENAME(graphene::protocol::predicate_result::rejection_indicator)\nFC_REFLECT(graphene::protocol::predicate_result, (success)(rejection_path))\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/special_authority.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace protocol {\n\nstruct no_special_authority {};\n\nstruct top_holders_special_authority\n{\n   asset_id_type asset;\n   uint8_t       num_top_holders = 1;\n};\n\ntypedef static_variant<\n   no_special_authority,\n   top_holders_special_authority\n   > special_authority;\n\nvoid validate_special_authority( const special_authority& auth );\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::no_special_authority, )\nFC_REFLECT( graphene::protocol::top_holders_special_authority, (asset)(num_top_holders) )\nFC_REFLECT_TYPENAME( graphene::protocol::special_authority )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::top_holders_special_authority )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/ticket.hpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\n#include <fc/optional.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /// Type of a ticket\n   enum ticket_type\n   {\n      liquid            = 0,\n      lock_180_days     = 1,\n      lock_360_days     = 2,\n      lock_720_days     = 3,\n      lock_forever      = 4,\n      TICKET_TYPE_COUNT = 5\n   };\n\n   /**\n    * @brief Creates a new ticket\n    * @ingroup operations\n    */\n   struct ticket_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 50 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;         ///< Operation fee\n      account_id_type account;     ///< The account who creates the ticket\n      unsigned_int    target_type; ///< The target ticket type, see @ref ticket_type\n      asset           amount;      ///< The amount of the ticket\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Updates an existing ticket\n    * @ingroup operations\n    */\n   struct ticket_update_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 50 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset           fee;         ///< Operation fee\n      ticket_id_type  ticket;      ///< The ticket to update\n      account_id_type account;     ///< The account who owns the ticket\n      unsigned_int    target_type; ///< New target ticket type, see @ref ticket_type\n      optional<asset> amount_for_new_target; ///< The amount to be used for the new target\n\n      extensions_type extensions;  ///< Unused. Reserved for future use.\n\n      account_id_type fee_payer()const { return account; }\n      void            validate()const;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT_ENUM( graphene::protocol::ticket_type,\n                 (liquid)(lock_180_days)(lock_360_days)(lock_720_days)(lock_forever)(TICKET_TYPE_COUNT) )\n\nFC_REFLECT( graphene::protocol::ticket_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::ticket_update_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::ticket_create_operation,\n            (fee)(account)(target_type)(amount)(extensions) )\nFC_REFLECT( graphene::protocol::ticket_update_operation,\n            (fee)(ticket)(account)(target_type)(amount_for_new_target)(extensions) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_update_operation::fee_parameters_type )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_update_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/transaction.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/operations.hpp>\n\nnamespace graphene { namespace protocol {\n   struct predicate_result;\n\n   using rejected_predicate = static_variant<predicate_result, fc::exception>;\n   using rejected_predicate_map = map<custom_authority_id_type, rejected_predicate>;\n   using custom_authority_lookup = std::function<vector<authority>(account_id_type, const operation&,\n                                                                   rejected_predicate_map*)>;\n\n   /**\n    * @defgroup transactions Transactions\n    *\n    * All transactions are sets of operations that must be applied atomically. Transactions must refer to a recent\n    * block that defines the context of the operation so that they assert a known binding to the object id's referenced\n    * in the transaction.\n    *\n    * Rather than specify a full block number, we only specify the lower 16 bits of the block number which means you\n    * can reference any block within the last 65,536 blocks which is 3.5 days with a 5 second block interval or 18\n    * hours with a 1 second interval.\n    *\n    * All transactions must expire so that the network does not have to maintain a permanent record of all transactions\n    * ever published.  A transaction may not have an expiration date too far in the future because this would require\n    * keeping too much transaction history in memory.\n    *\n    * The block prefix is the first 4 bytes of the block hash of the reference block number, which is the second 4\n    * bytes of the @ref block_id_type (the first 4 bytes of the block ID are the block number)\n    *\n    * Note: A transaction which selects a reference block cannot be migrated between forks outside the period of\n    * ref_block_num.time to (ref_block_num.time + rel_exp * interval). This fact can be used to protect market orders\n    * which should specify a relatively short re-org window of perhaps less than 1 minute. Normal payments should\n    * probably have a longer re-org window to ensure their transaction can still go through in the event of a momentary\n    * disruption in service.\n    *\n    * @note It is not recommended to set the @ref ref_block_num, @ref ref_block_prefix, and @ref expiration\n    * fields manually. Call the appropriate overload of @ref set_expiration instead.\n    *\n    * @{\n    */\n\n   /**\n    *  @brief groups operations that should be applied atomically\n    */\n   class transaction\n   {\n   public:\n      virtual ~transaction() = default;\n      /**\n       * Least significant 16 bits from the reference block number. If @ref relative_expiration is zero, this field\n       * must be zero as well.\n       */\n      uint16_t           ref_block_num    = 0;\n      /**\n       * The first non-block-number 32-bits of the reference block ID. Recall that block IDs have 32 bits of block\n       * number followed by the actual block hash, so this field should be set using the second 32 bits in the\n       * @ref block_id_type\n       */\n      uint32_t           ref_block_prefix = 0;\n\n      /**\n       * This field specifies the absolute expiration for this transaction.\n       */\n      fc::time_point_sec expiration;\n\n      vector<operation>  operations;\n      extensions_type    extensions;\n\n      /// Calculate the digest for a transaction\n      digest_type                        digest()const;\n      virtual const transaction_id_type& id()const;\n      virtual void                       validate() const;\n\n      void set_expiration( fc::time_point_sec expiration_time );\n      void set_reference_block( const block_id_type& reference_block );\n\n      /// visit all operations\n      template<typename Visitor>\n      vector<typename Visitor::result_type> visit( Visitor&& visitor )\n      {\n         vector<typename Visitor::result_type> results;\n         for( auto& op : operations )\n            results.push_back(op.visit( std::forward<Visitor>( visitor ) ));\n         return results;\n      }\n      template<typename Visitor>\n      vector<typename Visitor::result_type> visit( Visitor&& visitor )const\n      {\n         vector<typename Visitor::result_type> results;\n         for( auto& op : operations )\n            results.push_back(op.visit( std::forward<Visitor>( visitor ) ));\n         return results;\n      }\n\n      void get_required_authorities( flat_set<account_id_type>& active,\n                                     flat_set<account_id_type>& owner,\n                                     vector<authority>& other,\n                                     bool ignore_custom_operation_required_auths )const;\n\n      virtual uint64_t get_packed_size()const;\n\n   protected:\n      // Calculate the digest used for signature validation\n      digest_type sig_digest( const chain_id_type& chain_id )const;\n      mutable transaction_id_type _tx_id_buffer;\n   };\n\n   /**\n    *  @brief adds a signature to a transaction\n    */\n   class signed_transaction : public transaction\n   {\n   public:\n      signed_transaction( const transaction& trx = transaction() )\n         : transaction(trx){}\n      virtual ~signed_transaction() = default;\n\n      /** signs and appends to signatures */\n      const signature_type& sign( const private_key_type& key, const chain_id_type& chain_id );\n\n      /** returns signature but does not append */\n      signature_type sign( const private_key_type& key, const chain_id_type& chain_id )const;\n\n      /**\n       *  The purpose of this method is to identify some subset of\n       *  @ref available_keys that will produce sufficient signatures\n       *  for a transaction.  The result is not always a minimal set of\n       *  signatures, but any non-minimal result will still pass\n       *  validation.\n       */\n      set<public_key_type> get_required_signatures(\n              const chain_id_type& chain_id,\n              const flat_set<public_key_type>& available_keys,\n              const std::function<const authority*(account_id_type)>& get_active,\n              const std::function<const authority*(account_id_type)>& get_owner,\n              bool allow_non_immediate_owner,\n              bool ignore_custom_operation_required_authorities,\n              uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;\n\n      /**\n       * Checks whether signatures in this signed transaction are sufficient to authorize the transaction.\n       *   Throws an exception when failed.\n       *\n       * @param chain_id the ID of a block chain\n       * @param get_active callback function to retrieve active authorities of a given account\n       * @param get_owner  callback function to retrieve owner authorities of a given account\n       * @param get_custom callback function to retrieve viable custom authorities for a given account and operation\n       * @param allow_non_immediate_owner whether to allow owner authority of non-immediately\n       *            required accounts to authorize operations in the transaction\n       * @param ignore_custom_operation_required_auths See issue #210; whether to ignore the\n       *            required_auths field of custom_operation or not\n       * @param max_recursion maximum level of recursion when verifying, since an account\n       *            can have another account in active authorities and/or owner authorities\n       */\n      void verify_authority(\n              const chain_id_type& chain_id,\n              const std::function<const authority*(account_id_type)>& get_active,\n              const std::function<const authority*(account_id_type)>& get_owner,\n              const custom_authority_lookup& get_custom,\n              bool allow_non_immediate_owner,\n              bool ignore_custom_operation_required_auths,\n              uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;\n\n      /**\n       * This is a slower replacement for get_required_signatures()\n       * which returns a minimal set in all cases, including\n       * some cases where get_required_signatures() returns a\n       * non-minimal set.\n       */\n      set<public_key_type> minimize_required_signatures(\n              const chain_id_type& chain_id,\n              const flat_set<public_key_type>& available_keys,\n              const std::function<const authority*(account_id_type)>& get_active,\n              const std::function<const authority*(account_id_type)>& get_owner,\n              const custom_authority_lookup& get_custom,\n              bool allow_non_immediate_owner,\n              bool ignore_custom_operation_required_auths,\n              uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH) const;\n\n      /**\n       * @brief Extract public keys from signatures with given chain ID.\n       * @param chain_id A chain ID\n       * @return Public keys\n       * @note If @ref signees is empty, E.G. when it's the first time calling\n       *       this function for the signed transaction, public keys will be\n       *       extracted with given chain ID, and be stored into the mutable\n       *       @ref signees field, then @ref signees will be returned;\n       *       otherwise, the @ref chain_id parameter will be ignored, and\n       *       @ref signees will be returned directly.\n       */\n      virtual const flat_set<public_key_type>& get_signature_keys( const chain_id_type& chain_id )const;\n\n      /** Signatures */\n      vector<signature_type> signatures;\n\n      /** Removes all operations and signatures */\n      void clear() { operations.clear(); signatures.clear(); }\n\n      /** Removes all signatures */\n      void clear_signatures() { signatures.clear(); }\n   protected:\n      /** Public keys extracted from signatures */\n      mutable flat_set<public_key_type> _signees;\n   };\n\n   /** This represents a signed transaction that will never have its operations,\n    *  signatures etc. modified again, after initial creation. It is therefore\n    *  safe to cache results from various calls.\n    */\n   class precomputable_transaction : public signed_transaction {\n   public:\n      precomputable_transaction() {}\n      precomputable_transaction( const signed_transaction& tx ) : signed_transaction(tx) {};\n      precomputable_transaction( signed_transaction&& tx ) : signed_transaction( std::move(tx) ) {};\n      virtual ~precomputable_transaction() = default;\n\n      virtual const transaction_id_type&       id()const override;\n      virtual void                             validate()const override;\n      virtual const flat_set<public_key_type>& get_signature_keys( const chain_id_type& chain_id )const override;\n      virtual uint64_t                         get_packed_size()const override;\n   protected:\n      mutable bool _validated = false;\n      mutable uint64_t _packed_size = 0;\n   };\n\n   /**\n    * Checks whether given public keys and approvals are sufficient to authorize given operations.\n    *   Throws an exception when failed.\n    *\n    * @param ops a vector of operations\n    * @param sigs a set of public keys\n    * @param get_active callback function to retrieve active authorities of a given account\n    * @param get_owner  callback function to retrieve owner authorities of a given account\n    * @param get_custom callback function to retrieve viable custom authorities for a given account and operation\n    * @param allow_non_immediate_owner whether to allow owner authority of non-immediately\n    *            required accounts to authorize operations\n    * @param ignore_custom_operation_required_auths See issue #210; whether to ignore the\n    *            required_auths field of custom_operation or not\n    * @param max_recursion maximum level of recursion when verifying, since an account\n    *            can have another account in active authorities and/or owner authorities\n    * @param allow_committee whether to allow the special \"committee account\" to authorize the operations\n    * @param active_approvals accounts that approved the operations with their active authories\n    * @param owner_approvals accounts that approved the operations with their owner authories\n    */\n   void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,\n                          const std::function<const authority*(account_id_type)>& get_active,\n                          const std::function<const authority*(account_id_type)>& get_owner,\n                          const custom_authority_lookup& get_custom,\n                          bool allow_non_immediate_owner,\n                          bool ignore_custom_operation_required_auths,\n                          uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,\n                          bool allow_committee = false,\n                          const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),\n                          const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>() );\n\n   /**\n    *  @brief captures the result of evaluating the operations contained in the transaction\n    *\n    *  When processing a transaction some operations generate\n    *  new object IDs and these IDs cannot be known until the\n    *  transaction is actually included into a block.  When a\n    *  block is produced these new ids are captured and included\n    *  with every transaction.  The index in operation_results should\n    *  correspond to the same index in operations.\n    *\n    *  If an operation did not create any new object IDs then 0\n    *  should be returned.\n    */\n   struct processed_transaction : public precomputable_transaction\n   {\n      processed_transaction( const signed_transaction& trx = signed_transaction() )\n         : precomputable_transaction(trx){}\n      virtual ~processed_transaction() = default;\n\n      vector<operation_result> operation_results;\n\n      digest_type merkle_digest()const;\n   };\n\n   /// @} transactions group\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::transaction, (ref_block_num)(ref_block_prefix)(expiration)(operations)(extensions) )\n// Note: not reflecting signees field for backward compatibility; in addition, it should not be in p2p messages\nFC_REFLECT_DERIVED( graphene::protocol::signed_transaction, (graphene::protocol::transaction), (signatures) )\nFC_REFLECT_DERIVED( graphene::protocol::precomputable_transaction, (graphene::protocol::signed_transaction), )\nFC_REFLECT_DERIVED( graphene::protocol::processed_transaction, (graphene::protocol::precomputable_transaction), (operation_results) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transaction)\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::signed_transaction)\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::precomputable_transaction)\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::processed_transaction)\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/transfer.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/memo.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    * @ingroup operations\n    *\n    * @brief Transfers an amount of one asset from one account to another\n    *\n    *  Fees are paid by the \"from\" account\n    *\n    *  @pre amount.amount > 0\n    *  @pre fee.amount >= 0\n    *  @pre from != to\n    *  @post from account's balance will be reduced by fee and amount\n    *  @post to account's balance will be increased by amount\n    *  @return n/a\n    */\n   struct transfer_operation : public base_operation\n   {\n      struct fee_parameters_type {\n         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t price_per_kbyte = 10 * GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.\n      };\n\n      asset            fee;\n      /// Account to transfer asset from\n      account_id_type  from;\n      /// Account to transfer asset to\n      account_id_type  to;\n      /// The amount of asset to transfer from @ref from to @ref to\n      asset            amount;\n\n      /// User provided data encrypted to the memo key of the \"to\" account\n      optional<memo_data> memo;\n      extensions_type   extensions;\n\n      account_id_type fee_payer()const { return from; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n   /**\n    *  @class override_transfer_operation\n    *  @brief Allows the issuer of an asset to transfer an asset from any account to any account if they have override_authority\n    *  @ingroup operations\n    *\n    *  @pre amount.asset_id->issuer == issuer\n    *  @pre issuer != from  because this is pointless, use a normal transfer operation\n    */\n   struct override_transfer_operation : public base_operation\n   {\n      struct fee_parameters_type {\n         uint64_t fee       = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         uint32_t price_per_kbyte = 10; /// only required for large memos.\n      };\n\n      asset           fee;\n      account_id_type issuer;\n      /// Account to transfer asset from\n      account_id_type from;\n      /// Account to transfer asset to\n      account_id_type to;\n      /// The amount of asset to transfer from @ref from to @ref to\n      asset amount;\n\n      /// User provided data encrypted to the memo key of the \"to\" account\n      optional<memo_data> memo;\n      extensions_type   extensions;\n\n      account_id_type fee_payer()const { return issuer; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n}} // graphene::protocol\n\nFC_REFLECT( graphene::protocol::transfer_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::override_transfer_operation::fee_parameters_type, (fee)(price_per_kbyte) )\n\nFC_REFLECT( graphene::protocol::override_transfer_operation, (fee)(issuer)(from)(to)(amount)(memo)(extensions) )\nFC_REFLECT( graphene::protocol::transfer_operation, (fee)(from)(to)(amount)(memo)(extensions) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::override_transfer_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::override_transfer_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/types.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <memory>\n#include <vector>\n#include <deque>\n#include <cstdint>\n\n#include <boost/preprocessor/seq/for_each.hpp>\n#include <boost/preprocessor/seq/transform.hpp>\n#include <boost/preprocessor/seq/elem.hpp>\n#include <boost/preprocessor/seq/enum.hpp>\n#include <boost/preprocessor/tuple/elem.hpp>\n#include <boost/preprocessor/cat.hpp>\n\n#include <boost/rational.hpp>\n\n#include <fc/container/flat_fwd.hpp>\n#include <fc/io/varint.hpp>\n#include <fc/io/enum_type.hpp>\n#include <fc/crypto/ripemd160.hpp>\n#include <fc/crypto/sha1.hpp>\n#include <fc/crypto/sha224.hpp>\n#include <fc/crypto/sha256.hpp>\n#include <fc/crypto/hash160.hpp>\n#include <fc/crypto/elliptic.hpp>\n#include <fc/reflect/reflect.hpp>\n#include <fc/reflect/variant.hpp>\n#include <fc/optional.hpp>\n#include <fc/safe.hpp>\n#include <fc/container/flat.hpp>\n#include <fc/string.hpp>\n\n#include <fc/io/datastream.hpp>\n#include <fc/io/raw_fwd.hpp>\n#include <fc/static_variant.hpp>\n\n#include <graphene/protocol/object_id.hpp>\n#include <graphene/protocol/config.hpp>\n\n#define GRAPHENE_EXTERNAL_SERIALIZATION(ext, type) \\\nnamespace fc { \\\n   ext template void from_variant( const variant& v, type& vo, uint32_t max_depth ); \\\n   ext template void to_variant( const type& v, variant& vo, uint32_t max_depth ); \\\nnamespace raw { \\\n   ext template void pack< datastream<size_t>, type >( datastream<size_t>& s, const type& tx, uint32_t _max_depth ); \\\n   ext template void pack< sha256::encoder, type >( sha256::encoder& s, const type& tx, uint32_t _max_depth ); \\\n   ext template void pack< datastream<char*>, type >( datastream<char*>& s, const type& tx, uint32_t _max_depth ); \\\n   ext template void unpack< datastream<const char*>, type >( datastream<const char*>& s, type& tx, uint32_t _max_depth ); \\\n} } // fc::raw\n#define GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION(type) GRAPHENE_EXTERNAL_SERIALIZATION(extern, type)\n#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type) GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, type)\n\n#define GRAPHENE_NAME_TO_OBJECT_TYPE(x, prefix, name) BOOST_PP_CAT(prefix, BOOST_PP_CAT(name, _object_type))\n#define GRAPHENE_NAME_TO_ID_TYPE(x, y, name) BOOST_PP_CAT(name, _id_type)\n#define GRAPHENE_DECLARE_ID(x, space_prefix_seq, name) \\\n    using BOOST_PP_CAT(name, _id_type) = object_id<BOOST_PP_TUPLE_ELEM(2, 0, space_prefix_seq), \\\n                            GRAPHENE_NAME_TO_OBJECT_TYPE(x, BOOST_PP_TUPLE_ELEM(2, 1, space_prefix_seq), name)>;\n#define GRAPHENE_REFLECT_ID(x, id_namespace, name) FC_REFLECT_TYPENAME(graphene::id_namespace::name)\n\n#define GRAPHENE_DEFINE_IDS(id_namespace, object_space, object_type_prefix, names_seq) \\\n   namespace graphene { namespace id_namespace { \\\n   \\\n   enum BOOST_PP_CAT(object_type_prefix, object_type) { \\\n      BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GRAPHENE_NAME_TO_OBJECT_TYPE, object_type_prefix, names_seq)) \\\n   }; \\\n   \\\n   BOOST_PP_SEQ_FOR_EACH(GRAPHENE_DECLARE_ID, (object_space, object_type_prefix), names_seq) \\\n   \\\n   } } \\\n   \\\n   FC_REFLECT_ENUM(graphene::id_namespace::BOOST_PP_CAT(object_type_prefix, object_type), \\\n                   BOOST_PP_SEQ_TRANSFORM(GRAPHENE_NAME_TO_OBJECT_TYPE, object_type_prefix, names_seq)) \\\n   BOOST_PP_SEQ_FOR_EACH(GRAPHENE_REFLECT_ID, id_namespace, BOOST_PP_SEQ_TRANSFORM(GRAPHENE_NAME_TO_ID_TYPE, , names_seq))\n\nnamespace graphene { namespace protocol {\nusing namespace graphene::db;\n\nusing std::map;\nusing std::vector;\nusing std::unordered_map;\nusing std::string;\nusing std::deque;\nusing std::shared_ptr;\nusing std::weak_ptr;\nusing std::unique_ptr;\nusing std::set;\nusing std::pair;\nusing std::enable_shared_from_this;\nusing std::tie;\nusing std::make_pair;\n\nusing fc::variant_object;\nusing fc::variant;\nusing fc::enum_type;\nusing fc::optional;\nusing fc::unsigned_int;\nusing fc::time_point_sec;\nusing fc::time_point;\nusing fc::safe;\nusing fc::flat_map;\nusing fc::flat_set;\nusing fc::static_variant;\nusing fc::ecc::range_proof_type;\nusing fc::ecc::range_proof_info;\nusing fc::ecc::commitment_type;\nstruct void_t{};\n\nusing private_key_type = fc::ecc::private_key;\nusing chain_id_type = fc::sha256;\nusing ratio_type = boost::rational<int32_t>;\n\nenum asset_issuer_permission_flags {\n    /// @note If one of these bits is set in asset issuer permissions,\n    ///       it means the asset issuer (or owner for bitassets) has the permission to update\n    ///       the corresponding flag, parameters or perform certain actions.\n    ///@{\n    charge_market_fee    = 0x01, ///< market trades in this asset may be charged\n    white_list           = 0x02, ///< accounts must be whitelisted in order to hold or transact this asset\n    override_authority   = 0x04, ///< issuer may transfer asset back to himself\n    transfer_restricted  = 0x08, ///< require the issuer to be one party to every transfer\n    disable_force_settle = 0x10, ///< disable force settling\n    global_settle        = 0x20, ///< allow the bitasset owner to force a global settling, permission only\n    disable_confidential = 0x40, ///< disallow the asset to be used with confidential transactions\n    witness_fed_asset    = 0x80, ///< the bitasset is to be fed by witnesses\n    committee_fed_asset  = 0x100, ///< the bitasset is to be fed by the committee\n    ///@}\n    /// @note If one of these bits is set in asset issuer permissions,\n    ///       it means the asset issuer (or owner for bitassets) does NOT have the permission to update\n    ///       the corresponding flag, parameters or perform certain actions.\n    ///       This is to be compatible with old client software.\n    ///@{\n    lock_max_supply      = 0x200, ///< the max supply of the asset can not be updated\n    disable_new_supply   = 0x400, ///< unable to create new supply for the asset\n    /// @note These parameters are for issuer permission only.\n    ///       For each parameter, if it is set in issuer permission,\n    ///       it means the bitasset owner can not update the corresponding parameter.\n    ///       In this case, if the value of the parameter was set by the bitasset owner, it can not be updated;\n    ///       if no value was set by the owner, the value can still be updated by the feed producers.\n    ///@{\n    disable_mcr_update   = 0x800, ///< the bitasset owner can not update MCR, permisison only\n    disable_icr_update   = 0x1000, ///< the bitasset owner can not update ICR, permisison only\n    disable_mssr_update  = 0x2000 ///< the bitasset owner can not update MSSR, permisison only\n    ///@}\n    ///@}\n};\n\n// The bits that can be used in asset issuer permissions for non-UIA assets\nconst static uint16_t ASSET_ISSUER_PERMISSION_MASK =\n        charge_market_fee\n        | white_list\n        | override_authority\n        | transfer_restricted\n        | disable_force_settle\n        | global_settle\n        | disable_confidential\n        | witness_fed_asset\n        | committee_fed_asset\n        | lock_max_supply\n        | disable_new_supply\n        | disable_mcr_update\n        | disable_icr_update\n        | disable_mssr_update;\n// The \"enable\" bits for non-UIA assets\nconst static uint16_t ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK =\n        charge_market_fee\n        | white_list\n        | override_authority\n        | transfer_restricted\n        | disable_force_settle\n        | global_settle\n        | disable_confidential\n        | witness_fed_asset\n        | committee_fed_asset;\n// The \"disable\" bits for non-UIA assets\nconst static uint16_t ASSET_ISSUER_PERMISSION_DISABLE_BITS_MASK =\n        lock_max_supply\n        | disable_new_supply\n        | disable_mcr_update\n        | disable_icr_update\n        | disable_mssr_update;\n// The bits that can be used in asset issuer permissions for UIA assets\nconst static uint16_t UIA_ASSET_ISSUER_PERMISSION_MASK =\n        charge_market_fee\n        | white_list\n        | override_authority\n        | transfer_restricted\n        | disable_confidential\n        | lock_max_supply\n        | disable_new_supply;\n// The bits that can be used in asset issuer permissions for UIA assets before hf48/75\nconst static uint16_t DEFAULT_UIA_ASSET_ISSUER_PERMISSION =\n        charge_market_fee\n        | white_list\n        | override_authority\n        | transfer_restricted\n        | disable_confidential;\n// The bits that can be used in asset issuer permissions for non-UIA assets but not for UIA assets\nconst static uint16_t NON_UIA_ONLY_ISSUER_PERMISSION_MASK =\n        ASSET_ISSUER_PERMISSION_MASK ^ UIA_ASSET_ISSUER_PERMISSION_MASK;\n// The bits that can be used in asset issuer permissions but can not be used in flags\nconst static uint16_t PERMISSION_ONLY_MASK =\n        global_settle\n        | disable_mcr_update\n        | disable_icr_update\n        | disable_mssr_update;\n// The bits that can be used in flags for non-UIA assets\nconst static uint16_t VALID_FLAGS_MASK = ASSET_ISSUER_PERMISSION_MASK & ~PERMISSION_ONLY_MASK;\n// the bits that can be used in flags for UIA assets\nconst static uint16_t UIA_VALID_FLAGS_MASK = UIA_ASSET_ISSUER_PERMISSION_MASK;\n\nenum reserved_spaces {\n    relative_protocol_ids = 0,\n    protocol_ids          = 1,\n    implementation_ids    = 2\n};\n\ninline bool is_relative(object_id_type o) { return o.space() == 0; }\n\nusing block_id_type = fc::ripemd160;\nusing checksum_type = fc::ripemd160;\nusing transaction_id_type = fc::ripemd160;\nusing digest_type = fc::sha256;\nusing signature_type = fc::ecc::compact_signature;\nusing share_type = safe<int64_t>;\nusing weight_type = uint16_t;\n\nstruct public_key_type {\n    struct binary_key {\n        binary_key() = default;\n        uint32_t check = 0;\n        fc::ecc::public_key_data data;\n    };\n    fc::ecc::public_key_data key_data;\n    public_key_type();\n    public_key_type(const fc::ecc::public_key_data& data);\n    public_key_type(const fc::ecc::public_key& pubkey);\n    explicit public_key_type(const std::string& base58str);\n    operator fc::ecc::public_key_data() const;\n    operator fc::ecc::public_key() const;\n    explicit operator std::string() const;\n    friend bool operator == (const public_key_type& p1, const fc::ecc::public_key& p2);\n    friend bool operator == (const public_key_type& p1, const public_key_type& p2);\n    friend bool operator != (const public_key_type& p1, const public_key_type& p2);\n};\n\nclass pubkey_comparator {\npublic:\n    inline bool operator()(const public_key_type& a, const public_key_type& b) const {\n        return a.key_data < b.key_data;\n    }\n};\n\nstruct fee_schedule;\n} }  // graphene::protocol\n\nnamespace fc {\nvoid to_variant(const graphene::protocol::public_key_type& var,  fc::variant& vo, uint32_t max_depth = 2);\nvoid from_variant(const fc::variant& var,  graphene::protocol::public_key_type& vo, uint32_t max_depth = 2);\n\n\ntemplate<>\nstruct get_typename<std::shared_ptr<const graphene::protocol::fee_schedule>> { static const char* name() {\n    return \"shared_ptr<const fee_schedule>\";\n} };\ntemplate<>\nstruct get_typename<std::shared_ptr<graphene::protocol::fee_schedule>> { static const char* name() {\n    return \"shared_ptr<fee_schedule>\";\n} };\nvoid from_variant( const fc::variant& var, std::shared_ptr<const graphene::protocol::fee_schedule>& vo,\n                   uint32_t max_depth = 2 );\n\n} // fc::raw\n\nGRAPHENE_DEFINE_IDS(protocol, protocol_ids, /*protocol objects are not prefixed*/,\n                    (null)\n                    (base)\n                    (account)\n                    (asset)\n                    (force_settlement)\n                    (committee_member)\n                    (witness)\n                    (limit_order)\n                    (call_order)\n                    (custom) // unused\n                    (proposal)\n                    (operation_history)\n                    (withdraw_permission)\n                    (vesting_balance)\n                    (worker)\n                    (balance)\n                    (htlc)\n                    (custom_authority)\n                    (ticket)\n                    (liquidity_pool)\n                   )\n\nFC_REFLECT(graphene::protocol::public_key_type, (key_data))\nFC_REFLECT(graphene::protocol::public_key_type::binary_key, (data)(check))\n\nFC_REFLECT_TYPENAME(graphene::protocol::share_type)\nFC_REFLECT(graphene::protocol::void_t,)\n\nFC_REFLECT_ENUM(graphene::protocol::asset_issuer_permission_flags,\n                (charge_market_fee)\n                (white_list)\n                (transfer_restricted)\n                (override_authority)\n                (disable_force_settle)\n                (global_settle)\n                (disable_confidential)\n                (witness_fed_asset)\n                (committee_fed_asset)\n                (lock_max_supply)\n                (disable_new_supply)\n                (disable_mcr_update)\n                (disable_icr_update)\n                (disable_mssr_update)\n               )\n\nnamespace fc { namespace raw {\n   extern template void pack( datastream<size_t>& s, const graphene::protocol::public_key_type& tx,\n                              uint32_t _max_depth );\n   extern template void pack( datastream<char*>& s, const graphene::protocol::public_key_type& tx,\n                              uint32_t _max_depth );\n   extern template void unpack( datastream<const char*>& s, graphene::protocol::public_key_type& tx,\n                                uint32_t _max_depth );\n} } // fc::raw\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/vesting.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol { \n\n   struct linear_vesting_policy_initializer\n   {\n      /** while vesting begins on begin_timestamp, none may be claimed before vesting_cliff_seconds have passed */\n      fc::time_point_sec begin_timestamp;\n      uint32_t           vesting_cliff_seconds = 0;\n      uint32_t           vesting_duration_seconds = 0;\n   };\n\n   struct cdd_vesting_policy_initializer\n   {\n      /** while coindays may accrue over time, none may be claimed before the start_claim time */\n      fc::time_point_sec start_claim;\n      uint32_t           vesting_seconds = 0;\n      cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){}\n   };\n\n   struct instant_vesting_policy_initializer\n   {\n   };\n\n   typedef fc::static_variant<\n      linear_vesting_policy_initializer,\n      cdd_vesting_policy_initializer,\n      instant_vesting_policy_initializer\n   > vesting_policy_initializer;\n\n\n   /**\n    * @brief Create a vesting balance.\n    * @ingroup operations\n    *\n    *  The chain allows a user to create a vesting balance.\n    *  Normally, vesting balances are created automatically as part\n    *  of cashback and worker operations.  This operation allows\n    *  vesting balances to be created manually as well.\n    *\n    *  Manual creation of vesting balances can be used by a stakeholder\n    *  to publicly demonstrate that they are committed to the chain.\n    *  It can also be used as a building block to create transactions\n    *  that function like public debt.  Finally, it is useful for\n    *  testing vesting balance functionality.\n    *\n    * @return ID of newly created vesting_balance_object\n    */\n   struct vesting_balance_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                       fee;\n      account_id_type             creator; ///< Who provides funds initially\n      account_id_type             owner; ///< Who is able to withdraw the balance\n      asset                       amount;\n      vesting_policy_initializer  policy;\n\n      account_id_type   fee_payer()const { return creator; }\n      void              validate()const\n      {\n         FC_ASSERT( fee.amount >= 0 );\n         FC_ASSERT( amount.amount > 0 );\n      }\n   };\n\n   /**\n    * @brief Withdraw from a vesting balance.\n    * @ingroup operations\n    *\n    * Withdrawal from a not-completely-mature vesting balance\n    * will result in paying fees.\n    *\n    * @return Nothing\n    */\n   struct vesting_balance_withdraw_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 20*GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                   fee;\n      vesting_balance_id_type vesting_balance;\n      account_id_type         owner; ///< Must be vesting_balance.owner\n      asset                   amount;\n\n      account_id_type   fee_payer()const { return owner; }\n      void              validate()const\n      {\n         FC_ASSERT( fee.amount >= 0 );\n         FC_ASSERT( amount.amount > 0 );\n         FC_ASSERT( owner != GRAPHENE_TEMP_ACCOUNT );\n      }\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::vesting_balance_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::vesting_balance_withdraw_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy) )\nFC_REFLECT( graphene::protocol::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) )\n\nFC_REFLECT(graphene::protocol::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) )\nFC_REFLECT(graphene::protocol::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) )\nFC_REFLECT_EMPTY( graphene::protocol::instant_vesting_policy_initializer )\nFC_REFLECT_TYPENAME( graphene::protocol::vesting_policy_initializer )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_withdraw_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_withdraw_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/vote.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n\n#include <graphene/protocol/types.hpp>\n\nnamespace graphene { namespace protocol {\n\n/**\n * @brief An ID for some votable object\n *\n * This class stores an ID for a votable object. The ID is comprised of two fields: a type, and an instance. The\n * type field stores which kind of object is being voted on, and the instance stores which specific object of that\n * type is being referenced by this ID.\n *\n * A value of vote_id_type is implicitly convertible to an unsigned 32-bit integer containing only the instance. It\n * may also be implicitly assigned from a uint32_t, which will update the instance. It may not, however, be\n * implicitly constructed from a uint32_t, as in this case, the type would be unknown.\n *\n * On the wire, a vote_id_type is represented as a 32-bit integer with the type in the lower 8 bits and the instance\n * in the upper 24 bits. This means that types may never exceed 8 bits, and instances may never exceed 24 bits.\n *\n * In JSON, a vote_id_type is represented as a string \"type:instance\", i.e. \"1:5\" would be type 1 and instance 5.\n *\n * @note In the BitShares protocol, vote_id_type instances are unique across types; that is to say, if an object of\n * type 1 has instance 4, an object of type 0 may not also have instance 4. In other words, the type is not a\n * namespace for instances; it is only an informational field.\n */\nstruct vote_id_type\n{\n   /// Lower 8 bits are type; upper 24 bits are instance\n   uint32_t content;\n\n   friend size_t hash_value( vote_id_type v ) { return std::hash<uint32_t>()(v.content); }\n   enum vote_type\n   {\n      committee,\n      witness,\n      worker,\n      VOTE_TYPE_COUNT\n   };\n\n   /// Default constructor. Sets type and instance to 0\n   vote_id_type():content(0){}\n   /// Construct this vote_id_type with provided type and instance\n   vote_id_type(vote_type type, uint32_t instance = 0)\n      : content(instance<<8 | type)\n   {}\n   /// Construct this vote_id_type from a serial string in the form \"type:instance\"\n   explicit vote_id_type(const std::string& serial)\n   { try {\n      auto colon = serial.find(':');\n      FC_ASSERT( colon != std::string::npos );\n      *this = vote_id_type(vote_type(std::stoul(serial.substr(0, colon))), std::stoul(serial.substr(colon+1)));\n   } FC_CAPTURE_AND_RETHROW( (serial) ) }\n\n   /// Set the type of this vote_id_type\n   void set_type(vote_type type)\n   {\n      content &= 0xffffff00;\n      content |= type & 0xff;\n   }\n   /// Get the type of this vote_id_type\n   vote_type type()const\n   {\n      return vote_type(content & 0xff);\n   }\n\n   /// Set the instance of this vote_id_type\n   void set_instance(uint32_t instance)\n   {\n      assert(instance < 0x01000000);\n      content &= 0xff;\n      content |= instance << 8;\n   }\n   /// Get the instance of this vote_id_type\n   uint32_t instance()const\n   {\n      return content >> 8;\n   }\n\n   vote_id_type& operator =(vote_id_type other)\n   {\n      content = other.content;\n      return *this;\n   }\n   /// Set the instance of this vote_id_type\n   vote_id_type& operator =(uint32_t instance)\n   {\n      set_instance(instance);\n      return *this;\n   }\n   /// Get the instance of this vote_id_type\n   operator uint32_t()const\n   {\n      return instance();\n   }\n\n   /// Convert this vote_id_type to a serial string in the form \"type:instance\"\n   explicit operator std::string()const\n   {\n      return std::to_string(type()) + \":\" + std::to_string(instance());\n   }\n};\n\n} } // graphene::protocol\n\nnamespace fc\n{\n\nclass variant;\n\nvoid to_variant( const graphene::protocol::vote_id_type& var, fc::variant& vo, uint32_t max_depth = 1 );\nvoid from_variant( const fc::variant& var, graphene::protocol::vote_id_type& vo, uint32_t max_depth = 1 );\n\n} // fc\n\nFC_REFLECT_TYPENAME( fc::flat_set<graphene::protocol::vote_id_type> )\n\nFC_REFLECT_ENUM( graphene::protocol::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )\nFC_REFLECT( graphene::protocol::vote_id_type, (content) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::vote_id_type )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/withdraw_permission.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/memo.hpp>\n\nnamespace graphene { namespace protocol { \n\n   /**\n    * @brief Create a new withdrawal permission\n    * @ingroup operations\n    *\n    * This operation creates a withdrawal permission, which allows some authorized account to withdraw from an\n    * authorizing account. This operation is primarily useful for scheduling recurring payments.\n    *\n    * Withdrawal permissions define withdrawal periods, which is a span of time during which the authorized account may\n    * make a withdrawal. Any number of withdrawals may be made so long as the total amount withdrawn per period does\n    * not exceed the limit for any given period.\n    *\n    * Withdrawal permissions authorize only a specific pairing, i.e. a permission only authorizes one specified\n    * authorized account to withdraw from one specified authorizing account. Withdrawals are limited and may not exceet\n    * the withdrawal limit. The withdrawal must be made in the same asset as the limit; attempts with withdraw any\n    * other asset type will be rejected.\n    *\n    * The fee for this operation is paid by withdraw_from_account, and this account is required to authorize this\n    * operation.\n    */\n   struct withdraw_permission_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee =  GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset             fee;\n      /// The account authorizing withdrawals from its balances\n      account_id_type   withdraw_from_account;\n      /// The account authorized to make withdrawals from withdraw_from_account\n      account_id_type   authorized_account;\n      /// The maximum amount authorized_account is allowed to withdraw in a given withdrawal period\n      asset             withdrawal_limit;\n      /// Length of the withdrawal period in seconds\n      uint32_t          withdrawal_period_sec = 0;\n      /// The number of withdrawal periods this permission is valid for\n      uint32_t          periods_until_expiration = 0;\n      /// Time at which the first withdrawal period begins; must be in the future\n      time_point_sec    period_start_time;\n\n      account_id_type fee_payer()const { return withdraw_from_account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Update an existing withdraw permission\n    * @ingroup operations\n    *\n    * This oeration is used to update the settings for an existing withdrawal permission. The accounts to withdraw to\n    * and from may never be updated. The fields which may be updated are the withdrawal limit (both amount and asset\n    * type may be updated), the withdrawal period length, the remaining number of periods until expiration, and the\n    * starting time of the new period.\n    *\n    * Fee is paid by withdraw_from_account, which is required to authorize this operation\n    */\n   struct withdraw_permission_update_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee =  GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                         fee;\n      /// This account pays the fee. Must match permission_to_update->withdraw_from_account\n      account_id_type               withdraw_from_account;\n      /// The account authorized to make withdrawals. Must match permission_to_update->authorized_account\n      account_id_type               authorized_account;\n      /// ID of the permission which is being updated\n      withdraw_permission_id_type   permission_to_update;\n      /// New maximum amount the withdrawer is allowed to charge per withdrawal period\n      asset                         withdrawal_limit;\n      /// New length of the period between withdrawals\n      uint32_t                      withdrawal_period_sec = 0;\n      /// New beginning of the next withdrawal period; must be in the future\n      time_point_sec                period_start_time;\n      /// The new number of withdrawal periods for which this permission will be valid\n      uint32_t                      periods_until_expiration = 0;\n\n      account_id_type fee_payer()const { return withdraw_from_account; }\n      void            validate()const;\n   };\n\n   /**\n    * @brief Withdraw from an account which has published a withdrawal permission\n    * @ingroup operations\n    *\n    * This operation is used to withdraw from an account which has authorized such a withdrawal. It may be executed at\n    * most once per withdrawal period for the given permission. On execution, amount_to_withdraw is transferred from\n    * withdraw_from_account to withdraw_to_account, assuming amount_to_withdraw is within the withdrawal limit. The\n    * withdrawal permission will be updated to note that the withdrawal for the current period has occurred, and\n    * further withdrawals will not be permitted until the next withdrawal period, assuming the permission has not\n    * expired. This operation may be executed at any time within the current withdrawal period.\n    *\n    * Fee is paid by withdraw_to_account, which is required to authorize this operation\n    */\n   struct withdraw_permission_claim_operation : public base_operation\n   {\n      struct fee_parameters_type { \n         uint64_t fee = 20*GRAPHENE_BLOCKCHAIN_PRECISION; \n         uint32_t price_per_kbyte = 10;\n      };\n\n      /// Paid by withdraw_to_account\n      asset                       fee;\n      /// ID of the permission authorizing this withdrawal\n      withdraw_permission_id_type withdraw_permission;\n      /// Must match withdraw_permission->withdraw_from_account\n      account_id_type             withdraw_from_account;\n      /// Must match withdraw_permision->authorized_account\n      account_id_type             withdraw_to_account;\n      /// Amount to withdraw. Must not exceed withdraw_permission->withdrawal_limit\n      asset                       amount_to_withdraw;\n      /// Memo for withdraw_from_account. Should generally be encrypted with withdraw_from_account->memo_key\n      optional<memo_data>         memo;\n\n      account_id_type fee_payer()const { return withdraw_to_account; }\n      void            validate()const;\n      share_type      calculate_fee(const fee_parameters_type& k)const;\n   };\n\n   /**\n    * @brief Delete an existing withdrawal permission\n    * @ingroup operations\n    *\n    * This operation cancels a withdrawal permission, thus preventing any future withdrawals using that permission.\n    *\n    * Fee is paid by withdraw_from_account, which is required to authorize this operation\n    */\n   struct withdraw_permission_delete_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 0; };\n\n      asset                         fee;\n      /// Must match withdrawal_permission->withdraw_from_account. This account pays the fee.\n      account_id_type               withdraw_from_account;\n      /// The account previously authorized to make withdrawals. Must match withdrawal_permission->authorized_account\n      account_id_type               authorized_account;\n      /// ID of the permission to be revoked.\n      withdraw_permission_id_type   withdrawal_permission;\n\n      account_id_type fee_payer()const { return withdraw_from_account; }\n      void            validate()const;\n   };\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::withdraw_permission_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::withdraw_permission_update_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::withdraw_permission_claim_operation::fee_parameters_type, (fee)(price_per_kbyte) )\nFC_REFLECT( graphene::protocol::withdraw_permission_delete_operation::fee_parameters_type, (fee) )\n\nFC_REFLECT( graphene::protocol::withdraw_permission_create_operation, (fee)(withdraw_from_account)(authorized_account)\n            (withdrawal_limit)(withdrawal_period_sec)(periods_until_expiration)(period_start_time) )\nFC_REFLECT( graphene::protocol::withdraw_permission_update_operation, (fee)(withdraw_from_account)(authorized_account)\n            (permission_to_update)(withdrawal_limit)(withdrawal_period_sec)(period_start_time)(periods_until_expiration) )\nFC_REFLECT( graphene::protocol::withdraw_permission_claim_operation, (fee)(withdraw_permission)(withdraw_from_account)(withdraw_to_account)(amount_to_withdraw)(memo) );\nFC_REFLECT( graphene::protocol::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account)\n            (withdrawal_permission) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_claim_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_delete_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_update_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_claim_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_delete_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/witness.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol { \n\n  /**\n    * @brief Create a witness object, as a bid to hold a witness position on the network.\n    * @ingroup operations\n    *\n    * Accounts which wish to become witnesses may use this operation to create a witness object which stakeholders may\n    * vote on to approve its position as a witness.\n    */\n   struct witness_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset             fee;\n      /// The account which owns the witness. This account pays the fee for this operation.\n      account_id_type   witness_account;\n      string            url;\n      public_key_type   block_signing_key;\n\n      account_id_type fee_payer()const { return witness_account; }\n      void            validate()const;\n   };\n\n  /**\n    * @brief Update a witness object's URL and block signing key.\n    * @ingroup operations\n    */\n   struct witness_update_operation : public base_operation\n   {\n      struct fee_parameters_type\n      {\n         share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      };\n\n      asset             fee;\n      /// The witness object to update.\n      witness_id_type   witness;\n      /// The account which owns the witness. This account pays the fee for this operation.\n      account_id_type   witness_account;\n      /// The new URL.\n      optional< string > new_url;\n      /// The new block signing key.\n      optional< public_key_type > new_signing_key;\n\n      account_id_type fee_payer()const { return witness_account; }\n      void            validate()const;\n   };\n\n   /// TODO: witness_resign_operation : public base_operation\n\n} } // graphene::protocol\n\nFC_REFLECT( graphene::protocol::witness_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::witness_create_operation, (fee)(witness_account)(url)(block_signing_key) )\n\nFC_REFLECT( graphene::protocol::witness_update_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::witness_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::witness_update_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::witness_create_operation )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::witness_update_operation )\n"
  },
  {
    "path": "libraries/protocol/include/graphene/protocol/worker.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n\nnamespace graphene { namespace protocol {\n\n   /**\n    * @defgroup workers The Blockchain Worker System\n    * @ingroup operations\n    *\n    * BitShares blockchains allow the creation of special \"workers\" which are elected positions paid by the blockchain\n    * for services they provide. There may be several types of workers, and the semantics of how and when they are paid\n    * are defined by the @ref worker_type_enum enumeration. All workers are elected by core stakeholder approval, by\n    * voting for or against them.\n    *\n    * Workers are paid from the blockchain's daily budget if their total approval (votes for - votes against) is\n    * positive, ordered from most positive approval to least, until the budget is exhausted. Payments are processed at\n    * the blockchain maintenance interval. If a worker does not have positive approval during payment processing, or if\n    * the chain's budget is exhausted before the worker is paid, that worker is simply not paid at that interval.\n    * Payment is not prorated based on percentage of the interval the worker was approved. If the chain attempts to pay\n    * a worker, but the budget is insufficient to cover its entire pay, the worker is paid the remaining budget funds,\n    * even though this does not fulfill his total pay. The worker will not receive extra pay to make up the difference\n    * later. Worker pay is placed in a vesting balance and vests over the number of days specified at the worker's\n    * creation.\n    *\n    * Once created, a worker is immutable and will be kept by the blockchain forever.\n    *\n    * @{\n    */\n\n\n   struct vesting_balance_worker_initializer\n   {\n      vesting_balance_worker_initializer(uint16_t days=0):pay_vesting_period_days(days){}\n      uint16_t pay_vesting_period_days = 0;\n   };\n\n   struct burn_worker_initializer\n   {};\n\n   struct refund_worker_initializer\n   {};\n\n\n   typedef static_variant< \n      refund_worker_initializer,\n      vesting_balance_worker_initializer,\n      burn_worker_initializer > worker_initializer;\n\n\n   /**\n    * @brief Create a new worker object\n    * @ingroup operations\n    */\n   struct worker_create_operation : public base_operation\n   {\n      struct fee_parameters_type { uint64_t fee = 5000*GRAPHENE_BLOCKCHAIN_PRECISION; };\n\n      asset                fee;\n      account_id_type      owner;\n      time_point_sec       work_begin_date;\n      time_point_sec       work_end_date;\n      share_type           daily_pay;\n      string               name;\n      string               url;\n      /// This should be set to the initializer appropriate for the type of worker to be created.\n      worker_initializer   initializer;\n\n      account_id_type   fee_payer()const { return owner; }\n      void              validate()const;\n   };\n   ///@}\n\n} }\n\nFC_REFLECT( graphene::protocol::vesting_balance_worker_initializer, (pay_vesting_period_days) )\nFC_REFLECT( graphene::protocol::burn_worker_initializer, )\nFC_REFLECT( graphene::protocol::refund_worker_initializer, )\nFC_REFLECT_TYPENAME( graphene::protocol::worker_initializer )\n\nFC_REFLECT( graphene::protocol::worker_create_operation::fee_parameters_type, (fee) )\nFC_REFLECT( graphene::protocol::worker_create_operation,\n            (fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(name)(url)(initializer) )\n\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::worker_create_operation::fee_parameters_type )\nGRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::worker_create_operation )\n"
  },
  {
    "path": "libraries/protocol/liquidity_pool.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/liquidity_pool.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid liquidity_pool_create_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n   FC_ASSERT( asset_a < asset_b, \"ID of the first asset should be smaller than ID of the second asset\" );\n   FC_ASSERT( asset_a != share_asset && asset_b != share_asset,\n              \"Share asset can not be the same as one of the assets in the pool\" );\n   FC_ASSERT( taker_fee_percent <= GRAPHENE_100_PERCENT, \"Taker fee percent should not exceed 100%\" );\n   FC_ASSERT( withdrawal_fee_percent <= GRAPHENE_100_PERCENT, \"Withdrawal fee percent should not exceed 100%\" );\n}\n\nvoid liquidity_pool_delete_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n}\n\nvoid liquidity_pool_deposit_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n   FC_ASSERT( amount_a.amount > 0 && amount_b.amount > 0, \"Both amounts of the assets should be positive\" );\n   FC_ASSERT( amount_a.asset_id < amount_b.asset_id,\n              \"ID of the first asset should be smaller than ID of the second asset\" );\n}\n\nvoid liquidity_pool_withdraw_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n   FC_ASSERT( share_amount.amount > 0, \"Amount of the share asset should be positive\" );\n}\n\nvoid liquidity_pool_exchange_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n   FC_ASSERT( amount_to_sell.amount > 0, \"Amount to sell should be positive\" );\n   FC_ASSERT( min_to_receive.amount > 0, \"Minimum amount to receive should be positive\" );\n   FC_ASSERT( amount_to_sell.asset_id != min_to_receive.asset_id,\n             \"ID of the two assets should not be the same\" );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation::fee_parameters_type )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_delete_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_deposit_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_withdraw_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::liquidity_pool_exchange_operation )\n"
  },
  {
    "path": "libraries/protocol/market.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/market.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid limit_order_create_operation::validate()const\n{\n   FC_ASSERT( amount_to_sell.asset_id != min_to_receive.asset_id );\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( amount_to_sell.amount > 0 );\n   FC_ASSERT( min_to_receive.amount > 0 );\n}\n\nvoid limit_order_cancel_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n}\n\nvoid call_order_update_operation::validate()const\n{ try {\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( delta_collateral.asset_id != delta_debt.asset_id );\n   FC_ASSERT( delta_collateral.amount != 0 || delta_debt.amount != 0 );\n\n   // note: no validation is needed for extensions so far: the only attribute inside is target_collateral_ratio\n\n} FC_CAPTURE_AND_RETHROW((*this)) }\n\nvoid bid_collateral_operation::validate()const\n{ try {\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( debt_covered.amount == 0 || (debt_covered.amount > 0 && additional_collateral.amount > 0) );\n} FC_CAPTURE_AND_RETHROW((*this)) }\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::call_order_update_operation::options_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_cancel_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::call_order_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::bid_collateral_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::limit_order_cancel_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::call_order_update_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::bid_collateral_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::fill_order_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::execute_bid_operation )\n"
  },
  {
    "path": "libraries/protocol/memo.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/memo.hpp>\n#include <boost/endian/conversion.hpp>\n#include <fc/crypto/aes.hpp>\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid memo_data::set_message(const fc::ecc::private_key& priv, const fc::ecc::public_key& pub,\n                            const string& msg, uint64_t custom_nonce)\n{\n   if( priv != fc::ecc::private_key() && public_key_type(pub) != public_key_type() )\n   {\n      from = priv.get_public_key();\n      to = pub;\n      if( custom_nonce == 0 )\n      {\n         uint64_t entropy = fc::sha224::hash(fc::ecc::private_key::generate())._hash[0].value();\n         entropy <<= 32;\n         entropy                                                     &= 0xff00000000000000;\n         nonce = (fc::time_point::now().time_since_epoch().count()   &  0x00ffffffffffffff) | entropy;\n      } else\n         nonce = custom_nonce;\n      auto secret = priv.get_shared_secret(pub);\n      auto nonce_plus_secret = fc::sha512::hash(fc::to_string(nonce) + secret.str());\n      string text = memo_message(digest_type::hash(msg)._hash[0].value(), msg).serialize();\n      message = fc::aes_encrypt( nonce_plus_secret, vector<char>(text.begin(), text.end()) );\n   }\n   else\n   {\n      auto text = memo_message(0, msg).serialize();\n      message = vector<char>(text.begin(), text.end());\n   }\n}\n\nstring memo_data::get_message(const fc::ecc::private_key& priv,\n                              const fc::ecc::public_key& pub)const\n{\n   if( from != public_key_type() )\n   {\n      auto secret = priv.get_shared_secret(pub);\n      auto nonce_plus_secret = fc::sha512::hash(fc::to_string(nonce) + secret.str());\n      auto plain_text = fc::aes_decrypt( nonce_plus_secret, message );\n      auto result = memo_message::deserialize(string(plain_text.begin(), plain_text.end()));\n      FC_ASSERT( result.checksum == (uint32_t)digest_type::hash(result.text)._hash[0].value() );\n      return result.text;\n   }\n   else\n   {\n      return memo_message::deserialize(string(message.begin(), message.end())).text;\n   }\n}\n\nstring memo_message::serialize() const\n{\n   auto serial_checksum = string(sizeof(checksum), ' ');\n   (uint32_t&)(*serial_checksum.data()) = boost::endian::native_to_little(checksum);\n   return serial_checksum + text;\n}\n\nmemo_message memo_message::deserialize(const string& serial)\n{\n   memo_message result;\n   FC_ASSERT( serial.size() >= sizeof(result.checksum) );\n   result.checksum = boost::endian::little_to_native((uint32_t&)(*serial.data()));\n   result.text = serial.substr(sizeof(result.checksum));\n   return result;\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::memo_message )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::memo_data )\n"
  },
  {
    "path": "libraries/protocol/operations.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/operations.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n#include <fc/uint128.hpp>\n\nnamespace graphene { namespace protocol {\n\nuint64_t base_operation::calculate_data_fee( uint64_t bytes, uint64_t price_per_kbyte )\n{\n   auto result = (fc::uint128_t(bytes) * price_per_kbyte) / 1024;\n   FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );\n   return static_cast<uint64_t>(result);\n}\n\nfc::optional< fc::future<void> > base_operation::validate_parallel( uint32_t skip )const\n{\n   validate();\n   return fc::optional< fc::future<void> >();\n}\n\nvoid balance_claim_operation::validate()const\n{\n   FC_ASSERT( fee == asset() );\n   FC_ASSERT( balance_owner_key != public_key_type() );\n}\n\n/**\n * @brief Used to validate operations in a polymorphic manner\n */\nstruct operation_validator\n{\n   typedef void result_type;\n   template<typename T>\n   void operator()( const T& v )const { v.validate(); }\n};\n\nstruct operation_get_required_auth\n{\n   using result_type = void;\n\n   flat_set<account_id_type>& active;\n   flat_set<account_id_type>& owner;\n   vector<authority>&         other;\n   bool                       ignore_custom_op_reqd_auths;\n\n\n   operation_get_required_auth( flat_set<account_id_type>& a,\n                                flat_set<account_id_type>& own,\n                                vector<authority>& oth,\n                                bool ignore_custom_operation_required_auths )\n       : active( a ), owner( own ), other( oth ),\n         ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )\n   {}\n\n   template<typename T>\n   void operator()( const T& v ) const {\n      active.insert( v.fee_payer() );\n      v.get_required_active_authorities( active );\n      v.get_required_owner_authorities( owner );\n      v.get_required_authorities( other );\n   }\n\n   void operator()( const custom_operation& op ) const {\n      active.insert( op.fee_payer() );\n      if( !ignore_custom_op_reqd_auths ) {\n         op.get_required_active_authorities( active );\n         op.get_required_owner_authorities( owner );\n         op.get_required_authorities( other );\n      }\n   }\n};\n\nvoid operation_validate( const operation& op )\n{\n   op.visit( operation_validator() );\n}\n\nvoid operation_get_required_authorities( const operation& op,\n                                         flat_set<account_id_type>& active,\n                                         flat_set<account_id_type>& owner,\n                                         vector<authority>& other,\n                                         bool ignore_custom_operation_required_auths )\n{\n   op.visit( operation_get_required_auth( active, owner, other, ignore_custom_operation_required_auths ) );\n}\n\n} } // namespace graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::generic_operation_result )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::generic_exchange_operation_result )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::op_wrapper )\n"
  },
  {
    "path": "libraries/protocol/proposal.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/operations.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nproposal_create_operation proposal_create_operation::committee_proposal(const chain_parameters& global_params, fc::time_point_sec head_block_time )\n{\n   // TODO move this method to unit tests as it is not useful\n   proposal_create_operation op;\n   op.expiration_time = head_block_time + global_params.maximum_proposal_lifetime;\n   op.review_period_seconds = global_params.committee_proposal_review_period;\n   return op;\n}\n\nvoid proposal_create_operation::validate() const\n{\n   FC_ASSERT( !proposed_ops.empty() );\n   for( const auto& op : proposed_ops ) operation_validate( op.op );\n}\n\nshare_type proposal_create_operation::calculate_fee(const fee_parameters_type& k) const\n{\n   return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );\n}\n\nvoid proposal_update_operation::validate() const\n{\n   FC_ASSERT(fee.amount >= 0);\n   FC_ASSERT(!(active_approvals_to_add.empty() && active_approvals_to_remove.empty() &&\n               owner_approvals_to_add.empty() && owner_approvals_to_remove.empty() &&\n               key_approvals_to_add.empty() && key_approvals_to_remove.empty()));\n   for( auto a : active_approvals_to_add )\n   {\n      FC_ASSERT(active_approvals_to_remove.find(a) == active_approvals_to_remove.end(),\n                \"Cannot add and remove approval at the same time.\");\n   }\n   for( auto a : owner_approvals_to_add )\n   {\n      FC_ASSERT(owner_approvals_to_remove.find(a) == owner_approvals_to_remove.end(),\n                \"Cannot add and remove approval at the same time.\");\n   }\n   for( auto a : key_approvals_to_add )\n   {\n      FC_ASSERT(key_approvals_to_remove.find(a) == key_approvals_to_remove.end(),\n                \"Cannot add and remove approval at the same time.\");\n   }\n}\n\nvoid proposal_delete_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n}\n\nshare_type proposal_update_operation::calculate_fee(const fee_parameters_type& k) const\n{\n   return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );\n}\n\nvoid proposal_update_operation::get_required_authorities( vector<authority>& o )const\n{\n   authority auth;\n   for( const auto& k : key_approvals_to_add )\n      auth.key_auths[k] = 1;\n   for( const auto& k : key_approvals_to_remove )\n      auth.key_auths[k] = 1;\n   auth.weight_threshold = auth.key_auths.size();\n\n   if( auth.weight_threshold > 0 )\n      o.emplace_back( std::move(auth) );\n}\n\nvoid proposal_update_operation::get_required_active_authorities( flat_set<account_id_type>& a )const\n{\n   for( const auto& i : active_approvals_to_add )    a.insert(i);\n   for( const auto& i : active_approvals_to_remove ) a.insert(i);\n}\n\nvoid proposal_update_operation::get_required_owner_authorities( flat_set<account_id_type>& a )const\n{\n   for( const auto& i : owner_approvals_to_add )    a.insert(i);\n   for( const auto& i : owner_approvals_to_remove ) a.insert(i);\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_delete_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_update_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::proposal_delete_operation )\n"
  },
  {
    "path": "libraries/protocol/pts_address.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/pts_address.hpp>\n\n#include <fc/crypto/base58.hpp>\n#include <fc/crypto/elliptic.hpp>\n#include <fc/crypto/ripemd160.hpp>\n#include <fc/io/raw.hpp>\n#include <algorithm>\n\nnamespace graphene { namespace protocol {\n\n   pts_address::pts_address()\n   {\n      memset( addr.data(), 0, addr.size() );\n   }\n\n   pts_address::pts_address( const std::string& base58str )\n   {\n      std::vector<char> v = fc::from_base58( fc::string(base58str) );\n      if( v.size() )\n         memcpy( addr.data(), v.data(), std::min<size_t>( v.size(), addr.size() ) );\n\n      FC_ASSERT(is_valid(), \"invalid pts_address ${a}\", (\"a\", base58str));\n   }\n\n   pts_address::pts_address( const fc::ecc::public_key& pub, bool compressed, uint8_t version )\n   {\n       fc::sha256 sha2;\n       if( compressed )\n       {\n           auto dat = pub.serialize();\n           sha2     = fc::sha256::hash((char*) dat.data(), dat.size() );\n       }\n       else\n       {\n           auto dat = pub.serialize_ecc_point();\n           sha2     = fc::sha256::hash((char*) dat.data(), dat.size() );\n       }\n       auto rep      = fc::ripemd160::hash((char*)&sha2,sizeof(sha2));\n       addr[0]  = version;\n       memcpy( addr.data() + 1, (char*)&rep, sizeof(rep) );\n       auto check    = fc::sha256::hash( addr.data(), sizeof(rep)+1 );\n       check = fc::sha256::hash(check);\n       memcpy( addr.data() + 1 + sizeof(rep), (char*)&check, 4 );\n   }\n\n   /**\n    *  Checks the address to verify it has a\n    *  valid checksum\n    */\n   bool pts_address::is_valid()const\n   {\n       auto check    = fc::sha256::hash( addr.data(), sizeof(fc::ripemd160)+1 );\n       check = fc::sha256::hash(check);\n       return memcmp( addr.data() + 1 + sizeof(fc::ripemd160), (char*)&check, 4 ) == 0;\n   }\n\n   pts_address::operator std::string()const\n   {\n        return fc::to_base58( addr.data(), addr.size() );\n   }\n\n} } // namespace graphene\n\nnamespace fc\n{\n   void to_variant( const graphene::protocol::pts_address& var,  variant& vo, uint32_t max_depth )\n   {\n        vo = std::string(var);\n   }\n   void from_variant( const variant& var,  graphene::protocol::pts_address& vo, uint32_t max_depth )\n   {\n        vo = graphene::protocol::pts_address( var.as_string() );\n   }\n\nnamespace raw {\n   template void pack( datastream<size_t>& s, const graphene::protocol::pts_address& tx,\n                       uint32_t _max_depth=FC_PACK_MAX_DEPTH );\n   template void pack( datastream<char*>& s, const graphene::protocol::pts_address& tx,\n                       uint32_t _max_depth=FC_PACK_MAX_DEPTH );\n   template void unpack( datastream<const char*>& s, graphene::protocol::pts_address& tx,\n                         uint32_t _max_depth=FC_PACK_MAX_DEPTH );\n} } // fc::raw\n"
  },
  {
    "path": "libraries/protocol/restriction.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/restriction.hpp>\n\nnamespace graphene { namespace protocol {\n\nstruct adder {\n   size_t sum = 0;\n   void operator()(const restriction& r) { sum += r.restriction_count(); }\n   void operator()(const vector<restriction>& r) { sum += std::for_each(r.begin(), r.end(), adder()).sum; }\n};\n\nsize_t restriction::restriction_count(const vector<restriction>& restrictions) {\n   return std::for_each(restrictions.begin(), restrictions.end(), adder()).sum;\n}\n\nsize_t restriction::restriction_count() const {\n   if (argument.is_type<vector<restriction>>()) {\n      const vector<restriction>& rs = argument.get<vector<restriction>>();\n      return 1 + std::for_each(rs.begin(), rs.end(), adder()).sum;\n   } else if (argument.is_type<vector<vector<restriction>>>()) {\n      const vector<vector<restriction>>& rs = argument.get<vector<vector<restriction>>>();\n      return 1 + std::for_each(rs.begin(), rs.end(), adder()).sum;\n   } else if (argument.is_type<variant_assert_argument_type>()) {\n      const variant_assert_argument_type& arg = argument.get<variant_assert_argument_type>();\n      return 1 + std::for_each(arg.second.begin(), arg.second.end(), adder()).sum;\n   }\n   return 1;\n}\n\n} } // namespace graphene::protocol\n"
  },
  {
    "path": "libraries/protocol/small_ops.cpp",
    "content": "/*\n * Copyright (c) 2019 BitShares Blockchain Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/balance.hpp>\n#include <graphene/protocol/buyback.hpp>\n#include <graphene/protocol/exceptions.hpp>\n#include <graphene/protocol/fba.hpp>\n#include <graphene/protocol/vesting.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nFC_IMPLEMENT_EXCEPTION( protocol_exception, 4000000, \"protocol exception\" )\n\nFC_IMPLEMENT_DERIVED_EXCEPTION( transaction_exception,      protocol_exception, 4010000,\n                                \"transaction validation exception\" )\n\nFC_IMPLEMENT_DERIVED_EXCEPTION( tx_missing_active_auth,     transaction_exception, 4010001,\n                                \"missing required active authority\" )\nFC_IMPLEMENT_DERIVED_EXCEPTION( tx_missing_owner_auth,      transaction_exception, 4010002,\n                                \"missing required owner authority\" )\nFC_IMPLEMENT_DERIVED_EXCEPTION( tx_missing_other_auth,      transaction_exception, 4010003,\n                                \"missing required other authority\" )\nFC_IMPLEMENT_DERIVED_EXCEPTION( tx_irrelevant_sig,          transaction_exception, 4010004,\n                                \"irrelevant signature included\" )\nFC_IMPLEMENT_DERIVED_EXCEPTION( tx_duplicate_sig,           transaction_exception, 4010005,\n                                \"duplicate signature included\" )\nFC_IMPLEMENT_DERIVED_EXCEPTION( invalid_committee_approval, transaction_exception, 4010006,\n                                \"committee account cannot directly approve transaction\" )\nFC_IMPLEMENT_DERIVED_EXCEPTION( insufficient_fee,           transaction_exception, 4010007, \"insufficient fee\" )\n\n} } // graphene::protocol\n\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::balance_claim_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::buyback_account_options )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::fba_distribute_operation )\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_withdraw_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::vesting_balance_withdraw_operation )\n"
  },
  {
    "path": "libraries/protocol/special_authority.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/special_authority.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nstruct special_authority_validate_visitor\n{\n   typedef void result_type;\n\n   void operator()( const no_special_authority& a ) {}\n\n   void operator()( const top_holders_special_authority& a )\n   {\n      FC_ASSERT( a.num_top_holders > 0 );\n   }\n};\n\nvoid validate_special_authority( const special_authority& a )\n{\n   special_authority_validate_visitor vtor;\n   a.visit( vtor );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::top_holders_special_authority )\n"
  },
  {
    "path": "libraries/protocol/ticket.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/ticket.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid ticket_create_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n   FC_ASSERT( target_type != static_cast<uint64_t>(liquid), \"Target type can not be liquid\" );\n   FC_ASSERT( target_type < static_cast<uint64_t>(TICKET_TYPE_COUNT), \"Invalid target type\" );\n   FC_ASSERT( amount.amount > 0, \"A positive amount is needed for creating a ticket\" );\n   FC_ASSERT( amount.asset_id == asset_id_type(), \"Amount must be in BTS so far\" );\n}\n\nvoid ticket_update_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0, \"Fee should not be negative\" );\n   FC_ASSERT( target_type < static_cast<uint64_t>(TICKET_TYPE_COUNT), \"Invalid target type\" );\n   if( amount_for_new_target.valid() )\n   {\n      FC_ASSERT( amount_for_new_target->amount > 0, \"A positive amount is needed\" );\n      FC_ASSERT( amount_for_new_target->asset_id == asset_id_type(), \"Amount must be in BTS so far\" );\n   }\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::ticket_update_operation )\n"
  },
  {
    "path": "libraries/protocol/transaction.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/transaction.hpp>\n#include <graphene/protocol/block.hpp>\n#include <graphene/protocol/exceptions.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/protocol/pts_address.hpp>\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\ndigest_type processed_transaction::merkle_digest()const\n{\n   digest_type::encoder enc;\n   fc::raw::pack( enc, *this );\n   return enc.result();\n}\n\ndigest_type transaction::digest()const\n{\n   digest_type::encoder enc;\n   fc::raw::pack( enc, *this );\n   return enc.result();\n}\n\ndigest_type transaction::sig_digest( const chain_id_type& chain_id )const\n{\n   digest_type::encoder enc;\n   fc::raw::pack( enc, chain_id );\n   fc::raw::pack( enc, *this );\n   return enc.result();\n}\n\nvoid transaction::validate() const\n{\n   FC_ASSERT( operations.size() > 0, \"A transaction must have at least one operation\", (\"trx\",*this) );\n   for( const auto& op : operations )\n      operation_validate(op);\n}\n\nuint64_t transaction::get_packed_size() const\n{\n   return fc::raw::pack_size(*this);\n}\n\nconst transaction_id_type& transaction::id() const\n{\n   auto h = digest();\n   memcpy(_tx_id_buffer._hash, h._hash, std::min(sizeof(_tx_id_buffer), sizeof(h)));\n   return _tx_id_buffer;\n}\n\nconst signature_type& graphene::protocol::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id)\n{\n   digest_type h = sig_digest( chain_id );\n   signatures.push_back(key.sign_compact(h));\n   return signatures.back();\n}\n\nsignature_type graphene::protocol::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id)const\n{\n   digest_type::encoder enc;\n   fc::raw::pack( enc, chain_id );\n   fc::raw::pack( enc, *this );\n   return key.sign_compact(enc.result());\n}\n\nvoid transaction::set_expiration( fc::time_point_sec expiration_time )\n{\n    expiration = expiration_time;\n}\n\nvoid transaction::set_reference_block( const block_id_type& reference_block )\n{\n   ref_block_num = boost::endian::endian_reverse(reference_block._hash[0].value());\n   ref_block_prefix = reference_block._hash[1].value();\n}\n\nvoid transaction::get_required_authorities( flat_set<account_id_type>& active,\n                                            flat_set<account_id_type>& owner,\n                                            vector<authority>& other,\n                                            bool ignore_custom_operation_required_auths )const\n{\n   for( const auto& op : operations )\n      operation_get_required_authorities( op, active, owner, other, ignore_custom_operation_required_auths );\n   for( const auto& account : owner )\n      active.erase( account );\n}\n\n\nconst flat_set<public_key_type> empty_keyset;\n\nstruct sign_state\n{\n      /** returns true if we have a signature for this key or can\n       * produce a signature for this key, else returns false.\n       */\n      bool signed_by( const public_key_type& k )\n      {\n         auto itr = provided_signatures.find(k);\n         if( itr == provided_signatures.end() )\n         {\n            auto pk = available_keys.find(k);\n            if( pk  != available_keys.end() )\n               return provided_signatures[k] = true;\n            return false;\n         }\n         return itr->second = true;\n      }\n\n      optional<map<address,public_key_type>> available_address_sigs;\n      optional<map<address,public_key_type>> provided_address_sigs;\n\n      bool signed_by( const address& a ) {\n         if( !available_address_sigs ) {\n            available_address_sigs = std::map<address,public_key_type>();\n            provided_address_sigs = std::map<address,public_key_type>();\n            for( auto& item : available_keys ) {\n             (*available_address_sigs)[ address(pts_address(item, false, 56) ) ] = item;\n             (*available_address_sigs)[ address(pts_address(item, true, 56) ) ] = item;\n             (*available_address_sigs)[ address(pts_address(item, false, 0) ) ] = item;\n             (*available_address_sigs)[ address(pts_address(item, true, 0) ) ] = item;\n             (*available_address_sigs)[ address(item) ] = item;\n            }\n            for( auto& item : provided_signatures ) {\n             (*provided_address_sigs)[ address(pts_address(item.first, false, 56) ) ] = item.first;\n             (*provided_address_sigs)[ address(pts_address(item.first, true, 56) ) ] = item.first;\n             (*provided_address_sigs)[ address(pts_address(item.first, false, 0) ) ] = item.first;\n             (*provided_address_sigs)[ address(pts_address(item.first, true, 0) ) ] = item.first;\n             (*provided_address_sigs)[ address(item.first) ] = item.first;\n            }\n         }\n         auto itr = provided_address_sigs->find(a);\n         if( itr == provided_address_sigs->end() )\n         {\n            auto aitr = available_address_sigs->find(a);\n            if( aitr != available_address_sigs->end() ) {\n               auto pk = available_keys.find(aitr->second);\n               if( pk != available_keys.end() )\n                  return provided_signatures[aitr->second] = true;\n               return false;\n            }\n         }\n         return provided_signatures[itr->second] = true;\n      }\n\n      bool check_authority( account_id_type id )\n      {\n         if( approved_by.find(id) != approved_by.end() ) return true;\n         return check_authority( get_active(id) ) || ( allow_non_immediate_owner && check_authority( get_owner(id) ) );\n      }\n\n      /**\n       *  Checks to see if we have signatures of the active authorites of\n       *  the accounts specified in authority or the keys specified.\n       */\n      bool check_authority( const authority* au, uint32_t depth = 0 )\n      {\n         if( au == nullptr ) return false;\n         const authority& auth = *au;\n\n         uint32_t total_weight = 0;\n         for( const auto& k : auth.key_auths )\n            if( signed_by( k.first ) )\n            {\n               total_weight += k.second;\n               if( total_weight >= auth.weight_threshold )\n                  return true;\n            }\n\n         for( const auto& k : auth.address_auths )\n            if( signed_by( k.first ) )\n            {\n               total_weight += k.second;\n               if( total_weight >= auth.weight_threshold )\n                  return true;\n            }\n\n         for( const auto& a : auth.account_auths )\n         {\n            if( approved_by.find(a.first) == approved_by.end() )\n            {\n               if( depth == max_recursion )\n                  continue;\n               if( check_authority( get_active( a.first ), depth+1 )\n                     || ( allow_non_immediate_owner && check_authority( get_owner( a.first ), depth+1 ) ) )\n               {\n                  approved_by.insert( a.first );\n                  total_weight += a.second;\n                  if( total_weight >= auth.weight_threshold )\n                     return true;\n               }\n            }\n            else\n            {\n               total_weight += a.second;\n               if( total_weight >= auth.weight_threshold )\n                  return true;\n            }\n         }\n         return total_weight >= auth.weight_threshold;\n      }\n\n      bool remove_unused_signatures()\n      {\n         vector<public_key_type> remove_sigs;\n         for( const auto& sig : provided_signatures )\n            if( !sig.second ) remove_sigs.push_back( sig.first );\n\n         for( auto& sig : remove_sigs )\n            provided_signatures.erase(sig);\n\n         return remove_sigs.size() != 0;\n      }\n\n      sign_state( const flat_set<public_key_type>& sigs,\n                  const std::function<const authority*(account_id_type)>& active,\n                  const std::function<const authority*(account_id_type)>& owner,\n                  bool allow_owner,\n                  uint32_t max_recursion_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH,\n                  const flat_set<public_key_type>& keys = empty_keyset )\n      :  get_active(active),\n         get_owner(owner),\n         allow_non_immediate_owner(allow_owner),\n         max_recursion(max_recursion_depth),\n         available_keys(keys)\n      {\n         for( const auto& key : sigs )\n            provided_signatures[ key ] = false;\n         approved_by.insert( GRAPHENE_TEMP_ACCOUNT  );\n      }\n\n      const std::function<const authority*(account_id_type)>& get_active;\n      const std::function<const authority*(account_id_type)>& get_owner;\n\n      const bool                       allow_non_immediate_owner;\n      const uint32_t                   max_recursion;\n      const flat_set<public_key_type>& available_keys;\n\n      flat_map<public_key_type,bool>   provided_signatures;\n      flat_set<account_id_type>        approved_by;\n};\n\n\nvoid verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,\n                       const std::function<const authority*(account_id_type)>& get_active,\n                       const std::function<const authority*(account_id_type)>& get_owner,\n                       const custom_authority_lookup& get_custom,\n                       bool allow_non_immediate_owner,\n                       bool ignore_custom_operation_required_auths,\n                       uint32_t max_recursion_depth,\n                       bool  allow_committee,\n                       const flat_set<account_id_type>& active_aprovals,\n                       const flat_set<account_id_type>& owner_approvals )\n{\n   rejected_predicate_map rejected_custom_auths;\n   try {\n   flat_set<account_id_type> required_active;\n   flat_set<account_id_type> required_owner;\n   vector<authority> other;\n\n   sign_state s( sigs, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth );\n   for( auto& id : active_aprovals )\n      s.approved_by.insert( id );\n   for( auto& id : owner_approvals )\n      s.approved_by.insert( id );\n\n   auto approved_by_custom_authority = [&s, &rejected_custom_auths, get_custom = std::move(get_custom)](\n           account_id_type account,\n           operation op ) mutable {\n      auto viable_custom_auths = get_custom( account, op, &rejected_custom_auths );\n      for( const auto& auth : viable_custom_auths )\n         if( s.check_authority( &auth ) ) return true;\n      return false;\n   };\n\n   for( const auto& op : ops ) {\n      flat_set<account_id_type> operation_required_active;\n      operation_get_required_authorities( op, operation_required_active, required_owner, other,\n                                          ignore_custom_operation_required_auths );\n\n      auto itr = operation_required_active.begin();\n      while ( itr != operation_required_active.end() ) {\n         if ( approved_by_custom_authority( *itr, op ) )\n            itr = operation_required_active.erase( itr );\n         else\n            ++itr;\n      }\n\n      required_active.insert( operation_required_active.begin(), operation_required_active.end() );\n   }\n\n   if( !allow_committee )\n      GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),\n                       invalid_committee_approval, \"Committee account may only propose transactions\" );\n\n   for( const auto& auth : other )\n   {\n      GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, \"Missing Authority\", (\"auth\",auth)(\"sigs\",sigs) );\n   }\n\n   // fetch all of the top level authorities\n   for( auto id : required_owner )\n   {\n      GRAPHENE_ASSERT( owner_approvals.find(id) != owner_approvals.end() ||\n                       s.check_authority(get_owner(id)),\n                       tx_missing_owner_auth, \"Missing Owner Authority ${id}\", (\"id\",id)(\"auth\",*get_owner(id)) );\n   }\n\n   for( auto id : required_active )\n   {\n      GRAPHENE_ASSERT( s.check_authority(id) ||\n                       s.check_authority(get_owner(id)),\n                       tx_missing_active_auth, \"Missing Active Authority ${id}\",\n                       (\"id\",id)(\"auth\",*get_active(id))(\"owner\",*get_owner(id)) );\n   }\n\n   GRAPHENE_ASSERT(\n      !s.remove_unused_signatures(),\n      tx_irrelevant_sig,\n      \"Unnecessary signature(s) detected\"\n      );\n} FC_CAPTURE_AND_RETHROW( (rejected_custom_auths)(ops)(sigs) ) }\n\n\nconst flat_set<public_key_type>& signed_transaction::get_signature_keys( const chain_id_type& chain_id )const\n{ try {\n   auto d = sig_digest( chain_id );\n   flat_set<public_key_type> result;\n   for( const auto&  sig : signatures )\n   {\n      GRAPHENE_ASSERT(\n         result.insert( fc::ecc::public_key(sig,d) ).second,\n            tx_duplicate_sig,\n            \"Duplicate Signature detected\" );\n   }\n   _signees = std::move( result );\n   return _signees;\n} FC_CAPTURE_AND_RETHROW() }\n\n\nset<public_key_type> signed_transaction::get_required_signatures( const chain_id_type& chain_id,\n                                                                  const flat_set<public_key_type>& available_keys,\n                                                                  const std::function<const authority*(account_id_type)>& get_active,\n                                                                  const std::function<const authority*(account_id_type)>& get_owner,\n                                                                  bool allow_non_immediate_owner,\n                                                                  bool ignore_custom_operation_required_authorities,\n                                                                  uint32_t max_recursion_depth )const\n{\n   flat_set<account_id_type> required_active;\n   flat_set<account_id_type> required_owner;\n   vector<authority> other;\n   get_required_authorities( required_active, required_owner, other, ignore_custom_operation_required_authorities );\n\n   const flat_set<public_key_type>& signature_keys = get_signature_keys(chain_id);\n   sign_state s( signature_keys, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth, available_keys );\n\n   for( const auto& auth : other )\n      s.check_authority( &auth );\n   for( auto& owner : required_owner )\n      s.check_authority( get_owner( owner ) );\n   for( auto& active : required_active )\n      s.check_authority( active ) || s.check_authority( get_owner( active ) );\n\n   s.remove_unused_signatures();\n\n   set<public_key_type> result;\n\n   for( auto& provided_sig : s.provided_signatures )\n      if( available_keys.find( provided_sig.first ) != available_keys.end()\n            && signature_keys.find( provided_sig.first ) == signature_keys.end() )\n         result.insert( provided_sig.first );\n\n   return result;\n}\n\nset<public_key_type> signed_transaction::minimize_required_signatures(\n         const chain_id_type& chain_id,\n         const flat_set<public_key_type>& available_keys,\n         const std::function<const authority*(account_id_type)>& get_active,\n         const std::function<const authority*(account_id_type)>& get_owner,\n         const custom_authority_lookup &get_custom,\n         bool allow_non_immediate_owner,\n         bool ignore_custom_operation_required_auths,\n         uint32_t max_recursion )const\n{\n   set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner,\n                                                       allow_non_immediate_owner,\n                                                       ignore_custom_operation_required_auths, max_recursion );\n   flat_set< public_key_type > result( s.begin(), s.end() );\n\n   for( const public_key_type& k : s )\n   {\n      result.erase( k );\n      try\n      {\n         graphene::protocol::verify_authority( operations, result, get_active, get_owner, get_custom,\n                                               allow_non_immediate_owner,ignore_custom_operation_required_auths,\n                                               max_recursion );\n         continue;  // element stays erased if verify_authority is ok\n      }\n      catch( const tx_missing_owner_auth& e ) {}\n      catch( const tx_missing_active_auth& e ) {}\n      catch( const tx_missing_other_auth& e ) {}\n      result.insert( k );\n   }\n   return set<public_key_type>( result.begin(), result.end() );\n}\n\nconst transaction_id_type& precomputable_transaction::id()const\n{\n   if( !_tx_id_buffer._hash[0].value() )\n      transaction::id();\n   return _tx_id_buffer;\n}\n\nvoid precomputable_transaction::validate() const\n{\n   if( _validated ) return;\n   transaction::validate();\n   _validated = true;\n}\n\nuint64_t precomputable_transaction::get_packed_size()const\n{\n   if( _packed_size == 0 )\n      _packed_size = transaction::get_packed_size();\n   return _packed_size;\n}\n\nconst flat_set<public_key_type>& precomputable_transaction::get_signature_keys( const chain_id_type& chain_id )const\n{\n   // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field.\n   // However, we don't pass in another chain ID so far, for better performance, we skip the check.\n   if( _signees.empty() )\n      signed_transaction::get_signature_keys( chain_id );\n   return _signees;\n}\n\nvoid signed_transaction::verify_authority( const chain_id_type& chain_id,\n                                           const std::function<const authority*(account_id_type)>& get_active,\n                                           const std::function<const authority*(account_id_type)>& get_owner,\n                                           const custom_authority_lookup& get_custom,\n                                           bool allow_non_immediate_owner,\n                                           bool ignore_custom_operation_required_auths,\n                                           uint32_t max_recursion )const\n{ try {\n   graphene::protocol::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner,\n                                         get_custom, allow_non_immediate_owner,\n                                         ignore_custom_operation_required_auths, max_recursion );\n} FC_CAPTURE_AND_RETHROW( (*this) ) }\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transaction)\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::signed_transaction)\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::precomputable_transaction)\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::processed_transaction)\n"
  },
  {
    "path": "libraries/protocol/transfer.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/transfer.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nshare_type transfer_operation::calculate_fee( const fee_parameters_type& schedule )const\n{\n   share_type core_fee_required = schedule.fee;\n   if( memo )\n      core_fee_required += calculate_data_fee( fc::raw::pack_size(memo), schedule.price_per_kbyte );\n   return core_fee_required;\n}\n\n\nvoid transfer_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( from != to );\n   FC_ASSERT( amount.amount > 0 );\n}\n\n\n\nshare_type override_transfer_operation::calculate_fee( const fee_parameters_type& schedule )const\n{\n   share_type core_fee_required = schedule.fee;\n   if( memo )\n      core_fee_required += calculate_data_fee( fc::raw::pack_size(memo), schedule.price_per_kbyte );\n   return core_fee_required;\n}\n\n\nvoid override_transfer_operation::validate()const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( from != to );\n   FC_ASSERT( amount.amount > 0 );\n   FC_ASSERT( issuer != from );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::override_transfer_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::transfer_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::override_transfer_operation )\n"
  },
  {
    "path": "libraries/protocol/types.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/types.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <fc/crypto/base58.hpp>\n#include <fc/crypto/ripemd160.hpp>\n#include <fc/exception/exception.hpp>\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\n    public_key_type::public_key_type():key_data(){};\n\n    public_key_type::public_key_type( const fc::ecc::public_key_data& data )\n        :key_data( data ) {};\n\n    public_key_type::public_key_type( const fc::ecc::public_key& pubkey )\n        :key_data( pubkey ) {};\n\n    public_key_type::public_key_type( const std::string& base58str )\n    {\n      // TODO:  Refactor syntactic checks into static is_valid()\n      //        to make public_key_type API more similar to address API\n       std::string prefix( GRAPHENE_ADDRESS_PREFIX );\n       const size_t prefix_len = prefix.size();\n       FC_ASSERT( base58str.size() > prefix_len );\n       FC_ASSERT( base58str.substr( 0, prefix_len ) ==  prefix , \"\", (\"base58str\", base58str) );\n       auto bin = fc::from_base58( base58str.substr( prefix_len ) );\n       auto bin_key = fc::raw::unpack<binary_key>(bin);\n       key_data = bin_key.data;\n       FC_ASSERT( fc::ripemd160::hash( (char*) key_data.data(), key_data.size() )._hash[0].value() == bin_key.check );\n    };\n\n    public_key_type::operator fc::ecc::public_key_data() const\n    {\n       return key_data;\n    };\n\n    public_key_type::operator fc::ecc::public_key() const\n    {\n       return fc::ecc::public_key( key_data );\n    };\n\n    public_key_type::operator std::string() const\n    {\n       binary_key k;\n       k.data = key_data;\n       k.check = fc::ripemd160::hash( (char*) k.data.data(), k.data.size() )._hash[0].value();\n       auto data = fc::raw::pack( k );\n       return GRAPHENE_ADDRESS_PREFIX + fc::to_base58( data.data(), data.size() );\n    }\n\n    bool operator == ( const public_key_type& p1, const fc::ecc::public_key& p2)\n    {\n       return p1.key_data == p2.serialize();\n    }\n\n    bool operator == ( const public_key_type& p1, const public_key_type& p2)\n    {\n       return p1.key_data == p2.key_data;\n    }\n\n    bool operator != ( const public_key_type& p1, const public_key_type& p2)\n    {\n       return p1.key_data != p2.key_data;\n    }\n    \n} } // graphene::protocol\n\nnamespace fc\n{\n    using namespace std;\n    void to_variant( const graphene::protocol::public_key_type& var,  fc::variant& vo, uint32_t max_depth )\n    {\n        vo = std::string( var );\n    }\n\n    void from_variant( const fc::variant& var,  graphene::protocol::public_key_type& vo, uint32_t max_depth )\n    {\n        vo = graphene::protocol::public_key_type( var.as_string() );\n    }\n    \n    void from_variant( const fc::variant& var, std::shared_ptr<const graphene::protocol::fee_schedule>& vo,\n                       uint32_t max_depth ) {\n        // If it's null, just make a new one\n        if (!vo) vo = std::make_shared<const graphene::protocol::fee_schedule>();\n        // Convert the non-const shared_ptr<const fee_schedule> to a non-const fee_schedule& so we can write it\n        // Don't decrement max_depth since we're not actually deserializing at this step\n        from_variant(var, const_cast<graphene::protocol::fee_schedule&>(*vo), max_depth);\n    }\n\nnamespace raw {\n   template void pack( datastream<size_t>& s, const graphene::protocol::public_key_type& tx,\n                       uint32_t _max_depth=FC_PACK_MAX_DEPTH );\n   template void pack( datastream<char*>& s, const graphene::protocol::public_key_type& tx,\n                       uint32_t _max_depth=FC_PACK_MAX_DEPTH );\n   template void unpack( datastream<const char*>& s, graphene::protocol::public_key_type& tx,\n                         uint32_t _max_depth=FC_PACK_MAX_DEPTH );\n} } // fc::raw\n"
  },
  {
    "path": "libraries/protocol/vote.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/protocol/vote.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace fc\n{\n\nvoid to_variant( const graphene::protocol::vote_id_type& var, variant& vo, uint32_t max_depth )\n{\n   vo = string(var);\n}\n\nvoid from_variant( const variant& var, graphene::protocol::vote_id_type& vo, uint32_t max_depth )\n{\n   vo = graphene::protocol::vote_id_type(var.as_string());\n}\n\n} // fc\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::vote_id_type )\n"
  },
  {
    "path": "libraries/protocol/withdraw_permission.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/withdraw_permission.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid withdraw_permission_update_operation::validate()const\n{\n   FC_ASSERT( withdrawal_limit.amount > 0 );\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( withdrawal_period_sec > 0 );\n   FC_ASSERT( withdraw_from_account != authorized_account );\n   FC_ASSERT( periods_until_expiration > 0 );\n}\n\nvoid withdraw_permission_claim_operation::validate()const\n{\n   FC_ASSERT( withdraw_to_account != withdraw_from_account );\n   FC_ASSERT( amount_to_withdraw.amount > 0 );\n   FC_ASSERT( fee.amount >= 0 );\n}\n\nshare_type withdraw_permission_claim_operation::calculate_fee(const fee_parameters_type& k)const\n{\n   share_type core_fee_required = k.fee;\n   if( memo )\n      core_fee_required += calculate_data_fee( fc::raw::pack_size(memo), k.price_per_kbyte );\n   return core_fee_required;\n}\n\nvoid withdraw_permission_create_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( withdraw_from_account != authorized_account );\n   FC_ASSERT( withdrawal_limit.amount > 0 );\n   //TODO: better bounds checking on these values\n   FC_ASSERT( withdrawal_period_sec > 0 );\n   FC_ASSERT( periods_until_expiration > 0 );\n}\n\nvoid withdraw_permission_delete_operation::validate() const\n{\n   FC_ASSERT( fee.amount >= 0 );\n   FC_ASSERT( withdraw_from_account != authorized_account );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_claim_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_delete_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_update_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_claim_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::withdraw_permission_delete_operation )\n"
  },
  {
    "path": "libraries/protocol/witness.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/witness.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid witness_create_operation::validate() const\n{\n   FC_ASSERT(fee.amount >= 0);\n   FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH );\n}\n\nvoid witness_update_operation::validate() const\n{\n   FC_ASSERT(fee.amount >= 0);\n   if( new_url.valid() )\n       FC_ASSERT(new_url->size() < GRAPHENE_MAX_URL_LENGTH );\n}\n\n} } // graphene::protocol\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::witness_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::witness_update_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::witness_create_operation )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::witness_update_operation )\n"
  },
  {
    "path": "libraries/protocol/worker.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/worker.hpp>\n\n#include <fc/io/raw.hpp>\n\nnamespace graphene { namespace protocol {\n\nvoid worker_create_operation::validate() const\n{\n   FC_ASSERT(fee.amount >= 0);\n   FC_ASSERT(work_end_date > work_begin_date);\n   FC_ASSERT(daily_pay > 0);\n   FC_ASSERT(daily_pay < GRAPHENE_MAX_SHARE_SUPPLY);\n   FC_ASSERT(name.size() < GRAPHENE_MAX_WORKER_NAME_LENGTH );\n   FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH );\n}\n\n} }\n\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::worker_create_operation::fee_parameters_type )\nGRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::worker_create_operation )\n"
  },
  {
    "path": "libraries/utilities/CMakeLists.txt",
    "content": "list( APPEND CMAKE_MODULE_PATH \"${CMAKE_SOURCE_DIR}/libraries/fc/GitVersionGen\" )\ninclude( GetGitRevisionDescription )\nget_git_head_revision(GIT_REFSPEC GRAPHENE_GIT_REVISION_SHA)\nget_git_unix_timestamp(GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP)\ngit_describe(GRAPHENE_GIT_REVISION_DESCRIPTION --tags)\nif(NOT GRAPHENE_GIT_REVISION_DESCRIPTION)\n    set(GRAPHENE_GIT_REVISION_DESCRIPTION \"unknown\")\nendif(NOT GRAPHENE_GIT_REVISION_DESCRIPTION)\n\nfile(GLOB HEADERS \"include/graphene/utilities/*.hpp\")\n\nset(sources\n   key_conversion.cpp\n   string_escape.cpp\n   tempdir.cpp\n   words.cpp\n   elasticsearch.cpp\n   ${HEADERS})\n\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/git_revision.cpp.in\" \"${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp\" @ONLY)\nlist(APPEND sources \"${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp\")\n\nfind_curl()\n\ninclude_directories(${CURL_INCLUDE_DIRS})\nadd_library( graphene_utilities\n             ${sources}\n             ${HEADERS} )\nif(CURL_STATICLIB)\n  SET_TARGET_PROPERTIES(graphene_utilities PROPERTIES\n  COMPILE_DEFINITIONS \"CURL_STATICLIB\")\nendif(CURL_STATICLIB)\ntarget_link_libraries( graphene_utilities fc ${CURL_LIBRARIES} )\ntarget_include_directories( graphene_utilities\n                            PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\nif (USE_PCH)\n  set_target_properties(graphene_utilities PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)\n  cotire(graphene_utilities)\nendif(USE_PCH)\n\ninstall( TARGETS\n   graphene_utilities\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\ninstall( FILES ${HEADERS} DESTINATION \"include/graphene/utilities\" )\n"
  },
  {
    "path": "libraries/utilities/elasticsearch.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/utilities/elasticsearch.hpp>\n\n#include <boost/algorithm/string/join.hpp>\n#include <boost/algorithm/string.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/io/json.hpp>\n\nsize_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)\n{\n   ((std::string*)userp)->append((char*)contents, size * nmemb);\n   return size * nmemb;\n}\n\nnamespace graphene { namespace utilities {\n\nbool checkES(ES& es)\n{\n   graphene::utilities::CurlRequest curl_request;\n   curl_request.handler = es.curl;\n   curl_request.url = es.elasticsearch_url + \"_nodes\";\n   curl_request.auth = es.auth;\n   curl_request.type = \"GET\";\n\n   if(doCurl(curl_request).empty())\n      return false;\n   return true;\n\n}\nconst std::string simpleQuery(ES& es)\n{\n   graphene::utilities::CurlRequest curl_request;\n   curl_request.handler = es.curl;\n   curl_request.url = es.elasticsearch_url + es.endpoint;\n   curl_request.auth = es.auth;\n   curl_request.type = \"POST\";\n   curl_request.query = es.query;\n\n   return doCurl(curl_request);\n}\n\nbool SendBulk(ES&& es)\n{\n   std::string bulking = joinBulkLines(es.bulk_lines);\n\n   graphene::utilities::CurlRequest curl_request;\n   curl_request.handler = es.curl;\n   curl_request.url = es.elasticsearch_url + \"_bulk\";\n   curl_request.auth = es.auth;\n   curl_request.type = \"POST\";\n   curl_request.query = std::move(bulking);\n\n   auto curlResponse = doCurl(curl_request);\n\n   if(handleBulkResponse(getResponseCode(curl_request.handler), curlResponse))\n      return true;\n   return false;\n}\n\nconst std::string joinBulkLines(const std::vector<std::string>& bulk)\n{\n   auto bulking = boost::algorithm::join(bulk, \"\\n\");\n   bulking = bulking + \"\\n\";\n\n   return bulking;\n}\nlong getResponseCode(CURL *handler)\n{\n   long http_code = 0;\n   curl_easy_getinfo (handler, CURLINFO_RESPONSE_CODE, &http_code);\n   return http_code;\n}\n\nbool handleBulkResponse(long http_code, const std::string& CurlReadBuffer)\n{\n   if(http_code == 200) {\n      // all good, but check errors in response\n      fc::variant j = fc::json::from_string(CurlReadBuffer);\n      bool errors = j[\"errors\"].as_bool();\n      if(errors == true) {\n         return false;\n      }\n   }\n   else {\n      if(http_code == 413) {\n         elog( \"413 error: Can be low disk space\" );\n      }\n      else if(http_code == 401) {\n         elog( \"401 error: Unauthorized\" );\n      }\n      else {\n         elog( std::to_string(http_code) + \" error: Unknown error\" );\n      }\n      return false;\n   }\n   return true;\n}\n\nconst std::vector<std::string> createBulk(const fc::mutable_variant_object& bulk_header, std::string&& data)\n{\n   std::vector<std::string> bulk;\n   fc::mutable_variant_object final_bulk_header;\n   final_bulk_header[\"index\"] = bulk_header;\n   bulk.push_back(fc::json::to_string(final_bulk_header));\n   bulk.push_back(data);\n\n   return bulk;\n}\n\nbool deleteAll(ES& es)\n{\n   graphene::utilities::CurlRequest curl_request;\n   curl_request.handler = es.curl;\n   curl_request.url = es.elasticsearch_url + es.index_prefix + \"*\";\n   curl_request.auth = es.auth;\n   curl_request.type = \"DELETE\";\n\n   auto curl_response = doCurl(curl_request);\n   if(curl_response.empty())\n      return false;\n   else\n      return true;\n}\nconst std::string getEndPoint(ES& es)\n{\n   graphene::utilities::CurlRequest curl_request;\n   curl_request.handler = es.curl;\n   curl_request.url = es.elasticsearch_url + es.endpoint;\n   curl_request.auth = es.auth;\n   curl_request.type = \"GET\";\n\n   return doCurl(curl_request);\n}\n\nconst std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix)\n{\n   auto block_date_string = block_date.to_iso_string();\n   std::vector<std::string> parts;\n   boost::split(parts, block_date_string, boost::is_any_of(\"-\"));\n   std::string index_name = _elasticsearch_index_prefix + parts[0] + \"-\" + parts[1];\n   return index_name;\n}\n\nconst std::string doCurl(CurlRequest& curl)\n{\n   std::string CurlReadBuffer;\n   struct curl_slist *headers = NULL;\n   headers = curl_slist_append(headers, \"Content-Type: application/json\");\n\n   curl_easy_setopt(curl.handler, CURLOPT_HTTPHEADER, headers);\n   curl_easy_setopt(curl.handler, CURLOPT_URL, curl.url.c_str());\n   curl_easy_setopt(curl.handler, CURLOPT_CUSTOMREQUEST, curl.type.c_str());\n   if(curl.type == \"POST\")\n   {\n      curl_easy_setopt(curl.handler, CURLOPT_POST, true);\n      curl_easy_setopt(curl.handler, CURLOPT_POSTFIELDS, curl.query.c_str());\n   }\n   curl_easy_setopt(curl.handler, CURLOPT_WRITEFUNCTION, WriteCallback);\n   curl_easy_setopt(curl.handler, CURLOPT_WRITEDATA, (void *)&CurlReadBuffer);\n   curl_easy_setopt(curl.handler, CURLOPT_USERAGENT, \"libcrp/0.1\");\n   if(!curl.auth.empty())\n      curl_easy_setopt(curl.handler, CURLOPT_USERPWD, curl.auth.c_str());\n   curl_easy_perform(curl.handler);\n\n   return CurlReadBuffer;\n}\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/git_revision.cpp.in",
    "content": "#include <stdint.h>\n#include <graphene/utilities/git_revision.hpp>\n\n#define GRAPHENE_GIT_REVISION_SHA \"@GRAPHENE_GIT_REVISION_SHA@\"\n#define GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP @GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP@\n#define GRAPHENE_GIT_REVISION_DESCRIPTION \"@GRAPHENE_GIT_REVISION_DESCRIPTION@\"\n\nnamespace graphene { namespace utilities {\n\nconst char* const git_revision_sha = GRAPHENE_GIT_REVISION_SHA;\nconst uint32_t git_revision_unix_timestamp = GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP;\nconst char* const git_revision_description = GRAPHENE_GIT_REVISION_DESCRIPTION;\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/elasticsearch.hpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include <curl/curl.h>\n#include <fc/time.hpp>\n#include <fc/variant_object.hpp>\n\nsize_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);\n\nnamespace graphene { namespace utilities {\n\n   class ES {\n      public:\n         CURL *curl;\n         std::vector <std::string> bulk_lines;\n         std::string elasticsearch_url;\n         std::string index_prefix;\n         std::string auth;\n         std::string endpoint;\n         std::string query;\n   };\n   class CurlRequest {\n      public:\n         CURL *handler;\n         std::string url;\n         std::string type;\n         std::string auth;\n         std::string query;\n   };\n\n   bool SendBulk(ES&& es);\n   const std::vector<std::string> createBulk(const fc::mutable_variant_object& bulk_header, std::string&& data);\n   bool checkES(ES& es);\n   const std::string simpleQuery(ES& es);\n   bool deleteAll(ES& es);\n   bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer);\n   const std::string getEndPoint(ES& es);\n   const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix);\n   const std::string doCurl(CurlRequest& curl);\n   const std::string joinBulkLines(const std::vector<std::string>& bulk);\n   long getResponseCode(CURL *handler);\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/git_revision.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n#include <stdint.h>\n\nnamespace graphene { namespace utilities {\n\nextern const char* const git_revision_sha;\nextern const uint32_t git_revision_unix_timestamp;\nextern const char* const git_revision_description;\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/key_conversion.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <string>\n#include <fc/crypto/elliptic.hpp>\n#include <fc/optional.hpp>\n\nnamespace graphene { namespace utilities {\n\nstd::string                        key_to_wif(const fc::sha256& private_secret );\nstd::string                        key_to_wif(const fc::ecc::private_key& key);\nfc::optional<fc::ecc::private_key> wif_to_key( const std::string& wif_key );\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/padding_ostream.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\nnamespace graphene { namespace utilities {\n\ntemplate<size_t BlockSize=16, char PaddingChar=' '>\nclass padding_ostream : public fc::buffered_ostream {\npublic:\n   padding_ostream( fc::ostream_ptr o, size_t bufsize = 4096 ) : buffered_ostream(o, bufsize) {}\n   virtual ~padding_ostream() {}\n\n   virtual size_t writesome( const char* buffer, size_t len ) {\n      auto out = buffered_ostream::writesome(buffer, len);\n      bytes_out += out;\n      bytes_out %= BlockSize;\n      return out;\n   }\n   virtual size_t writesome( const std::shared_ptr<const char>& buf, size_t len, size_t offset ) {\n      auto out = buffered_ostream::writesome(buf, len, offset);\n      bytes_out += out;\n      bytes_out %= BlockSize;\n      return out;\n   }\n   virtual void flush() {\n      static const char pad = PaddingChar;\n      while( bytes_out % BlockSize )\n         writesome(&pad, 1);\n      buffered_ostream::flush();\n   }\n\nprivate:\n   size_t bytes_out = 0;\n};\n\n} } //graphene::utilities\n\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/string_escape.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <string>\n\nnamespace graphene { namespace utilities {\n\n  std::string escape_string_for_c_source_code(const std::string& input);\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/tempdir.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <cstdlib>\n\n#include <fc/filesystem.hpp>\n\nnamespace graphene { namespace utilities {\n\nfc::path temp_directory_path();\n\n} } // graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/include/graphene/utilities/words.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\nnamespace graphene { namespace words {\n\ntypedef const char* const_char_ptr;\nextern const const_char_ptr word_list[];\nextern const uint32_t word_list_size;\n\n} }\n"
  },
  {
    "path": "libraries/utilities/key_conversion.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/utilities/key_conversion.hpp>\n#include <fc/crypto/base58.hpp>\n#include <fc/variant.hpp>\n\nnamespace graphene { namespace utilities {\n\nstd::string key_to_wif(const fc::sha256& secret )\n{\n  const size_t size_of_data_to_hash = sizeof(secret) + 1;\n  const size_t size_of_hash_bytes = 4;\n  char data[size_of_data_to_hash + size_of_hash_bytes];\n  data[0] = (char)0x80;\n  memcpy(&data[1], (char*)&secret, sizeof(secret));\n  fc::sha256 digest = fc::sha256::hash(data, size_of_data_to_hash);\n  digest = fc::sha256::hash(digest);\n  memcpy(data + size_of_data_to_hash, (char*)&digest, size_of_hash_bytes);\n  return fc::to_base58(data, sizeof(data));\n}\nstd::string key_to_wif(const fc::ecc::private_key& key)\n{\n  return key_to_wif( key.get_secret() );\n}\n\nfc::optional<fc::ecc::private_key> wif_to_key( const std::string& wif_key )\n{\n  std::vector<char> wif_bytes;\n  try\n  {\n    wif_bytes = fc::from_base58(wif_key);\n  }\n  catch (const fc::parse_error_exception&)\n  {\n    return fc::optional<fc::ecc::private_key>();\n  }\n  if (wif_bytes.size() < 5)\n    return fc::optional<fc::ecc::private_key>();\n  std::vector<char> key_bytes(wif_bytes.begin() + 1, wif_bytes.end() - 4);\n  fc::ecc::private_key key = fc::variant( key_bytes, 1 ).as<fc::ecc::private_key>( 1 );\n  fc::sha256 check = fc::sha256::hash(wif_bytes.data(), wif_bytes.size() - 4);\n  fc::sha256 check2 = fc::sha256::hash(check);\n    \n  if( memcmp( (char*)&check, wif_bytes.data() + wif_bytes.size() - 4, 4 ) == 0 || \n      memcmp( (char*)&check2, wif_bytes.data() + wif_bytes.size() - 4, 4 ) == 0 )\n    return key;\n\n  return fc::optional<fc::ecc::private_key>();\n}\n\n} } // end namespace graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/string_escape.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/utilities/string_escape.hpp>\n#include <sstream>\n\nnamespace graphene { namespace utilities {\n\n  std::string escape_string_for_c_source_code(const std::string& input)\n  {\n    std::ostringstream escaped_string;\n    escaped_string << \"\\\"\";\n    for (unsigned i = 0; i < input.size(); ++i)\n    {\n      switch (input[i])\n      {\n      case '\\a': \n        escaped_string << \"\\\\a\";\n        break;\n      case '\\b': \n        escaped_string << \"\\\\b\";\n        break;\n      case '\\t': \n        escaped_string << \"\\\\t\";\n        break;\n      case '\\n': \n        escaped_string << \"\\\\n\";\n        break;\n      case '\\v': \n        escaped_string << \"\\\\v\";\n        break;\n      case '\\f': \n        escaped_string << \"\\\\f\";\n        break;\n      case '\\r': \n        escaped_string << \"\\\\r\";\n        break;\n      case '\\\\': \n        escaped_string << \"\\\\\\\\\";\n        break;\n      case '\\\"': \n        escaped_string << \"\\\\\\\"\";\n        break;\n      default:\n        escaped_string << input[i];\n      }\n    }\n    escaped_string << \"\\\"\";\n    return escaped_string.str();\n  }\n\n} } // end namespace graphene::utilities\n\n"
  },
  {
    "path": "libraries/utilities/tempdir.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <cstdlib>\n\nnamespace graphene { namespace utilities {\n\nfc::path temp_directory_path()\n{\n   const char* graphene_tempdir = getenv(\"GRAPHENE_TEMPDIR\");\n   if( graphene_tempdir != nullptr )\n      return fc::path( graphene_tempdir );\n   return fc::temp_directory_path() / \"graphene-tmp\";\n}\n\n} } // graphene::utilities\n"
  },
  {
    "path": "libraries/utilities/words.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <stdint.h>\n#include <graphene/utilities/words.hpp>\n\nnamespace graphene { namespace words {\n\nconst const_char_ptr word_list[] = { \n\"a\",\n\"aa\",\n\"aal\",\n\"aalii\",\n\"aam\",\n\"aba\",\n\"abac\",\n\"abaca\",\n\"abacate\",\n\"abacay\",\n\"abacist\",\n\"aback\",\n\"abactor\",\n\"abacus\",\n\"abaff\",\n\"abaft\",\n\"abaiser\",\n\"abalone\",\n\"abandon\",\n\"abas\",\n\"abase\",\n\"abased\",\n\"abaser\",\n\"abash\",\n\"abashed\",\n\"abasia\",\n\"abasic\",\n\"abask\",\n\"abate\",\n\"abater\",\n\"abatis\",\n\"abaton\",\n\"abator\",\n\"abature\",\n\"abave\",\n\"abaxial\",\n\"abaxile\",\n\"abaze\",\n\"abb\",\n\"abbacy\",\n\"abbas\",\n\"abbasi\",\n\"abbassi\",\n\"abbess\",\n\"abbey\",\n\"abbot\",\n\"abbotcy\",\n\"abdal\",\n\"abdat\",\n\"abdest\",\n\"abdomen\",\n\"abduce\",\n\"abduct\",\n\"abeam\",\n\"abear\",\n\"abed\",\n\"abeigh\",\n\"abele\",\n\"abelite\",\n\"abet\",\n\"abettal\",\n\"abettor\",\n\"abey\",\n\"abeyant\",\n\"abfarad\",\n\"abhenry\",\n\"abhor\",\n\"abidal\",\n\"abide\",\n\"abider\",\n\"abidi\",\n\"abiding\",\n\"abietic\",\n\"abietin\",\n\"abigail\",\n\"abigeat\",\n\"abigeus\",\n\"abilao\",\n\"ability\",\n\"abilla\",\n\"abilo\",\n\"abiosis\",\n\"abiotic\",\n\"abir\",\n\"abiston\",\n\"abiuret\",\n\"abject\",\n\"abjoint\",\n\"abjudge\",\n\"abjure\",\n\"abjurer\",\n\"abkar\",\n\"abkari\",\n\"ablach\",\n\"ablare\",\n\"ablate\",\n\"ablator\",\n\"ablaut\",\n\"ablaze\",\n\"able\",\n\"ableeze\",\n\"abler\",\n\"ablest\",\n\"ablins\",\n\"abloom\",\n\"ablow\",\n\"ablude\",\n\"abluent\",\n\"ablush\",\n\"ably\",\n\"abmho\",\n\"abnet\",\n\"aboard\",\n\"abode\",\n\"abody\",\n\"abohm\",\n\"aboil\",\n\"abolish\",\n\"abolla\",\n\"aboma\",\n\"abomine\",\n\"aboon\",\n\"aborad\",\n\"aboral\",\n\"abord\",\n\"abort\",\n\"aborted\",\n\"abortin\",\n\"abortus\",\n\"abound\",\n\"about\",\n\"abouts\",\n\"above\",\n\"abox\",\n\"abrade\",\n\"abrader\",\n\"abraid\",\n\"abrasax\",\n\"abrase\",\n\"abrash\",\n\"abraum\",\n\"abraxas\",\n\"abreact\",\n\"abreast\",\n\"abret\",\n\"abrico\",\n\"abridge\",\n\"abrim\",\n\"abrin\",\n\"abroach\",\n\"abroad\",\n\"abrook\",\n\"abrupt\",\n\"abscess\",\n\"abscind\",\n\"abscise\",\n\"absciss\",\n\"abscond\",\n\"absence\",\n\"absent\",\n\"absit\",\n\"absmho\",\n\"absohm\",\n\"absolve\",\n\"absorb\",\n\"absorpt\",\n\"abstain\",\n\"absume\",\n\"absurd\",\n\"absvolt\",\n\"abthain\",\n\"abu\",\n\"abucco\",\n\"abulia\",\n\"abulic\",\n\"abuna\",\n\"abura\",\n\"aburban\",\n\"aburst\",\n\"aburton\",\n\"abuse\",\n\"abusee\",\n\"abuser\",\n\"abusion\",\n\"abusive\",\n\"abut\",\n\"abuttal\",\n\"abutter\",\n\"abuzz\",\n\"abvolt\",\n\"abwab\",\n\"aby\",\n\"abysm\",\n\"abysmal\",\n\"abyss\",\n\"abyssal\",\n\"acaciin\",\n\"acacin\",\n\"academe\",\n\"academy\",\n\"acajou\",\n\"acaleph\",\n\"acana\",\n\"acanth\",\n\"acantha\",\n\"acapnia\",\n\"acapu\",\n\"acara\",\n\"acardia\",\n\"acari\",\n\"acarian\",\n\"acarid\",\n\"acarine\",\n\"acaroid\",\n\"acarol\",\n\"acate\",\n\"acatery\",\n\"acaudal\",\n\"acca\",\n\"accede\",\n\"acceder\",\n\"accend\",\n\"accent\",\n\"accept\",\n\"accerse\",\n\"access\",\n\"accidia\",\n\"accidie\",\n\"accinge\",\n\"accite\",\n\"acclaim\",\n\"accloy\",\n\"accoast\",\n\"accoil\",\n\"accolle\",\n\"accompt\",\n\"accord\",\n\"accost\",\n\"account\",\n\"accoy\",\n\"accrete\",\n\"accrual\",\n\"accrue\",\n\"accruer\",\n\"accurse\",\n\"accusal\",\n\"accuse\",\n\"accused\",\n\"accuser\",\n\"ace\",\n\"acedia\",\n\"acedy\",\n\"acephal\",\n\"acerate\",\n\"acerb\",\n\"acerbic\",\n\"acerdol\",\n\"acerin\",\n\"acerose\",\n\"acerous\",\n\"acerra\",\n\"aceship\",\n\"acetal\",\n\"acetate\",\n\"acetic\",\n\"acetify\",\n\"acetin\",\n\"acetize\",\n\"acetoin\",\n\"acetol\",\n\"acetone\",\n\"acetose\",\n\"acetous\",\n\"acetum\",\n\"acetyl\",\n\"ach\",\n\"achage\",\n\"achar\",\n\"achate\",\n\"ache\",\n\"achene\",\n\"acher\",\n\"achete\",\n\"achieve\",\n\"achigan\",\n\"achill\",\n\"achime\",\n\"aching\",\n\"achira\",\n\"acholia\",\n\"acholic\",\n\"achor\",\n\"achree\",\n\"achroma\",\n\"achtel\",\n\"achy\",\n\"achylia\",\n\"achymia\",\n\"acicula\",\n\"acid\",\n\"acider\",\n\"acidic\",\n\"acidify\",\n\"acidite\",\n\"acidity\",\n\"acidize\",\n\"acidly\",\n\"acidoid\",\n\"acidyl\",\n\"acier\",\n\"aciform\",\n\"acinar\",\n\"acinary\",\n\"acinic\",\n\"acinose\",\n\"acinous\",\n\"acinus\",\n\"aciurgy\",\n\"acker\",\n\"ackey\",\n\"ackman\",\n\"acknow\",\n\"acle\",\n\"aclinal\",\n\"aclinic\",\n\"acloud\",\n\"aclys\",\n\"acmatic\",\n\"acme\",\n\"acmic\",\n\"acmite\",\n\"acne\",\n\"acnemia\",\n\"acnodal\",\n\"acnode\",\n\"acock\",\n\"acocotl\",\n\"acoin\",\n\"acoine\",\n\"acold\",\n\"acology\",\n\"acolous\",\n\"acolyte\",\n\"acoma\",\n\"acomia\",\n\"acomous\",\n\"acone\",\n\"aconic\",\n\"aconin\",\n\"aconine\",\n\"aconite\",\n\"acopic\",\n\"acopon\",\n\"acor\",\n\"acorea\",\n\"acoria\",\n\"acorn\",\n\"acorned\",\n\"acosmic\",\n\"acouasm\",\n\"acouchi\",\n\"acouchy\",\n\"acoupa\",\n\"acquest\",\n\"acquire\",\n\"acquist\",\n\"acquit\",\n\"acracy\",\n\"acraein\",\n\"acrasia\",\n\"acratia\",\n\"acrawl\",\n\"acraze\",\n\"acre\",\n\"acreage\",\n\"acreak\",\n\"acream\",\n\"acred\",\n\"acreman\",\n\"acrid\",\n\"acridan\",\n\"acridic\",\n\"acridly\",\n\"acridyl\",\n\"acrinyl\",\n\"acrisia\",\n\"acritan\",\n\"acrite\",\n\"acritol\",\n\"acroama\",\n\"acrobat\",\n\"acrogen\",\n\"acron\",\n\"acronyc\",\n\"acronym\",\n\"acronyx\",\n\"acrook\",\n\"acrose\",\n\"across\",\n\"acrotic\",\n\"acryl\",\n\"acrylic\",\n\"acrylyl\",\n\"act\",\n\"acta\",\n\"actable\",\n\"actify\",\n\"actin\",\n\"actinal\",\n\"actine\",\n\"acting\",\n\"actinic\",\n\"actinon\",\n\"action\",\n\"active\",\n\"activin\",\n\"actless\",\n\"acton\",\n\"actor\",\n\"actress\",\n\"actu\",\n\"actual\",\n\"actuary\",\n\"acture\",\n\"acuate\",\n\"acuity\",\n\"aculea\",\n\"aculeus\",\n\"acumen\",\n\"acushla\",\n\"acutate\",\n\"acute\",\n\"acutely\",\n\"acutish\",\n\"acyclic\",\n\"acyesis\",\n\"acyetic\",\n\"acyl\",\n\"acylate\",\n\"acyloin\",\n\"acyloxy\",\n\"acystia\",\n\"ad\",\n\"adactyl\",\n\"adad\",\n\"adage\",\n\"adagial\",\n\"adagio\",\n\"adamant\",\n\"adamas\",\n\"adamine\",\n\"adamite\",\n\"adance\",\n\"adangle\",\n\"adapid\",\n\"adapt\",\n\"adapter\",\n\"adaptor\",\n\"adarme\",\n\"adat\",\n\"adati\",\n\"adatom\",\n\"adaunt\",\n\"adaw\",\n\"adawe\",\n\"adawlut\",\n\"adawn\",\n\"adaxial\",\n\"aday\",\n\"adays\",\n\"adazzle\",\n\"adcraft\",\n\"add\",\n\"adda\",\n\"addable\",\n\"addax\",\n\"added\",\n\"addedly\",\n\"addend\",\n\"addenda\",\n\"adder\",\n\"addible\",\n\"addict\",\n\"addle\",\n\"addlins\",\n\"address\",\n\"addrest\",\n\"adduce\",\n\"adducer\",\n\"adduct\",\n\"ade\",\n\"adead\",\n\"adeem\",\n\"adeep\",\n\"adeling\",\n\"adelite\",\n\"adenase\",\n\"adenia\",\n\"adenine\",\n\"adenoid\",\n\"adenoma\",\n\"adenose\",\n\"adenyl\",\n\"adept\",\n\"adermia\",\n\"adermin\",\n\"adet\",\n\"adevism\",\n\"adfix\",\n\"adhaka\",\n\"adharma\",\n\"adhere\",\n\"adherer\",\n\"adhibit\",\n\"adiate\",\n\"adicity\",\n\"adieu\",\n\"adieux\",\n\"adinole\",\n\"adion\",\n\"adipate\",\n\"adipic\",\n\"adipoid\",\n\"adipoma\",\n\"adipose\",\n\"adipous\",\n\"adipsia\",\n\"adipsic\",\n\"adipsy\",\n\"adipyl\",\n\"adit\",\n\"adital\",\n\"aditus\",\n\"adjag\",\n\"adject\",\n\"adjiger\",\n\"adjoin\",\n\"adjoint\",\n\"adjourn\",\n\"adjudge\",\n\"adjunct\",\n\"adjure\",\n\"adjurer\",\n\"adjust\",\n\"adlay\",\n\"adless\",\n\"adlet\",\n\"adman\",\n\"admi\",\n\"admiral\",\n\"admire\",\n\"admired\",\n\"admirer\",\n\"admit\",\n\"admix\",\n\"adnate\",\n\"adnex\",\n\"adnexal\",\n\"adnexed\",\n\"adnoun\",\n\"ado\",\n\"adobe\",\n\"adonin\",\n\"adonite\",\n\"adonize\",\n\"adopt\",\n\"adopted\",\n\"adoptee\",\n\"adopter\",\n\"adoral\",\n\"adorant\",\n\"adore\",\n\"adorer\",\n\"adorn\",\n\"adorner\",\n\"adossed\",\n\"adoulie\",\n\"adown\",\n\"adoxy\",\n\"adoze\",\n\"adpao\",\n\"adpress\",\n\"adread\",\n\"adream\",\n\"adreamt\",\n\"adrenal\",\n\"adrenin\",\n\"adrift\",\n\"adrip\",\n\"adroit\",\n\"adroop\",\n\"adrop\",\n\"adrowse\",\n\"adrue\",\n\"adry\",\n\"adsbud\",\n\"adsmith\",\n\"adsorb\",\n\"adtevac\",\n\"adular\",\n\"adulate\",\n\"adult\",\n\"adulter\",\n\"adunc\",\n\"adusk\",\n\"adust\",\n\"advance\",\n\"advene\",\n\"adverb\",\n\"adverse\",\n\"advert\",\n\"advice\",\n\"advisal\",\n\"advise\",\n\"advised\",\n\"advisee\",\n\"adviser\",\n\"advisor\",\n\"advowee\",\n\"ady\",\n\"adynamy\",\n\"adyta\",\n\"adyton\",\n\"adytum\",\n\"adz\",\n\"adze\",\n\"adzer\",\n\"adzooks\",\n\"ae\",\n\"aecial\",\n\"aecium\",\n\"aedile\",\n\"aedilic\",\n\"aefald\",\n\"aefaldy\",\n\"aefauld\",\n\"aegis\",\n\"aenach\",\n\"aenean\",\n\"aeneous\",\n\"aeolid\",\n\"aeolina\",\n\"aeoline\",\n\"aeon\",\n\"aeonial\",\n\"aeonian\",\n\"aeonist\",\n\"aer\",\n\"aerage\",\n\"aerate\",\n\"aerator\",\n\"aerial\",\n\"aeric\",\n\"aerical\",\n\"aerie\",\n\"aeried\",\n\"aerify\",\n\"aero\",\n\"aerobe\",\n\"aerobic\",\n\"aerobus\",\n\"aerogel\",\n\"aerogen\",\n\"aerogun\",\n\"aeronat\",\n\"aeronef\",\n\"aerose\",\n\"aerosol\",\n\"aerugo\",\n\"aery\",\n\"aes\",\n\"aevia\",\n\"aface\",\n\"afaint\",\n\"afar\",\n\"afara\",\n\"afear\",\n\"afeard\",\n\"afeared\",\n\"afernan\",\n\"afetal\",\n\"affa\",\n\"affable\",\n\"affably\",\n\"affair\",\n\"affaite\",\n\"affect\",\n\"affeer\",\n\"affeir\",\n\"affiant\",\n\"affinal\",\n\"affine\",\n\"affined\",\n\"affirm\",\n\"affix\",\n\"affixal\",\n\"affixer\",\n\"afflict\",\n\"afflux\",\n\"afforce\",\n\"afford\",\n\"affray\",\n\"affront\",\n\"affuse\",\n\"affy\",\n\"afghani\",\n\"afield\",\n\"afire\",\n\"aflame\",\n\"aflare\",\n\"aflat\",\n\"aflaunt\",\n\"aflight\",\n\"afloat\",\n\"aflow\",\n\"aflower\",\n\"aflush\",\n\"afoam\",\n\"afoot\",\n\"afore\",\n\"afoul\",\n\"afraid\",\n\"afreet\",\n\"afresh\",\n\"afret\",\n\"afront\",\n\"afrown\",\n\"aft\",\n\"aftaba\",\n\"after\",\n\"aftergo\",\n\"aftmost\",\n\"aftosa\",\n\"aftward\",\n\"aga\",\n\"again\",\n\"against\",\n\"agal\",\n\"agalaxy\",\n\"agalite\",\n\"agallop\",\n\"agalma\",\n\"agama\",\n\"agamete\",\n\"agami\",\n\"agamian\",\n\"agamic\",\n\"agamid\",\n\"agamoid\",\n\"agamont\",\n\"agamous\",\n\"agamy\",\n\"agape\",\n\"agapeti\",\n\"agar\",\n\"agaric\",\n\"agarita\",\n\"agarwal\",\n\"agasp\",\n\"agate\",\n\"agathin\",\n\"agatine\",\n\"agatize\",\n\"agatoid\",\n\"agaty\",\n\"agavose\",\n\"agaze\",\n\"agazed\",\n\"age\",\n\"aged\",\n\"agedly\",\n\"agee\",\n\"ageless\",\n\"agelong\",\n\"agen\",\n\"agency\",\n\"agenda\",\n\"agendum\",\n\"agent\",\n\"agentry\",\n\"ager\",\n\"ageusia\",\n\"ageusic\",\n\"agger\",\n\"aggrade\",\n\"aggrate\",\n\"aggress\",\n\"aggroup\",\n\"aggry\",\n\"aggur\",\n\"agha\",\n\"aghanee\",\n\"aghast\",\n\"agile\",\n\"agilely\",\n\"agility\",\n\"aging\",\n\"agio\",\n\"agist\",\n\"agistor\",\n\"agitant\",\n\"agitate\",\n\"agla\",\n\"aglance\",\n\"aglare\",\n\"agleaf\",\n\"agleam\",\n\"aglet\",\n\"agley\",\n\"aglint\",\n\"aglow\",\n\"aglucon\",\n\"agnail\",\n\"agname\",\n\"agnamed\",\n\"agnate\",\n\"agnatic\",\n\"agnel\",\n\"agnize\",\n\"agnomen\",\n\"agnosia\",\n\"agnosis\",\n\"agnosy\",\n\"agnus\",\n\"ago\",\n\"agog\",\n\"agoge\",\n\"agogic\",\n\"agogics\",\n\"agoho\",\n\"agoing\",\n\"agon\",\n\"agonal\",\n\"agone\",\n\"agonic\",\n\"agonied\",\n\"agonist\",\n\"agonium\",\n\"agonize\",\n\"agony\",\n\"agora\",\n\"agouara\",\n\"agouta\",\n\"agouti\",\n\"agpaite\",\n\"agrah\",\n\"agral\",\n\"agre\",\n\"agree\",\n\"agreed\",\n\"agreer\",\n\"agrege\",\n\"agria\",\n\"agrin\",\n\"agrise\",\n\"agrito\",\n\"agroan\",\n\"agrom\",\n\"agroof\",\n\"agrope\",\n\"aground\",\n\"agrufe\",\n\"agruif\",\n\"agsam\",\n\"agua\",\n\"ague\",\n\"aguey\",\n\"aguish\",\n\"agunah\",\n\"agush\",\n\"agust\",\n\"agy\",\n\"agynary\",\n\"agynous\",\n\"agyrate\",\n\"agyria\",\n\"ah\",\n\"aha\",\n\"ahaaina\",\n\"ahaunch\",\n\"ahead\",\n\"aheap\",\n\"ahem\",\n\"ahey\",\n\"ahimsa\",\n\"ahind\",\n\"ahint\",\n\"ahmadi\",\n\"aho\",\n\"ahong\",\n\"ahorse\",\n\"ahoy\",\n\"ahsan\",\n\"ahu\",\n\"ahuatle\",\n\"ahull\",\n\"ahum\",\n\"ahungry\",\n\"ahunt\",\n\"ahura\",\n\"ahush\",\n\"ahwal\",\n\"ahypnia\",\n\"ai\",\n\"aid\",\n\"aidable\",\n\"aidance\",\n\"aidant\",\n\"aide\",\n\"aider\",\n\"aidful\",\n\"aidless\",\n\"aiel\",\n\"aiglet\",\n\"ail\",\n\"ailanto\",\n\"aile\",\n\"aileron\",\n\"ailette\",\n\"ailing\",\n\"aillt\",\n\"ailment\",\n\"ailsyte\",\n\"ailuro\",\n\"ailweed\",\n\"aim\",\n\"aimara\",\n\"aimer\",\n\"aimful\",\n\"aiming\",\n\"aimless\",\n\"ainaleh\",\n\"ainhum\",\n\"ainoi\",\n\"ainsell\",\n\"aint\",\n\"aion\",\n\"aionial\",\n\"air\",\n\"airable\",\n\"airampo\",\n\"airan\",\n\"aircrew\",\n\"airdock\",\n\"airdrop\",\n\"aire\",\n\"airer\",\n\"airfoil\",\n\"airhead\",\n\"airily\",\n\"airing\",\n\"airish\",\n\"airless\",\n\"airlift\",\n\"airlike\",\n\"airmail\",\n\"airman\",\n\"airmark\",\n\"airpark\",\n\"airport\",\n\"airship\",\n\"airsick\",\n\"airt\",\n\"airward\",\n\"airway\",\n\"airy\",\n\"aisle\",\n\"aisled\",\n\"aisling\",\n\"ait\",\n\"aitch\",\n\"aitesis\",\n\"aition\",\n\"aiwan\",\n\"aizle\",\n\"ajaja\",\n\"ajangle\",\n\"ajar\",\n\"ajari\",\n\"ajava\",\n\"ajhar\",\n\"ajivika\",\n\"ajog\",\n\"ajoint\",\n\"ajowan\",\n\"ak\",\n\"aka\",\n\"akala\",\n\"akaroa\",\n\"akasa\",\n\"akazga\",\n\"akcheh\",\n\"ake\",\n\"akeake\",\n\"akebi\",\n\"akee\",\n\"akeki\",\n\"akeley\",\n\"akepiro\",\n\"akerite\",\n\"akey\",\n\"akhoond\",\n\"akhrot\",\n\"akhyana\",\n\"akia\",\n\"akimbo\",\n\"akin\",\n\"akindle\",\n\"akinete\",\n\"akmudar\",\n\"aknee\",\n\"ako\",\n\"akoasm\",\n\"akoasma\",\n\"akonge\",\n\"akov\",\n\"akpek\",\n\"akra\",\n\"aku\",\n\"akule\",\n\"akund\",\n\"al\",\n\"ala\",\n\"alacha\",\n\"alack\",\n\"alada\",\n\"alaihi\",\n\"alaite\",\n\"alala\",\n\"alalite\",\n\"alalus\",\n\"alameda\",\n\"alamo\",\n\"alamoth\",\n\"alan\",\n\"aland\",\n\"alangin\",\n\"alani\",\n\"alanine\",\n\"alannah\",\n\"alantic\",\n\"alantin\",\n\"alantol\",\n\"alanyl\",\n\"alar\",\n\"alares\",\n\"alarm\",\n\"alarmed\",\n\"alarum\",\n\"alary\",\n\"alas\",\n\"alate\",\n\"alated\",\n\"alatern\",\n\"alation\",\n\"alb\",\n\"alba\",\n\"alban\",\n\"albarco\",\n\"albata\",\n\"albe\",\n\"albedo\",\n\"albee\",\n\"albeit\",\n\"albetad\",\n\"albify\",\n\"albinal\",\n\"albinic\",\n\"albino\",\n\"albite\",\n\"albitic\",\n\"albugo\",\n\"album\",\n\"albumen\",\n\"albumin\",\n\"alburn\",\n\"albus\",\n\"alcaide\",\n\"alcalde\",\n\"alcanna\",\n\"alcazar\",\n\"alchemy\",\n\"alchera\",\n\"alchimy\",\n\"alchymy\",\n\"alcine\",\n\"alclad\",\n\"alco\",\n\"alcoate\",\n\"alcogel\",\n\"alcohol\",\n\"alcosol\",\n\"alcove\",\n\"alcyon\",\n\"aldane\",\n\"aldazin\",\n\"aldehol\",\n\"alder\",\n\"aldern\",\n\"aldim\",\n\"aldime\",\n\"aldine\",\n\"aldol\",\n\"aldose\",\n\"ale\",\n\"aleak\",\n\"alec\",\n\"alecize\",\n\"alecost\",\n\"alecup\",\n\"alee\",\n\"alef\",\n\"aleft\",\n\"alegar\",\n\"alehoof\",\n\"alem\",\n\"alemana\",\n\"alembic\",\n\"alemite\",\n\"alemmal\",\n\"alen\",\n\"aleph\",\n\"alephs\",\n\"alepole\",\n\"alepot\",\n\"alerce\",\n\"alerse\",\n\"alert\",\n\"alertly\",\n\"alesan\",\n\"aletap\",\n\"alette\",\n\"alevin\",\n\"alewife\",\n\"alexia\",\n\"alexic\",\n\"alexin\",\n\"aleyard\",\n\"alf\",\n\"alfa\",\n\"alfaje\",\n\"alfalfa\",\n\"alfaqui\",\n\"alfet\",\n\"alfiona\",\n\"alfonso\",\n\"alforja\",\n\"alga\",\n\"algae\",\n\"algal\",\n\"algalia\",\n\"algate\",\n\"algebra\",\n\"algedo\",\n\"algesia\",\n\"algesic\",\n\"algesis\",\n\"algetic\",\n\"algic\",\n\"algid\",\n\"algific\",\n\"algin\",\n\"algine\",\n\"alginic\",\n\"algist\",\n\"algoid\",\n\"algor\",\n\"algosis\",\n\"algous\",\n\"algum\",\n\"alhenna\",\n\"alias\",\n\"alibi\",\n\"alible\",\n\"alichel\",\n\"alidade\",\n\"alien\",\n\"aliency\",\n\"alienee\",\n\"aliener\",\n\"alienor\",\n\"alif\",\n\"aliform\",\n\"alight\",\n\"align\",\n\"aligner\",\n\"aliipoe\",\n\"alike\",\n\"alima\",\n\"aliment\",\n\"alimony\",\n\"alin\",\n\"aliofar\",\n\"alipata\",\n\"aliped\",\n\"aliptes\",\n\"aliptic\",\n\"aliquot\",\n\"alish\",\n\"alisier\",\n\"alismad\",\n\"alismal\",\n\"aliso\",\n\"alison\",\n\"alisp\",\n\"alist\",\n\"alit\",\n\"alite\",\n\"aliunde\",\n\"alive\",\n\"aliyah\",\n\"alizari\",\n\"aljoba\",\n\"alk\",\n\"alkali\",\n\"alkalic\",\n\"alkamin\",\n\"alkane\",\n\"alkanet\",\n\"alkene\",\n\"alkenna\",\n\"alkenyl\",\n\"alkide\",\n\"alkine\",\n\"alkool\",\n\"alkoxy\",\n\"alkoxyl\",\n\"alky\",\n\"alkyd\",\n\"alkyl\",\n\"alkylic\",\n\"alkyne\",\n\"all\",\n\"allan\",\n\"allay\",\n\"allayer\",\n\"allbone\",\n\"allege\",\n\"alleger\",\n\"allegro\",\n\"allele\",\n\"allelic\",\n\"allene\",\n\"aller\",\n\"allergy\",\n\"alley\",\n\"alleyed\",\n\"allgood\",\n\"allheal\",\n\"allice\",\n\"allied\",\n\"allies\",\n\"allness\",\n\"allonym\",\n\"alloquy\",\n\"allose\",\n\"allot\",\n\"allotee\",\n\"allover\",\n\"allow\",\n\"allower\",\n\"alloxan\",\n\"alloy\",\n\"allseed\",\n\"alltud\",\n\"allude\",\n\"allure\",\n\"allurer\",\n\"alluvia\",\n\"allwork\",\n\"ally\",\n\"allyl\",\n\"allylic\",\n\"alma\",\n\"almadia\",\n\"almadie\",\n\"almagra\",\n\"almanac\",\n\"alme\",\n\"almemar\",\n\"almique\",\n\"almirah\",\n\"almoign\",\n\"almon\",\n\"almond\",\n\"almondy\",\n\"almoner\",\n\"almonry\",\n\"almost\",\n\"almous\",\n\"alms\",\n\"almsful\",\n\"almsman\",\n\"almuce\",\n\"almud\",\n\"almude\",\n\"almug\",\n\"almuten\",\n\"aln\",\n\"alnage\",\n\"alnager\",\n\"alnein\",\n\"alnico\",\n\"alnoite\",\n\"alnuin\",\n\"alo\",\n\"alochia\",\n\"alod\",\n\"alodial\",\n\"alodian\",\n\"alodium\",\n\"alody\",\n\"aloe\",\n\"aloed\",\n\"aloesol\",\n\"aloetic\",\n\"aloft\",\n\"alogia\",\n\"alogism\",\n\"alogy\",\n\"aloid\",\n\"aloin\",\n\"aloma\",\n\"alone\",\n\"along\",\n\"alongst\",\n\"aloof\",\n\"aloofly\",\n\"aloose\",\n\"alop\",\n\"alopeke\",\n\"alose\",\n\"aloud\",\n\"alow\",\n\"alowe\",\n\"alp\",\n\"alpaca\",\n\"alpeen\",\n\"alpha\",\n\"alphol\",\n\"alphorn\",\n\"alphos\",\n\"alphyl\",\n\"alpieu\",\n\"alpine\",\n\"alpist\",\n\"alquier\",\n\"alraun\",\n\"already\",\n\"alright\",\n\"alroot\",\n\"alruna\",\n\"also\",\n\"alsoon\",\n\"alt\",\n\"altaite\",\n\"altar\",\n\"altared\",\n\"alter\",\n\"alterer\",\n\"altern\",\n\"alterne\",\n\"althea\",\n\"althein\",\n\"altho\",\n\"althorn\",\n\"altilik\",\n\"altin\",\n\"alto\",\n\"altoun\",\n\"altrose\",\n\"altun\",\n\"aludel\",\n\"alula\",\n\"alular\",\n\"alulet\",\n\"alum\",\n\"alumic\",\n\"alumina\",\n\"alumine\",\n\"alumish\",\n\"alumite\",\n\"alumium\",\n\"alumna\",\n\"alumnae\",\n\"alumnal\",\n\"alumni\",\n\"alumnus\",\n\"alunite\",\n\"alupag\",\n\"alure\",\n\"aluta\",\n\"alvar\",\n\"alveary\",\n\"alveloz\",\n\"alveola\",\n\"alveole\",\n\"alveoli\",\n\"alveus\",\n\"alvine\",\n\"alvite\",\n\"alvus\",\n\"alway\",\n\"always\",\n\"aly\",\n\"alypin\",\n\"alysson\",\n\"am\",\n\"ama\",\n\"amaas\",\n\"amadou\",\n\"amaga\",\n\"amah\",\n\"amain\",\n\"amakebe\",\n\"amala\",\n\"amalaka\",\n\"amalgam\",\n\"amaltas\",\n\"amamau\",\n\"amandin\",\n\"amang\",\n\"amani\",\n\"amania\",\n\"amanori\",\n\"amanous\",\n\"amapa\",\n\"amar\",\n\"amarin\",\n\"amarine\",\n\"amarity\",\n\"amaroid\",\n\"amass\",\n\"amasser\",\n\"amastia\",\n\"amasty\",\n\"amateur\",\n\"amative\",\n\"amatol\",\n\"amatory\",\n\"amaze\",\n\"amazed\",\n\"amazia\",\n\"amazing\",\n\"amba\",\n\"ambage\",\n\"ambalam\",\n\"amban\",\n\"ambar\",\n\"ambaree\",\n\"ambary\",\n\"ambash\",\n\"ambassy\",\n\"ambatch\",\n\"ambay\",\n\"ambeer\",\n\"amber\",\n\"ambery\",\n\"ambiens\",\n\"ambient\",\n\"ambier\",\n\"ambit\",\n\"ambital\",\n\"ambitty\",\n\"ambitus\",\n\"amble\",\n\"ambler\",\n\"ambling\",\n\"ambo\",\n\"ambon\",\n\"ambos\",\n\"ambrain\",\n\"ambrein\",\n\"ambrite\",\n\"ambroid\",\n\"ambrose\",\n\"ambry\",\n\"ambsace\",\n\"ambury\",\n\"ambush\",\n\"amchoor\",\n\"ame\",\n\"ameed\",\n\"ameen\",\n\"amelia\",\n\"amellus\",\n\"amelu\",\n\"amelus\",\n\"amen\",\n\"amend\",\n\"amende\",\n\"amender\",\n\"amends\",\n\"amene\",\n\"amenia\",\n\"amenity\",\n\"ament\",\n\"amental\",\n\"amentia\",\n\"amentum\",\n\"amerce\",\n\"amercer\",\n\"amerism\",\n\"amesite\",\n\"ametria\",\n\"amgarn\",\n\"amhar\",\n\"amhran\",\n\"ami\",\n\"amiable\",\n\"amiably\",\n\"amianth\",\n\"amic\",\n\"amical\",\n\"amice\",\n\"amiced\",\n\"amicron\",\n\"amid\",\n\"amidase\",\n\"amidate\",\n\"amide\",\n\"amidic\",\n\"amidid\",\n\"amidide\",\n\"amidin\",\n\"amidine\",\n\"amido\",\n\"amidol\",\n\"amidon\",\n\"amidoxy\",\n\"amidst\",\n\"amil\",\n\"amimia\",\n\"amimide\",\n\"amin\",\n\"aminate\",\n\"amine\",\n\"amini\",\n\"aminic\",\n\"aminity\",\n\"aminize\",\n\"amino\",\n\"aminoid\",\n\"amir\",\n\"amiray\",\n\"amiss\",\n\"amity\",\n\"amixia\",\n\"amla\",\n\"amli\",\n\"amlikar\",\n\"amlong\",\n\"amma\",\n\"amman\",\n\"ammelin\",\n\"ammer\",\n\"ammeter\",\n\"ammine\",\n\"ammo\",\n\"ammonal\",\n\"ammonia\",\n\"ammonic\",\n\"ammono\",\n\"ammu\",\n\"amnesia\",\n\"amnesic\",\n\"amnesty\",\n\"amnia\",\n\"amniac\",\n\"amnic\",\n\"amnion\",\n\"amniote\",\n\"amober\",\n\"amobyr\",\n\"amoeba\",\n\"amoebae\",\n\"amoeban\",\n\"amoebic\",\n\"amoebid\",\n\"amok\",\n\"amoke\",\n\"amole\",\n\"amomal\",\n\"amomum\",\n\"among\",\n\"amongst\",\n\"amor\",\n\"amorado\",\n\"amoraic\",\n\"amoraim\",\n\"amoral\",\n\"amoret\",\n\"amorism\",\n\"amorist\",\n\"amoroso\",\n\"amorous\",\n\"amorphy\",\n\"amort\",\n\"amotion\",\n\"amotus\",\n\"amount\",\n\"amour\",\n\"amove\",\n\"ampalea\",\n\"amper\",\n\"ampere\",\n\"ampery\",\n\"amphid\",\n\"amphide\",\n\"amphora\",\n\"amphore\",\n\"ample\",\n\"amplify\",\n\"amply\",\n\"ampoule\",\n\"ampul\",\n\"ampulla\",\n\"amputee\",\n\"ampyx\",\n\"amra\",\n\"amreeta\",\n\"amrita\",\n\"amsath\",\n\"amsel\",\n\"amt\",\n\"amtman\",\n\"amuck\",\n\"amuguis\",\n\"amula\",\n\"amulet\",\n\"amulla\",\n\"amunam\",\n\"amurca\",\n\"amuse\",\n\"amused\",\n\"amusee\",\n\"amuser\",\n\"amusia\",\n\"amusing\",\n\"amusive\",\n\"amutter\",\n\"amuyon\",\n\"amuyong\",\n\"amuze\",\n\"amvis\",\n\"amy\",\n\"amyelia\",\n\"amyelic\",\n\"amygdal\",\n\"amyl\",\n\"amylan\",\n\"amylase\",\n\"amylate\",\n\"amylene\",\n\"amylic\",\n\"amylin\",\n\"amylo\",\n\"amyloid\",\n\"amylom\",\n\"amylon\",\n\"amylose\",\n\"amylum\",\n\"amyous\",\n\"amyrin\",\n\"amyrol\",\n\"amyroot\",\n\"an\",\n\"ana\",\n\"anabata\",\n\"anabo\",\n\"anabong\",\n\"anacara\",\n\"anacard\",\n\"anacid\",\n\"anadem\",\n\"anadrom\",\n\"anaemia\",\n\"anaemic\",\n\"anagap\",\n\"anagep\",\n\"anagoge\",\n\"anagogy\",\n\"anagram\",\n\"anagua\",\n\"anahau\",\n\"anal\",\n\"analav\",\n\"analgen\",\n\"analgia\",\n\"analgic\",\n\"anally\",\n\"analogy\",\n\"analyse\",\n\"analyst\",\n\"analyze\",\n\"anam\",\n\"anama\",\n\"anamite\",\n\"anan\",\n\"anana\",\n\"ananas\",\n\"ananda\",\n\"ananym\",\n\"anaphia\",\n\"anapnea\",\n\"anapsid\",\n\"anaqua\",\n\"anarch\",\n\"anarchy\",\n\"anareta\",\n\"anarya\",\n\"anatase\",\n\"anatifa\",\n\"anatine\",\n\"anatomy\",\n\"anatox\",\n\"anatron\",\n\"anaudia\",\n\"anaxial\",\n\"anaxon\",\n\"anaxone\",\n\"anay\",\n\"anba\",\n\"anbury\",\n\"anchor\",\n\"anchovy\",\n\"ancient\",\n\"ancile\",\n\"ancilla\",\n\"ancon\",\n\"anconad\",\n\"anconal\",\n\"ancone\",\n\"ancony\",\n\"ancora\",\n\"ancoral\",\n\"and\",\n\"anda\",\n\"andante\",\n\"andirin\",\n\"andiron\",\n\"andric\",\n\"android\",\n\"androl\",\n\"andron\",\n\"anear\",\n\"aneath\",\n\"anele\",\n\"anemia\",\n\"anemic\",\n\"anemone\",\n\"anemony\",\n\"anend\",\n\"anenst\",\n\"anent\",\n\"anepia\",\n\"anergia\",\n\"anergic\",\n\"anergy\",\n\"anerly\",\n\"aneroid\",\n\"anes\",\n\"anesis\",\n\"aneuria\",\n\"aneuric\",\n\"aneurin\",\n\"anew\",\n\"angaria\",\n\"angary\",\n\"angekok\",\n\"angel\",\n\"angelet\",\n\"angelic\",\n\"angelin\",\n\"angelot\",\n\"anger\",\n\"angerly\",\n\"angeyok\",\n\"angico\",\n\"angild\",\n\"angili\",\n\"angina\",\n\"anginal\",\n\"angioid\",\n\"angioma\",\n\"angle\",\n\"angled\",\n\"angler\",\n\"angling\",\n\"angloid\",\n\"ango\",\n\"angolar\",\n\"angor\",\n\"angrily\",\n\"angrite\",\n\"angry\",\n\"angst\",\n\"angster\",\n\"anguid\",\n\"anguine\",\n\"anguis\",\n\"anguish\",\n\"angula\",\n\"angular\",\n\"anguria\",\n\"anhang\",\n\"anhima\",\n\"anhinga\",\n\"ani\",\n\"anicut\",\n\"anidian\",\n\"aniente\",\n\"anigh\",\n\"anight\",\n\"anights\",\n\"anil\",\n\"anilao\",\n\"anilau\",\n\"anile\",\n\"anilic\",\n\"anilid\",\n\"anilide\",\n\"aniline\",\n\"anility\",\n\"anilla\",\n\"anima\",\n\"animal\",\n\"animate\",\n\"anime\",\n\"animi\",\n\"animism\",\n\"animist\",\n\"animize\",\n\"animous\",\n\"animus\",\n\"anion\",\n\"anionic\",\n\"anis\",\n\"anisal\",\n\"anisate\",\n\"anise\",\n\"aniseed\",\n\"anisic\",\n\"anisil\",\n\"anisoin\",\n\"anisole\",\n\"anisoyl\",\n\"anisum\",\n\"anisyl\",\n\"anither\",\n\"anjan\",\n\"ankee\",\n\"anker\",\n\"ankh\",\n\"ankle\",\n\"anklet\",\n\"anklong\",\n\"ankus\",\n\"ankusha\",\n\"anlace\",\n\"anlaut\",\n\"ann\",\n\"anna\",\n\"annal\",\n\"annale\",\n\"annals\",\n\"annat\",\n\"annates\",\n\"annatto\",\n\"anneal\",\n\"annelid\",\n\"annet\",\n\"annex\",\n\"annexa\",\n\"annexal\",\n\"annexer\",\n\"annite\",\n\"annona\",\n\"annoy\",\n\"annoyer\",\n\"annual\",\n\"annuary\",\n\"annuent\",\n\"annuity\",\n\"annul\",\n\"annular\",\n\"annulet\",\n\"annulus\",\n\"anoa\",\n\"anodal\",\n\"anode\",\n\"anodic\",\n\"anodize\",\n\"anodos\",\n\"anodyne\",\n\"anoesia\",\n\"anoesis\",\n\"anoetic\",\n\"anoil\",\n\"anoine\",\n\"anoint\",\n\"anole\",\n\"anoli\",\n\"anolian\",\n\"anolyte\",\n\"anomaly\",\n\"anomite\",\n\"anomy\",\n\"anon\",\n\"anonang\",\n\"anonol\",\n\"anonym\",\n\"anonyma\",\n\"anopia\",\n\"anopsia\",\n\"anorak\",\n\"anorexy\",\n\"anormal\",\n\"anorth\",\n\"anosmia\",\n\"anosmic\",\n\"another\",\n\"anotia\",\n\"anotta\",\n\"anotto\",\n\"anotus\",\n\"anounou\",\n\"anoxia\",\n\"anoxic\",\n\"ansa\",\n\"ansar\",\n\"ansate\",\n\"ansu\",\n\"answer\",\n\"ant\",\n\"anta\",\n\"antacid\",\n\"antal\",\n\"antapex\",\n\"antdom\",\n\"ante\",\n\"anteact\",\n\"anteal\",\n\"antefix\",\n\"antenna\",\n\"antes\",\n\"antewar\",\n\"anthela\",\n\"anthem\",\n\"anthema\",\n\"anthemy\",\n\"anther\",\n\"anthill\",\n\"anthine\",\n\"anthoid\",\n\"anthood\",\n\"anthrax\",\n\"anthrol\",\n\"anthryl\",\n\"anti\",\n\"antiae\",\n\"antiar\",\n\"antic\",\n\"antical\",\n\"anticly\",\n\"anticor\",\n\"anticum\",\n\"antifat\",\n\"antigen\",\n\"antigod\",\n\"antihum\",\n\"antiqua\",\n\"antique\",\n\"antired\",\n\"antirun\",\n\"antisun\",\n\"antitax\",\n\"antiwar\",\n\"antiwit\",\n\"antler\",\n\"antlia\",\n\"antling\",\n\"antoeci\",\n\"antonym\",\n\"antra\",\n\"antral\",\n\"antre\",\n\"antrin\",\n\"antrum\",\n\"antship\",\n\"antu\",\n\"antwise\",\n\"anubing\",\n\"anuloma\",\n\"anuran\",\n\"anuria\",\n\"anuric\",\n\"anurous\",\n\"anury\",\n\"anus\",\n\"anusim\",\n\"anvil\",\n\"anxiety\",\n\"anxious\",\n\"any\",\n\"anybody\",\n\"anyhow\",\n\"anyone\",\n\"anyway\",\n\"anyways\",\n\"anywhen\",\n\"anywhy\",\n\"anywise\",\n\"aogiri\",\n\"aonach\",\n\"aorist\",\n\"aorta\",\n\"aortal\",\n\"aortic\",\n\"aortism\",\n\"aosmic\",\n\"aoudad\",\n\"apa\",\n\"apace\",\n\"apache\",\n\"apadana\",\n\"apagoge\",\n\"apaid\",\n\"apalit\",\n\"apandry\",\n\"apar\",\n\"aparejo\",\n\"apart\",\n\"apasote\",\n\"apatan\",\n\"apathic\",\n\"apathy\",\n\"apatite\",\n\"ape\",\n\"apeak\",\n\"apedom\",\n\"apehood\",\n\"apeiron\",\n\"apelet\",\n\"apelike\",\n\"apeling\",\n\"apepsia\",\n\"apepsy\",\n\"apeptic\",\n\"aper\",\n\"aperch\",\n\"aperea\",\n\"apert\",\n\"apertly\",\n\"apery\",\n\"apetaly\",\n\"apex\",\n\"apexed\",\n\"aphagia\",\n\"aphakia\",\n\"aphakic\",\n\"aphasia\",\n\"aphasic\",\n\"aphemia\",\n\"aphemic\",\n\"aphesis\",\n\"apheta\",\n\"aphetic\",\n\"aphid\",\n\"aphides\",\n\"aphidid\",\n\"aphodal\",\n\"aphodus\",\n\"aphonia\",\n\"aphonic\",\n\"aphony\",\n\"aphoria\",\n\"aphotic\",\n\"aphrite\",\n\"aphtha\",\n\"aphthic\",\n\"aphylly\",\n\"aphyric\",\n\"apian\",\n\"apiary\",\n\"apiator\",\n\"apicad\",\n\"apical\",\n\"apices\",\n\"apicula\",\n\"apiece\",\n\"apieces\",\n\"apii\",\n\"apiin\",\n\"apilary\",\n\"apinch\",\n\"aping\",\n\"apinoid\",\n\"apio\",\n\"apioid\",\n\"apiole\",\n\"apiolin\",\n\"apionol\",\n\"apiose\",\n\"apish\",\n\"apishly\",\n\"apism\",\n\"apitong\",\n\"apitpat\",\n\"aplanat\",\n\"aplasia\",\n\"aplenty\",\n\"aplite\",\n\"aplitic\",\n\"aplomb\",\n\"aplome\",\n\"apnea\",\n\"apneal\",\n\"apneic\",\n\"apocarp\",\n\"apocha\",\n\"apocope\",\n\"apod\",\n\"apodal\",\n\"apodan\",\n\"apodema\",\n\"apodeme\",\n\"apodia\",\n\"apodous\",\n\"apogamy\",\n\"apogeal\",\n\"apogean\",\n\"apogee\",\n\"apogeic\",\n\"apogeny\",\n\"apohyal\",\n\"apoise\",\n\"apojove\",\n\"apokrea\",\n\"apolar\",\n\"apology\",\n\"aponia\",\n\"aponic\",\n\"apoop\",\n\"apoplex\",\n\"apopyle\",\n\"aporia\",\n\"aporose\",\n\"aport\",\n\"aposia\",\n\"aposoro\",\n\"apostil\",\n\"apostle\",\n\"apothem\",\n\"apotome\",\n\"apotype\",\n\"apout\",\n\"apozem\",\n\"apozema\",\n\"appall\",\n\"apparel\",\n\"appay\",\n\"appeal\",\n\"appear\",\n\"appease\",\n\"append\",\n\"appet\",\n\"appete\",\n\"applaud\",\n\"apple\",\n\"applied\",\n\"applier\",\n\"applot\",\n\"apply\",\n\"appoint\",\n\"apport\",\n\"appose\",\n\"apposer\",\n\"apprend\",\n\"apprise\",\n\"apprize\",\n\"approof\",\n\"approve\",\n\"appulse\",\n\"apraxia\",\n\"apraxic\",\n\"apricot\",\n\"apriori\",\n\"apron\",\n\"apropos\",\n\"apse\",\n\"apsidal\",\n\"apsides\",\n\"apsis\",\n\"apt\",\n\"apteral\",\n\"apteran\",\n\"aptly\",\n\"aptness\",\n\"aptote\",\n\"aptotic\",\n\"apulse\",\n\"apyonin\",\n\"apyrene\",\n\"apyrexy\",\n\"apyrous\",\n\"aqua\",\n\"aquabib\",\n\"aquage\",\n\"aquaria\",\n\"aquatic\",\n\"aquavit\",\n\"aqueous\",\n\"aquifer\",\n\"aquiver\",\n\"aquo\",\n\"aquose\",\n\"ar\",\n\"ara\",\n\"araba\",\n\"araban\",\n\"arabana\",\n\"arabin\",\n\"arabit\",\n\"arable\",\n\"araca\",\n\"aracari\",\n\"arachic\",\n\"arachin\",\n\"arad\",\n\"arado\",\n\"arain\",\n\"arake\",\n\"araliad\",\n\"aralie\",\n\"aralkyl\",\n\"aramina\",\n\"araneid\",\n\"aranein\",\n\"aranga\",\n\"arango\",\n\"arar\",\n\"arara\",\n\"ararao\",\n\"arariba\",\n\"araroba\",\n\"arati\",\n\"aration\",\n\"aratory\",\n\"arba\",\n\"arbacin\",\n\"arbalo\",\n\"arbiter\",\n\"arbor\",\n\"arboral\",\n\"arbored\",\n\"arboret\",\n\"arbute\",\n\"arbutin\",\n\"arbutus\",\n\"arc\",\n\"arca\",\n\"arcade\",\n\"arcana\",\n\"arcanal\",\n\"arcane\",\n\"arcanum\",\n\"arcate\",\n\"arch\",\n\"archae\",\n\"archaic\",\n\"arche\",\n\"archeal\",\n\"arched\",\n\"archer\",\n\"archery\",\n\"arches\",\n\"archeus\",\n\"archfoe\",\n\"archgod\",\n\"archil\",\n\"arching\",\n\"archive\",\n\"archly\",\n\"archon\",\n\"archont\",\n\"archsee\",\n\"archsin\",\n\"archspy\",\n\"archwag\",\n\"archway\",\n\"archy\",\n\"arcing\",\n\"arcked\",\n\"arcking\",\n\"arctian\",\n\"arctic\",\n\"arctiid\",\n\"arctoid\",\n\"arcual\",\n\"arcuale\",\n\"arcuate\",\n\"arcula\",\n\"ardeb\",\n\"ardella\",\n\"ardency\",\n\"ardent\",\n\"ardish\",\n\"ardoise\",\n\"ardor\",\n\"ardri\",\n\"ardu\",\n\"arduous\",\n\"are\",\n\"area\",\n\"areach\",\n\"aread\",\n\"areal\",\n\"arear\",\n\"areaway\",\n\"arecain\",\n\"ared\",\n\"areek\",\n\"areel\",\n\"arefact\",\n\"areito\",\n\"arena\",\n\"arenae\",\n\"arend\",\n\"areng\",\n\"arenoid\",\n\"arenose\",\n\"arent\",\n\"areola\",\n\"areolar\",\n\"areole\",\n\"areolet\",\n\"arete\",\n\"argal\",\n\"argala\",\n\"argali\",\n\"argans\",\n\"argasid\",\n\"argeers\",\n\"argel\",\n\"argenol\",\n\"argent\",\n\"arghan\",\n\"arghel\",\n\"arghool\",\n\"argil\",\n\"argo\",\n\"argol\",\n\"argolet\",\n\"argon\",\n\"argosy\",\n\"argot\",\n\"argotic\",\n\"argue\",\n\"arguer\",\n\"argufy\",\n\"argute\",\n\"argyria\",\n\"argyric\",\n\"arhar\",\n\"arhat\",\n\"aria\",\n\"aribine\",\n\"aricine\",\n\"arid\",\n\"aridge\",\n\"aridian\",\n\"aridity\",\n\"aridly\",\n\"ariel\",\n\"arienzo\",\n\"arietta\",\n\"aright\",\n\"arigue\",\n\"aril\",\n\"ariled\",\n\"arillus\",\n\"ariose\",\n\"arioso\",\n\"ariot\",\n\"aripple\",\n\"arisard\",\n\"arise\",\n\"arisen\",\n\"arist\",\n\"arista\",\n\"arite\",\n\"arjun\",\n\"ark\",\n\"arkite\",\n\"arkose\",\n\"arkosic\",\n\"arles\",\n\"arm\",\n\"armada\",\n\"armbone\",\n\"armed\",\n\"armer\",\n\"armet\",\n\"armful\",\n\"armhole\",\n\"armhoop\",\n\"armied\",\n\"armiger\",\n\"armil\",\n\"armilla\",\n\"arming\",\n\"armless\",\n\"armlet\",\n\"armload\",\n\"armoire\",\n\"armor\",\n\"armored\",\n\"armorer\",\n\"armory\",\n\"armpit\",\n\"armrack\",\n\"armrest\",\n\"arms\",\n\"armscye\",\n\"armure\",\n\"army\",\n\"arn\",\n\"arna\",\n\"arnee\",\n\"arni\",\n\"arnica\",\n\"arnotta\",\n\"arnotto\",\n\"arnut\",\n\"aroar\",\n\"aroast\",\n\"arock\",\n\"aroeira\",\n\"aroid\",\n\"aroint\",\n\"arolium\",\n\"arolla\",\n\"aroma\",\n\"aroon\",\n\"arose\",\n\"around\",\n\"arousal\",\n\"arouse\",\n\"arouser\",\n\"arow\",\n\"aroxyl\",\n\"arpen\",\n\"arpent\",\n\"arrack\",\n\"arrah\",\n\"arraign\",\n\"arrame\",\n\"arrange\",\n\"arrant\",\n\"arras\",\n\"arrased\",\n\"arratel\",\n\"arrau\",\n\"array\",\n\"arrayal\",\n\"arrayer\",\n\"arrear\",\n\"arrect\",\n\"arrent\",\n\"arrest\",\n\"arriage\",\n\"arriba\",\n\"arride\",\n\"arridge\",\n\"arrie\",\n\"arriere\",\n\"arrimby\",\n\"arris\",\n\"arrish\",\n\"arrival\",\n\"arrive\",\n\"arriver\",\n\"arroba\",\n\"arrope\",\n\"arrow\",\n\"arrowed\",\n\"arrowy\",\n\"arroyo\",\n\"arse\",\n\"arsenal\",\n\"arsenic\",\n\"arseno\",\n\"arsenyl\",\n\"arses\",\n\"arsheen\",\n\"arshin\",\n\"arshine\",\n\"arsine\",\n\"arsinic\",\n\"arsino\",\n\"arsis\",\n\"arsle\",\n\"arsoite\",\n\"arson\",\n\"arsonic\",\n\"arsono\",\n\"arsyl\",\n\"art\",\n\"artaba\",\n\"artabe\",\n\"artal\",\n\"artar\",\n\"artel\",\n\"arterin\",\n\"artery\",\n\"artful\",\n\"artha\",\n\"arthel\",\n\"arthral\",\n\"artiad\",\n\"article\",\n\"artisan\",\n\"artist\",\n\"artiste\",\n\"artless\",\n\"artlet\",\n\"artlike\",\n\"artware\",\n\"arty\",\n\"aru\",\n\"arui\",\n\"aruke\",\n\"arumin\",\n\"arupa\",\n\"arusa\",\n\"arusha\",\n\"arustle\",\n\"arval\",\n\"arvel\",\n\"arx\",\n\"ary\",\n\"aryl\",\n\"arylate\",\n\"arzan\",\n\"arzun\",\n\"as\",\n\"asaddle\",\n\"asak\",\n\"asale\",\n\"asana\",\n\"asaphia\",\n\"asaphid\",\n\"asaprol\",\n\"asarite\",\n\"asaron\",\n\"asarone\",\n\"asbest\",\n\"asbolin\",\n\"ascan\",\n\"ascare\",\n\"ascarid\",\n\"ascaron\",\n\"ascend\",\n\"ascent\",\n\"ascetic\",\n\"ascham\",\n\"asci\",\n\"ascian\",\n\"ascii\",\n\"ascites\",\n\"ascitic\",\n\"asclent\",\n\"ascoma\",\n\"ascon\",\n\"ascot\",\n\"ascribe\",\n\"ascript\",\n\"ascry\",\n\"ascula\",\n\"ascus\",\n\"asdic\",\n\"ase\",\n\"asearch\",\n\"aseethe\",\n\"aseity\",\n\"asem\",\n\"asemia\",\n\"asepsis\",\n\"aseptic\",\n\"aseptol\",\n\"asexual\",\n\"ash\",\n\"ashake\",\n\"ashame\",\n\"ashamed\",\n\"ashamnu\",\n\"ashcake\",\n\"ashen\",\n\"asherah\",\n\"ashery\",\n\"ashes\",\n\"ashet\",\n\"ashily\",\n\"ashine\",\n\"ashiver\",\n\"ashkoko\",\n\"ashlar\",\n\"ashless\",\n\"ashling\",\n\"ashman\",\n\"ashore\",\n\"ashpan\",\n\"ashpit\",\n\"ashraf\",\n\"ashrafi\",\n\"ashur\",\n\"ashweed\",\n\"ashwort\",\n\"ashy\",\n\"asialia\",\n\"aside\",\n\"asideu\",\n\"asiento\",\n\"asilid\",\n\"asimen\",\n\"asimmer\",\n\"asinego\",\n\"asinine\",\n\"asitia\",\n\"ask\",\n\"askable\",\n\"askance\",\n\"askant\",\n\"askar\",\n\"askari\",\n\"asker\",\n\"askew\",\n\"askip\",\n\"asklent\",\n\"askos\",\n\"aslant\",\n\"aslaver\",\n\"asleep\",\n\"aslop\",\n\"aslope\",\n\"asmack\",\n\"asmalte\",\n\"asmear\",\n\"asmile\",\n\"asmoke\",\n\"asnort\",\n\"asoak\",\n\"asocial\",\n\"asok\",\n\"asoka\",\n\"asonant\",\n\"asonia\",\n\"asop\",\n\"asor\",\n\"asouth\",\n\"asp\",\n\"aspace\",\n\"aspect\",\n\"aspen\",\n\"asper\",\n\"asperge\",\n\"asperse\",\n\"asphalt\",\n\"asphyxy\",\n\"aspic\",\n\"aspire\",\n\"aspirer\",\n\"aspirin\",\n\"aspish\",\n\"asport\",\n\"aspout\",\n\"asprawl\",\n\"aspread\",\n\"aspring\",\n\"asprout\",\n\"asquare\",\n\"asquat\",\n\"asqueal\",\n\"asquint\",\n\"asquirm\",\n\"ass\",\n\"assacu\",\n\"assagai\",\n\"assai\",\n\"assail\",\n\"assapan\",\n\"assart\",\n\"assary\",\n\"assate\",\n\"assault\",\n\"assaut\",\n\"assay\",\n\"assayer\",\n\"assbaa\",\n\"asse\",\n\"assegai\",\n\"asself\",\n\"assent\",\n\"assert\",\n\"assess\",\n\"asset\",\n\"assets\",\n\"assever\",\n\"asshead\",\n\"assi\",\n\"assify\",\n\"assign\",\n\"assilag\",\n\"assis\",\n\"assise\",\n\"assish\",\n\"assist\",\n\"assize\",\n\"assizer\",\n\"assizes\",\n\"asslike\",\n\"assman\",\n\"assoil\",\n\"assort\",\n\"assuade\",\n\"assuage\",\n\"assume\",\n\"assumed\",\n\"assumer\",\n\"assure\",\n\"assured\",\n\"assurer\",\n\"assurge\",\n\"ast\",\n\"asta\",\n\"astalk\",\n\"astare\",\n\"astart\",\n\"astasia\",\n\"astatic\",\n\"astay\",\n\"asteam\",\n\"asteep\",\n\"asteer\",\n\"asteism\",\n\"astelic\",\n\"astely\",\n\"aster\",\n\"asteria\",\n\"asterin\",\n\"astern\",\n\"astheny\",\n\"asthma\",\n\"asthore\",\n\"astilbe\",\n\"astint\",\n\"astir\",\n\"astite\",\n\"astomia\",\n\"astony\",\n\"astoop\",\n\"astor\",\n\"astound\",\n\"astrain\",\n\"astral\",\n\"astrand\",\n\"astray\",\n\"astream\",\n\"astrer\",\n\"astrict\",\n\"astride\",\n\"astrier\",\n\"astrild\",\n\"astroid\",\n\"astrut\",\n\"astute\",\n\"astylar\",\n\"asudden\",\n\"asunder\",\n\"aswail\",\n\"aswarm\",\n\"asway\",\n\"asweat\",\n\"aswell\",\n\"aswim\",\n\"aswing\",\n\"aswirl\",\n\"aswoon\",\n\"asyla\",\n\"asylum\",\n\"at\",\n\"atabal\",\n\"atabeg\",\n\"atabek\",\n\"atactic\",\n\"atafter\",\n\"ataman\",\n\"atangle\",\n\"atap\",\n\"ataraxy\",\n\"ataunt\",\n\"atavi\",\n\"atavic\",\n\"atavism\",\n\"atavist\",\n\"atavus\",\n\"ataxia\",\n\"ataxic\",\n\"ataxite\",\n\"ataxy\",\n\"atazir\",\n\"atbash\",\n\"ate\",\n\"atebrin\",\n\"atechny\",\n\"ateeter\",\n\"atef\",\n\"atelets\",\n\"atelier\",\n\"atelo\",\n\"ates\",\n\"ateuchi\",\n\"athanor\",\n\"athar\",\n\"atheism\",\n\"atheist\",\n\"atheize\",\n\"athelia\",\n\"athenee\",\n\"athenor\",\n\"atheous\",\n\"athing\",\n\"athirst\",\n\"athlete\",\n\"athodyd\",\n\"athort\",\n\"athrill\",\n\"athrive\",\n\"athrob\",\n\"athrong\",\n\"athwart\",\n\"athymia\",\n\"athymic\",\n\"athymy\",\n\"athyria\",\n\"athyrid\",\n\"atilt\",\n\"atimon\",\n\"atinga\",\n\"atingle\",\n\"atinkle\",\n\"atip\",\n\"atis\",\n\"atlas\",\n\"atlatl\",\n\"atle\",\n\"atlee\",\n\"atloid\",\n\"atma\",\n\"atman\",\n\"atmid\",\n\"atmo\",\n\"atmos\",\n\"atocha\",\n\"atocia\",\n\"atokal\",\n\"atoke\",\n\"atokous\",\n\"atoll\",\n\"atom\",\n\"atomerg\",\n\"atomic\",\n\"atomics\",\n\"atomism\",\n\"atomist\",\n\"atomity\",\n\"atomize\",\n\"atomy\",\n\"atonal\",\n\"atone\",\n\"atoner\",\n\"atonia\",\n\"atonic\",\n\"atony\",\n\"atop\",\n\"atophan\",\n\"atopic\",\n\"atopite\",\n\"atopy\",\n\"atour\",\n\"atoxic\",\n\"atoxyl\",\n\"atrail\",\n\"atrepsy\",\n\"atresia\",\n\"atresic\",\n\"atresy\",\n\"atretic\",\n\"atria\",\n\"atrial\",\n\"atrip\",\n\"atrium\",\n\"atrocha\",\n\"atropal\",\n\"atrophy\",\n\"atropia\",\n\"atropic\",\n\"atrous\",\n\"atry\",\n\"atta\",\n\"attacco\",\n\"attach\",\n\"attache\",\n\"attack\",\n\"attacus\",\n\"attagen\",\n\"attain\",\n\"attaint\",\n\"attaleh\",\n\"attar\",\n\"attask\",\n\"attempt\",\n\"attend\",\n\"attent\",\n\"atter\",\n\"attern\",\n\"attery\",\n\"attest\",\n\"attic\",\n\"attid\",\n\"attinge\",\n\"attire\",\n\"attired\",\n\"attirer\",\n\"attorn\",\n\"attract\",\n\"attrap\",\n\"attrist\",\n\"attrite\",\n\"attune\",\n\"atule\",\n\"atumble\",\n\"atune\",\n\"atwain\",\n\"atweel\",\n\"atween\",\n\"atwin\",\n\"atwirl\",\n\"atwist\",\n\"atwitch\",\n\"atwixt\",\n\"atwo\",\n\"atypic\",\n\"atypy\",\n\"auantic\",\n\"aube\",\n\"aubrite\",\n\"auburn\",\n\"auca\",\n\"auchlet\",\n\"auction\",\n\"aucuba\",\n\"audible\",\n\"audibly\",\n\"audient\",\n\"audile\",\n\"audio\",\n\"audion\",\n\"audit\",\n\"auditor\",\n\"auge\",\n\"augen\",\n\"augend\",\n\"auger\",\n\"augerer\",\n\"augh\",\n\"aught\",\n\"augite\",\n\"augitic\",\n\"augment\",\n\"augur\",\n\"augural\",\n\"augury\",\n\"august\",\n\"auh\",\n\"auhuhu\",\n\"auk\",\n\"auklet\",\n\"aula\",\n\"aulae\",\n\"auld\",\n\"auletai\",\n\"aulete\",\n\"auletes\",\n\"auletic\",\n\"aulic\",\n\"auloi\",\n\"aulos\",\n\"aulu\",\n\"aum\",\n\"aumaga\",\n\"aumail\",\n\"aumbry\",\n\"aumery\",\n\"aumil\",\n\"aumous\",\n\"aumrie\",\n\"auncel\",\n\"aune\",\n\"aunt\",\n\"auntie\",\n\"auntish\",\n\"auntly\",\n\"aupaka\",\n\"aura\",\n\"aurae\",\n\"aural\",\n\"aurally\",\n\"aurar\",\n\"aurate\",\n\"aurated\",\n\"aureate\",\n\"aureity\",\n\"aurelia\",\n\"aureola\",\n\"aureole\",\n\"aureous\",\n\"auresca\",\n\"aureus\",\n\"auric\",\n\"auricle\",\n\"auride\",\n\"aurific\",\n\"aurify\",\n\"aurigal\",\n\"aurin\",\n\"aurir\",\n\"aurist\",\n\"aurite\",\n\"aurochs\",\n\"auronal\",\n\"aurora\",\n\"aurorae\",\n\"auroral\",\n\"aurore\",\n\"aurous\",\n\"aurum\",\n\"aurure\",\n\"auryl\",\n\"auscult\",\n\"auslaut\",\n\"auspex\",\n\"auspice\",\n\"auspicy\",\n\"austere\",\n\"austral\",\n\"ausu\",\n\"ausubo\",\n\"autarch\",\n\"autarky\",\n\"aute\",\n\"autecy\",\n\"autem\",\n\"author\",\n\"autism\",\n\"autist\",\n\"auto\",\n\"autobus\",\n\"autocab\",\n\"autocar\",\n\"autoecy\",\n\"autoist\",\n\"automa\",\n\"automat\",\n\"autonym\",\n\"autopsy\",\n\"autumn\",\n\"auxesis\",\n\"auxetic\",\n\"auxin\",\n\"auxinic\",\n\"auxotox\",\n\"ava\",\n\"avadana\",\n\"avahi\",\n\"avail\",\n\"aval\",\n\"avalent\",\n\"avania\",\n\"avarice\",\n\"avast\",\n\"avaunt\",\n\"ave\",\n\"avellan\",\n\"aveloz\",\n\"avenage\",\n\"avener\",\n\"avenge\",\n\"avenger\",\n\"avenin\",\n\"avenous\",\n\"avens\",\n\"avenue\",\n\"aver\",\n\"avera\",\n\"average\",\n\"averah\",\n\"averil\",\n\"averin\",\n\"averral\",\n\"averse\",\n\"avert\",\n\"averted\",\n\"averter\",\n\"avian\",\n\"aviary\",\n\"aviate\",\n\"aviatic\",\n\"aviator\",\n\"avichi\",\n\"avicide\",\n\"avick\",\n\"avid\",\n\"avidity\",\n\"avidly\",\n\"avidous\",\n\"avidya\",\n\"avigate\",\n\"avijja\",\n\"avine\",\n\"aviso\",\n\"avital\",\n\"avitic\",\n\"avives\",\n\"avo\",\n\"avocado\",\n\"avocate\",\n\"avocet\",\n\"avodire\",\n\"avoid\",\n\"avoider\",\n\"avolate\",\n\"avouch\",\n\"avow\",\n\"avowal\",\n\"avowant\",\n\"avowed\",\n\"avower\",\n\"avowry\",\n\"avoyer\",\n\"avulse\",\n\"aw\",\n\"awa\",\n\"awabi\",\n\"awaft\",\n\"awag\",\n\"await\",\n\"awaiter\",\n\"awake\",\n\"awaken\",\n\"awald\",\n\"awalim\",\n\"awalt\",\n\"awane\",\n\"awapuhi\",\n\"award\",\n\"awarder\",\n\"aware\",\n\"awash\",\n\"awaste\",\n\"awat\",\n\"awatch\",\n\"awater\",\n\"awave\",\n\"away\",\n\"awber\",\n\"awd\",\n\"awe\",\n\"aweary\",\n\"aweband\",\n\"awee\",\n\"aweek\",\n\"aweel\",\n\"aweigh\",\n\"awesome\",\n\"awest\",\n\"aweto\",\n\"awfu\",\n\"awful\",\n\"awfully\",\n\"awheel\",\n\"awheft\",\n\"awhet\",\n\"awhile\",\n\"awhir\",\n\"awhirl\",\n\"awide\",\n\"awiggle\",\n\"awin\",\n\"awing\",\n\"awink\",\n\"awiwi\",\n\"awkward\",\n\"awl\",\n\"awless\",\n\"awlwort\",\n\"awmous\",\n\"awn\",\n\"awned\",\n\"awner\",\n\"awning\",\n\"awnless\",\n\"awnlike\",\n\"awny\",\n\"awoke\",\n\"awork\",\n\"awreck\",\n\"awrist\",\n\"awrong\",\n\"awry\",\n\"ax\",\n\"axal\",\n\"axe\",\n\"axed\",\n\"axenic\",\n\"axes\",\n\"axfetch\",\n\"axhead\",\n\"axial\",\n\"axially\",\n\"axiate\",\n\"axiform\",\n\"axil\",\n\"axile\",\n\"axilla\",\n\"axillae\",\n\"axillar\",\n\"axine\",\n\"axinite\",\n\"axiom\",\n\"axion\",\n\"axis\",\n\"axised\",\n\"axite\",\n\"axle\",\n\"axled\",\n\"axmaker\",\n\"axman\",\n\"axogamy\",\n\"axoid\",\n\"axolotl\",\n\"axon\",\n\"axonal\",\n\"axonost\",\n\"axseed\",\n\"axstone\",\n\"axtree\",\n\"axunge\",\n\"axweed\",\n\"axwise\",\n\"axwort\",\n\"ay\",\n\"ayah\",\n\"aye\",\n\"ayelp\",\n\"ayin\",\n\"ayless\",\n\"aylet\",\n\"ayllu\",\n\"ayond\",\n\"ayont\",\n\"ayous\",\n\"ayu\",\n\"azafrin\",\n\"azalea\",\n\"azarole\",\n\"azelaic\",\n\"azelate\",\n\"azide\",\n\"azilut\",\n\"azimene\",\n\"azimide\",\n\"azimine\",\n\"azimino\",\n\"azimuth\",\n\"azine\",\n\"aziola\",\n\"azo\",\n\"azoch\",\n\"azofier\",\n\"azofy\",\n\"azoic\",\n\"azole\",\n\"azon\",\n\"azonal\",\n\"azonic\",\n\"azonium\",\n\"azophen\",\n\"azorite\",\n\"azotate\",\n\"azote\",\n\"azoted\",\n\"azoth\",\n\"azotic\",\n\"azotine\",\n\"azotite\",\n\"azotize\",\n\"azotous\",\n\"azox\",\n\"azoxime\",\n\"azoxine\",\n\"azoxy\",\n\"azteca\",\n\"azulene\",\n\"azulite\",\n\"azulmic\",\n\"azumbre\",\n\"azure\",\n\"azurean\",\n\"azured\",\n\"azurine\",\n\"azurite\",\n\"azurous\",\n\"azury\",\n\"azygos\",\n\"azygous\",\n\"azyme\",\n\"azymite\",\n\"azymous\",\n\"b\",\n\"ba\",\n\"baa\",\n\"baal\",\n\"baar\",\n\"baba\",\n\"babai\",\n\"babasco\",\n\"babassu\",\n\"babbitt\",\n\"babble\",\n\"babbler\",\n\"babbly\",\n\"babby\",\n\"babe\",\n\"babelet\",\n\"babery\",\n\"babiche\",\n\"babied\",\n\"babish\",\n\"bablah\",\n\"babloh\",\n\"baboen\",\n\"baboo\",\n\"baboon\",\n\"baboot\",\n\"babroot\",\n\"babu\",\n\"babudom\",\n\"babuina\",\n\"babuism\",\n\"babul\",\n\"baby\",\n\"babydom\",\n\"babyish\",\n\"babyism\",\n\"bac\",\n\"bacaba\",\n\"bacach\",\n\"bacalao\",\n\"bacao\",\n\"bacca\",\n\"baccae\",\n\"baccara\",\n\"baccate\",\n\"bacchar\",\n\"bacchic\",\n\"bacchii\",\n\"bach\",\n\"bache\",\n\"bachel\",\n\"bacilli\",\n\"back\",\n\"backage\",\n\"backcap\",\n\"backed\",\n\"backen\",\n\"backer\",\n\"backet\",\n\"backie\",\n\"backing\",\n\"backjaw\",\n\"backlet\",\n\"backlog\",\n\"backrun\",\n\"backsaw\",\n\"backset\",\n\"backup\",\n\"backway\",\n\"baclin\",\n\"bacon\",\n\"baconer\",\n\"bacony\",\n\"bacula\",\n\"bacule\",\n\"baculi\",\n\"baculum\",\n\"baculus\",\n\"bacury\",\n\"bad\",\n\"badan\",\n\"baddish\",\n\"baddock\",\n\"bade\",\n\"badge\",\n\"badger\",\n\"badiaga\",\n\"badian\",\n\"badious\",\n\"badland\",\n\"badly\",\n\"badness\",\n\"bae\",\n\"baetuli\",\n\"baetyl\",\n\"bafaro\",\n\"baff\",\n\"baffeta\",\n\"baffle\",\n\"baffler\",\n\"baffy\",\n\"baft\",\n\"bafta\",\n\"bag\",\n\"baga\",\n\"bagani\",\n\"bagasse\",\n\"bagel\",\n\"bagful\",\n\"baggage\",\n\"baggala\",\n\"bagged\",\n\"bagger\",\n\"baggie\",\n\"baggily\",\n\"bagging\",\n\"baggit\",\n\"baggy\",\n\"baglike\",\n\"bagman\",\n\"bagnio\",\n\"bagnut\",\n\"bago\",\n\"bagonet\",\n\"bagpipe\",\n\"bagre\",\n\"bagreef\",\n\"bagroom\",\n\"bagwig\",\n\"bagworm\",\n\"bagwyn\",\n\"bah\",\n\"bahan\",\n\"bahar\",\n\"bahay\",\n\"bahera\",\n\"bahisti\",\n\"bahnung\",\n\"baho\",\n\"bahoe\",\n\"bahoo\",\n\"baht\",\n\"bahur\",\n\"bahut\",\n\"baignet\",\n\"baikie\",\n\"bail\",\n\"bailage\",\n\"bailee\",\n\"bailer\",\n\"bailey\",\n\"bailie\",\n\"bailiff\",\n\"bailor\",\n\"bain\",\n\"bainie\",\n\"baioc\",\n\"baiocco\",\n\"bairagi\",\n\"bairn\",\n\"bairnie\",\n\"bairnly\",\n\"baister\",\n\"bait\",\n\"baiter\",\n\"baith\",\n\"baittle\",\n\"baize\",\n\"bajada\",\n\"bajan\",\n\"bajra\",\n\"bajree\",\n\"bajri\",\n\"bajury\",\n\"baka\",\n\"bakal\",\n\"bake\",\n\"baked\",\n\"baken\",\n\"bakepan\",\n\"baker\",\n\"bakerly\",\n\"bakery\",\n\"bakie\",\n\"baking\",\n\"bakli\",\n\"baktun\",\n\"baku\",\n\"bakula\",\n\"bal\",\n\"balafo\",\n\"balagan\",\n\"balai\",\n\"balance\",\n\"balanic\",\n\"balanid\",\n\"balao\",\n\"balas\",\n\"balata\",\n\"balboa\",\n\"balcony\",\n\"bald\",\n\"balden\",\n\"balder\",\n\"baldish\",\n\"baldly\",\n\"baldrib\",\n\"baldric\",\n\"baldy\",\n\"bale\",\n\"baleen\",\n\"baleful\",\n\"balei\",\n\"baleise\",\n\"baler\",\n\"balete\",\n\"bali\",\n\"baline\",\n\"balita\",\n\"balk\",\n\"balker\",\n\"balky\",\n\"ball\",\n\"ballad\",\n\"ballade\",\n\"ballam\",\n\"ballan\",\n\"ballant\",\n\"ballast\",\n\"ballata\",\n\"ballate\",\n\"balldom\",\n\"balled\",\n\"baller\",\n\"ballet\",\n\"balli\",\n\"ballist\",\n\"ballium\",\n\"balloon\",\n\"ballot\",\n\"ballow\",\n\"ballup\",\n\"bally\",\n\"balm\",\n\"balmily\",\n\"balmony\",\n\"balmy\",\n\"balneal\",\n\"balonea\",\n\"baloney\",\n\"baloo\",\n\"balow\",\n\"balsa\",\n\"balsam\",\n\"balsamo\",\n\"balsamy\",\n\"baltei\",\n\"balter\",\n\"balteus\",\n\"balu\",\n\"balut\",\n\"balza\",\n\"bam\",\n\"bamban\",\n\"bambini\",\n\"bambino\",\n\"bamboo\",\n\"bamoth\",\n\"ban\",\n\"banaba\",\n\"banago\",\n\"banak\",\n\"banal\",\n\"banally\",\n\"banana\",\n\"banat\",\n\"banc\",\n\"banca\",\n\"bancal\",\n\"banchi\",\n\"banco\",\n\"bancus\",\n\"band\",\n\"banda\",\n\"bandage\",\n\"bandaka\",\n\"bandala\",\n\"bandar\",\n\"bandbox\",\n\"bande\",\n\"bandeau\",\n\"banded\",\n\"bander\",\n\"bandhu\",\n\"bandi\",\n\"bandie\",\n\"banding\",\n\"bandit\",\n\"bandle\",\n\"bandlet\",\n\"bandman\",\n\"bando\",\n\"bandog\",\n\"bandore\",\n\"bandrol\",\n\"bandy\",\n\"bane\",\n\"baneful\",\n\"bang\",\n\"banga\",\n\"bange\",\n\"banger\",\n\"banghy\",\n\"banging\",\n\"bangkok\",\n\"bangle\",\n\"bangled\",\n\"bani\",\n\"banian\",\n\"banig\",\n\"banilad\",\n\"banish\",\n\"baniwa\",\n\"baniya\",\n\"banjo\",\n\"banjore\",\n\"banjuke\",\n\"bank\",\n\"banked\",\n\"banker\",\n\"bankera\",\n\"banket\",\n\"banking\",\n\"bankman\",\n\"banky\",\n\"banner\",\n\"bannet\",\n\"banning\",\n\"bannock\",\n\"banns\",\n\"bannut\",\n\"banquet\",\n\"banshee\",\n\"bant\",\n\"bantam\",\n\"bantay\",\n\"banteng\",\n\"banter\",\n\"bantery\",\n\"banty\",\n\"banuyo\",\n\"banya\",\n\"banyan\",\n\"banzai\",\n\"baobab\",\n\"bap\",\n\"baptism\",\n\"baptize\",\n\"bar\",\n\"bara\",\n\"barad\",\n\"barauna\",\n\"barb\",\n\"barbal\",\n\"barbary\",\n\"barbas\",\n\"barbate\",\n\"barbe\",\n\"barbed\",\n\"barbel\",\n\"barber\",\n\"barbet\",\n\"barbion\",\n\"barblet\",\n\"barbone\",\n\"barbudo\",\n\"barbule\",\n\"bard\",\n\"bardane\",\n\"bardash\",\n\"bardel\",\n\"bardess\",\n\"bardic\",\n\"bardie\",\n\"bardily\",\n\"barding\",\n\"bardish\",\n\"bardism\",\n\"bardlet\",\n\"bardo\",\n\"bardy\",\n\"bare\",\n\"bareca\",\n\"barefit\",\n\"barely\",\n\"barer\",\n\"baresma\",\n\"baretta\",\n\"barff\",\n\"barfish\",\n\"barfly\",\n\"barful\",\n\"bargain\",\n\"barge\",\n\"bargee\",\n\"bargeer\",\n\"barger\",\n\"bargh\",\n\"bargham\",\n\"bari\",\n\"baria\",\n\"baric\",\n\"barid\",\n\"barie\",\n\"barile\",\n\"barilla\",\n\"baring\",\n\"baris\",\n\"barish\",\n\"barit\",\n\"barite\",\n\"barium\",\n\"bark\",\n\"barken\",\n\"barker\",\n\"barkery\",\n\"barkey\",\n\"barkhan\",\n\"barking\",\n\"barkle\",\n\"barky\",\n\"barless\",\n\"barley\",\n\"barling\",\n\"barlock\",\n\"barlow\",\n\"barm\",\n\"barmaid\",\n\"barman\",\n\"barmkin\",\n\"barmote\",\n\"barmy\",\n\"barn\",\n\"barnard\",\n\"barney\",\n\"barnful\",\n\"barnman\",\n\"barny\",\n\"baroi\",\n\"barolo\",\n\"baron\",\n\"baronet\",\n\"barong\",\n\"baronry\",\n\"barony\",\n\"baroque\",\n\"baroto\",\n\"barpost\",\n\"barra\",\n\"barrack\",\n\"barrad\",\n\"barrage\",\n\"barras\",\n\"barred\",\n\"barrel\",\n\"barren\",\n\"barrer\",\n\"barret\",\n\"barrico\",\n\"barrier\",\n\"barring\",\n\"barrio\",\n\"barroom\",\n\"barrow\",\n\"barruly\",\n\"barry\",\n\"barse\",\n\"barsom\",\n\"barter\",\n\"barth\",\n\"barton\",\n\"baru\",\n\"baruria\",\n\"barvel\",\n\"barwal\",\n\"barway\",\n\"barways\",\n\"barwise\",\n\"barwood\",\n\"barye\",\n\"baryta\",\n\"barytes\",\n\"barytic\",\n\"baryton\",\n\"bas\",\n\"basal\",\n\"basale\",\n\"basalia\",\n\"basally\",\n\"basalt\",\n\"basaree\",\n\"bascule\",\n\"base\",\n\"based\",\n\"basely\",\n\"baseman\",\n\"basenji\",\n\"bases\",\n\"bash\",\n\"bashaw\",\n\"bashful\",\n\"bashlyk\",\n\"basial\",\n\"basiate\",\n\"basic\",\n\"basidia\",\n\"basify\",\n\"basil\",\n\"basilar\",\n\"basilic\",\n\"basin\",\n\"basined\",\n\"basinet\",\n\"basion\",\n\"basis\",\n\"bask\",\n\"basker\",\n\"basket\",\n\"basoid\",\n\"bason\",\n\"basos\",\n\"basote\",\n\"basque\",\n\"basqued\",\n\"bass\",\n\"bassan\",\n\"bassara\",\n\"basset\",\n\"bassie\",\n\"bassine\",\n\"bassist\",\n\"basso\",\n\"bassoon\",\n\"bassus\",\n\"bast\",\n\"basta\",\n\"bastard\",\n\"baste\",\n\"basten\",\n\"baster\",\n\"bastide\",\n\"basting\",\n\"bastion\",\n\"bastite\",\n\"basto\",\n\"baston\",\n\"bat\",\n\"bataan\",\n\"batad\",\n\"batakan\",\n\"batara\",\n\"batata\",\n\"batch\",\n\"batcher\",\n\"bate\",\n\"batea\",\n\"bateau\",\n\"bateaux\",\n\"bated\",\n\"batel\",\n\"bateman\",\n\"bater\",\n\"batfish\",\n\"batfowl\",\n\"bath\",\n\"bathe\",\n\"bather\",\n\"bathic\",\n\"bathing\",\n\"bathman\",\n\"bathmic\",\n\"bathos\",\n\"bathtub\",\n\"bathyal\",\n\"batik\",\n\"batiker\",\n\"bating\",\n\"batino\",\n\"batiste\",\n\"batlan\",\n\"batlike\",\n\"batling\",\n\"batlon\",\n\"batman\",\n\"batoid\",\n\"baton\",\n\"batonne\",\n\"bats\",\n\"batsman\",\n\"batster\",\n\"batt\",\n\"batta\",\n\"battel\",\n\"batten\",\n\"batter\",\n\"battery\",\n\"battik\",\n\"batting\",\n\"battish\",\n\"battle\",\n\"battled\",\n\"battler\",\n\"battue\",\n\"batty\",\n\"batule\",\n\"batwing\",\n\"batz\",\n\"batzen\",\n\"bauble\",\n\"bauch\",\n\"bauchle\",\n\"bauckie\",\n\"baud\",\n\"baul\",\n\"bauleah\",\n\"baun\",\n\"bauno\",\n\"bauson\",\n\"bausond\",\n\"bauta\",\n\"bauxite\",\n\"bavaroy\",\n\"bavary\",\n\"bavian\",\n\"baviere\",\n\"bavin\",\n\"bavoso\",\n\"baw\",\n\"bawbee\",\n\"bawcock\",\n\"bawd\",\n\"bawdily\",\n\"bawdry\",\n\"bawl\",\n\"bawler\",\n\"bawley\",\n\"bawn\",\n\"bawtie\",\n\"baxter\",\n\"baxtone\",\n\"bay\",\n\"baya\",\n\"bayal\",\n\"bayamo\",\n\"bayard\",\n\"baybolt\",\n\"baybush\",\n\"baycuru\",\n\"bayed\",\n\"bayeta\",\n\"baygall\",\n\"bayhead\",\n\"bayish\",\n\"baylet\",\n\"baylike\",\n\"bayman\",\n\"bayness\",\n\"bayok\",\n\"bayonet\",\n\"bayou\",\n\"baywood\",\n\"bazaar\",\n\"baze\",\n\"bazoo\",\n\"bazooka\",\n\"bazzite\",\n\"bdellid\",\n\"be\",\n\"beach\",\n\"beached\",\n\"beachy\",\n\"beacon\",\n\"bead\",\n\"beaded\",\n\"beader\",\n\"beadily\",\n\"beading\",\n\"beadle\",\n\"beadlet\",\n\"beadman\",\n\"beadrow\",\n\"beady\",\n\"beagle\",\n\"beak\",\n\"beaked\",\n\"beaker\",\n\"beakful\",\n\"beaky\",\n\"beal\",\n\"beala\",\n\"bealing\",\n\"beam\",\n\"beamage\",\n\"beamed\",\n\"beamer\",\n\"beamful\",\n\"beamily\",\n\"beaming\",\n\"beamish\",\n\"beamlet\",\n\"beamman\",\n\"beamy\",\n\"bean\",\n\"beanbag\",\n\"beancod\",\n\"beanery\",\n\"beanie\",\n\"beano\",\n\"beant\",\n\"beany\",\n\"bear\",\n\"beard\",\n\"bearded\",\n\"bearder\",\n\"beardie\",\n\"beardom\",\n\"beardy\",\n\"bearer\",\n\"bearess\",\n\"bearing\",\n\"bearish\",\n\"bearlet\",\n\"bearm\",\n\"beast\",\n\"beastie\",\n\"beastly\",\n\"beat\",\n\"beata\",\n\"beatae\",\n\"beatee\",\n\"beaten\",\n\"beater\",\n\"beath\",\n\"beatify\",\n\"beating\",\n\"beatus\",\n\"beau\",\n\"beaufin\",\n\"beauish\",\n\"beauism\",\n\"beauti\",\n\"beauty\",\n\"beaux\",\n\"beaver\",\n\"beavery\",\n\"beback\",\n\"bebait\",\n\"bebang\",\n\"bebar\",\n\"bebaron\",\n\"bebaste\",\n\"bebat\",\n\"bebathe\",\n\"bebay\",\n\"bebeast\",\n\"bebed\",\n\"bebeeru\",\n\"bebilya\",\n\"bebite\",\n\"beblain\",\n\"beblear\",\n\"bebled\",\n\"bebless\",\n\"beblood\",\n\"bebloom\",\n\"bebog\",\n\"bebop\",\n\"beboss\",\n\"bebotch\",\n\"bebrave\",\n\"bebrine\",\n\"bebrush\",\n\"bebump\",\n\"bebusy\",\n\"becall\",\n\"becalm\",\n\"becap\",\n\"becard\",\n\"becarve\",\n\"becater\",\n\"because\",\n\"becense\",\n\"bechalk\",\n\"becharm\",\n\"bechase\",\n\"becheck\",\n\"becher\",\n\"bechern\",\n\"bechirp\",\n\"becivet\",\n\"beck\",\n\"becker\",\n\"becket\",\n\"beckon\",\n\"beclad\",\n\"beclang\",\n\"beclart\",\n\"beclasp\",\n\"beclaw\",\n\"becloak\",\n\"beclog\",\n\"becloud\",\n\"beclout\",\n\"beclown\",\n\"becolme\",\n\"becolor\",\n\"become\",\n\"becomes\",\n\"becomma\",\n\"becoom\",\n\"becost\",\n\"becovet\",\n\"becram\",\n\"becramp\",\n\"becrawl\",\n\"becreep\",\n\"becrime\",\n\"becroak\",\n\"becross\",\n\"becrowd\",\n\"becrown\",\n\"becrush\",\n\"becrust\",\n\"becry\",\n\"becuiba\",\n\"becuna\",\n\"becurl\",\n\"becurry\",\n\"becurse\",\n\"becut\",\n\"bed\",\n\"bedad\",\n\"bedamn\",\n\"bedamp\",\n\"bedare\",\n\"bedark\",\n\"bedash\",\n\"bedaub\",\n\"bedawn\",\n\"beday\",\n\"bedaze\",\n\"bedbug\",\n\"bedcap\",\n\"bedcase\",\n\"bedcord\",\n\"bedded\",\n\"bedder\",\n\"bedding\",\n\"bedead\",\n\"bedeaf\",\n\"bedebt\",\n\"bedeck\",\n\"bedel\",\n\"beden\",\n\"bedene\",\n\"bedevil\",\n\"bedew\",\n\"bedewer\",\n\"bedfast\",\n\"bedfoot\",\n\"bedgery\",\n\"bedgoer\",\n\"bedgown\",\n\"bedight\",\n\"bedikah\",\n\"bedim\",\n\"bedin\",\n\"bedip\",\n\"bedirt\",\n\"bedirty\",\n\"bedizen\",\n\"bedkey\",\n\"bedlam\",\n\"bedlar\",\n\"bedless\",\n\"bedlids\",\n\"bedman\",\n\"bedmate\",\n\"bedog\",\n\"bedolt\",\n\"bedot\",\n\"bedote\",\n\"bedouse\",\n\"bedown\",\n\"bedoyo\",\n\"bedpan\",\n\"bedpost\",\n\"bedrail\",\n\"bedral\",\n\"bedrape\",\n\"bedress\",\n\"bedrid\",\n\"bedrift\",\n\"bedrip\",\n\"bedrock\",\n\"bedroll\",\n\"bedroom\",\n\"bedrop\",\n\"bedrown\",\n\"bedrug\",\n\"bedsick\",\n\"bedside\",\n\"bedsite\",\n\"bedsock\",\n\"bedsore\",\n\"bedtick\",\n\"bedtime\",\n\"bedub\",\n\"beduck\",\n\"beduke\",\n\"bedull\",\n\"bedumb\",\n\"bedunce\",\n\"bedunch\",\n\"bedung\",\n\"bedur\",\n\"bedusk\",\n\"bedust\",\n\"bedwarf\",\n\"bedway\",\n\"bedways\",\n\"bedwell\",\n\"bedye\",\n\"bee\",\n\"beearn\",\n\"beech\",\n\"beechen\",\n\"beechy\",\n\"beedged\",\n\"beedom\",\n\"beef\",\n\"beefer\",\n\"beefily\",\n\"beefin\",\n\"beefish\",\n\"beefy\",\n\"beehead\",\n\"beeherd\",\n\"beehive\",\n\"beeish\",\n\"beek\",\n\"beekite\",\n\"beelbow\",\n\"beelike\",\n\"beeline\",\n\"beelol\",\n\"beeman\",\n\"been\",\n\"beennut\",\n\"beer\",\n\"beerage\",\n\"beerily\",\n\"beerish\",\n\"beery\",\n\"bees\",\n\"beest\",\n\"beeswax\",\n\"beet\",\n\"beeth\",\n\"beetle\",\n\"beetled\",\n\"beetler\",\n\"beety\",\n\"beeve\",\n\"beevish\",\n\"beeware\",\n\"beeway\",\n\"beeweed\",\n\"beewise\",\n\"beewort\",\n\"befall\",\n\"befame\",\n\"befan\",\n\"befancy\",\n\"befavor\",\n\"befilch\",\n\"befile\",\n\"befilth\",\n\"befire\",\n\"befist\",\n\"befit\",\n\"beflag\",\n\"beflap\",\n\"beflea\",\n\"befleck\",\n\"beflour\",\n\"beflout\",\n\"beflum\",\n\"befoam\",\n\"befog\",\n\"befool\",\n\"befop\",\n\"before\",\n\"befoul\",\n\"befret\",\n\"befrill\",\n\"befriz\",\n\"befume\",\n\"beg\",\n\"begad\",\n\"begall\",\n\"begani\",\n\"begar\",\n\"begari\",\n\"begash\",\n\"begat\",\n\"begaud\",\n\"begaudy\",\n\"begay\",\n\"begaze\",\n\"begeck\",\n\"begem\",\n\"beget\",\n\"beggar\",\n\"beggary\",\n\"begging\",\n\"begift\",\n\"begild\",\n\"begin\",\n\"begird\",\n\"beglad\",\n\"beglare\",\n\"beglic\",\n\"beglide\",\n\"begloom\",\n\"begloze\",\n\"begluc\",\n\"beglue\",\n\"begnaw\",\n\"bego\",\n\"begob\",\n\"begobs\",\n\"begohm\",\n\"begone\",\n\"begonia\",\n\"begorra\",\n\"begorry\",\n\"begoud\",\n\"begowk\",\n\"begrace\",\n\"begrain\",\n\"begrave\",\n\"begray\",\n\"begreen\",\n\"begrett\",\n\"begrim\",\n\"begrime\",\n\"begroan\",\n\"begrown\",\n\"beguard\",\n\"beguess\",\n\"beguile\",\n\"beguine\",\n\"begulf\",\n\"begum\",\n\"begun\",\n\"begunk\",\n\"begut\",\n\"behale\",\n\"behalf\",\n\"behap\",\n\"behave\",\n\"behead\",\n\"behear\",\n\"behears\",\n\"behedge\",\n\"beheld\",\n\"behelp\",\n\"behen\",\n\"behenic\",\n\"behest\",\n\"behind\",\n\"behint\",\n\"behn\",\n\"behold\",\n\"behoney\",\n\"behoof\",\n\"behoot\",\n\"behoove\",\n\"behorn\",\n\"behowl\",\n\"behung\",\n\"behymn\",\n\"beice\",\n\"beige\",\n\"being\",\n\"beinked\",\n\"beira\",\n\"beisa\",\n\"bejade\",\n\"bejan\",\n\"bejant\",\n\"bejazz\",\n\"bejel\",\n\"bejewel\",\n\"bejig\",\n\"bekah\",\n\"bekick\",\n\"beking\",\n\"bekiss\",\n\"bekko\",\n\"beknave\",\n\"beknit\",\n\"beknow\",\n\"beknown\",\n\"bel\",\n\"bela\",\n\"belabor\",\n\"belaced\",\n\"beladle\",\n\"belady\",\n\"belage\",\n\"belah\",\n\"belam\",\n\"belanda\",\n\"belar\",\n\"belard\",\n\"belash\",\n\"belate\",\n\"belated\",\n\"belaud\",\n\"belay\",\n\"belayer\",\n\"belch\",\n\"belcher\",\n\"beld\",\n\"beldam\",\n\"beleaf\",\n\"beleap\",\n\"beleave\",\n\"belee\",\n\"belfry\",\n\"belga\",\n\"belibel\",\n\"belick\",\n\"belie\",\n\"belief\",\n\"belier\",\n\"believe\",\n\"belight\",\n\"beliked\",\n\"belion\",\n\"belite\",\n\"belive\",\n\"bell\",\n\"bellboy\",\n\"belle\",\n\"belled\",\n\"bellhop\",\n\"bellied\",\n\"belling\",\n\"bellite\",\n\"bellman\",\n\"bellote\",\n\"bellow\",\n\"bellows\",\n\"belly\",\n\"bellyer\",\n\"beloam\",\n\"beloid\",\n\"belong\",\n\"belonid\",\n\"belord\",\n\"belout\",\n\"belove\",\n\"beloved\",\n\"below\",\n\"belsire\",\n\"belt\",\n\"belted\",\n\"belter\",\n\"beltie\",\n\"beltine\",\n\"belting\",\n\"beltman\",\n\"belton\",\n\"beluga\",\n\"belute\",\n\"belve\",\n\"bely\",\n\"belying\",\n\"bema\",\n\"bemad\",\n\"bemadam\",\n\"bemail\",\n\"bemaim\",\n\"beman\",\n\"bemar\",\n\"bemask\",\n\"bemat\",\n\"bemata\",\n\"bemaul\",\n\"bemazed\",\n\"bemeal\",\n\"bemean\",\n\"bemercy\",\n\"bemire\",\n\"bemist\",\n\"bemix\",\n\"bemoan\",\n\"bemoat\",\n\"bemock\",\n\"bemoil\",\n\"bemole\",\n\"bemolt\",\n\"bemoon\",\n\"bemotto\",\n\"bemoult\",\n\"bemouth\",\n\"bemuck\",\n\"bemud\",\n\"bemuddy\",\n\"bemuse\",\n\"bemused\",\n\"bemusk\",\n\"ben\",\n\"bena\",\n\"benab\",\n\"bename\",\n\"benami\",\n\"benasty\",\n\"benben\",\n\"bench\",\n\"bencher\",\n\"benchy\",\n\"bencite\",\n\"bend\",\n\"benda\",\n\"bended\",\n\"bender\",\n\"bending\",\n\"bendlet\",\n\"bendy\",\n\"bene\",\n\"beneath\",\n\"benefic\",\n\"benefit\",\n\"benempt\",\n\"benet\",\n\"beng\",\n\"beni\",\n\"benight\",\n\"benign\",\n\"benison\",\n\"benj\",\n\"benjy\",\n\"benmost\",\n\"benn\",\n\"benne\",\n\"bennel\",\n\"bennet\",\n\"benny\",\n\"beno\",\n\"benorth\",\n\"benote\",\n\"bensel\",\n\"bensh\",\n\"benshea\",\n\"benshee\",\n\"benshi\",\n\"bent\",\n\"bentang\",\n\"benthal\",\n\"benthic\",\n\"benthon\",\n\"benthos\",\n\"benting\",\n\"benty\",\n\"benumb\",\n\"benward\",\n\"benweed\",\n\"benzal\",\n\"benzein\",\n\"benzene\",\n\"benzil\",\n\"benzine\",\n\"benzo\",\n\"benzoic\",\n\"benzoid\",\n\"benzoin\",\n\"benzol\",\n\"benzole\",\n\"benzoxy\",\n\"benzoyl\",\n\"benzyl\",\n\"beode\",\n\"bepaid\",\n\"bepale\",\n\"bepaper\",\n\"beparch\",\n\"beparse\",\n\"bepart\",\n\"bepaste\",\n\"bepat\",\n\"bepaw\",\n\"bepearl\",\n\"bepelt\",\n\"bepen\",\n\"bepewed\",\n\"bepiece\",\n\"bepile\",\n\"bepill\",\n\"bepinch\",\n\"bepity\",\n\"beprank\",\n\"bepray\",\n\"bepress\",\n\"bepride\",\n\"beprose\",\n\"bepuff\",\n\"bepun\",\n\"bequalm\",\n\"bequest\",\n\"bequote\",\n\"ber\",\n\"berain\",\n\"berakah\",\n\"berake\",\n\"berapt\",\n\"berat\",\n\"berate\",\n\"beray\",\n\"bere\",\n\"bereave\",\n\"bereft\",\n\"berend\",\n\"beret\",\n\"berg\",\n\"berger\",\n\"berglet\",\n\"bergut\",\n\"bergy\",\n\"bergylt\",\n\"berhyme\",\n\"beride\",\n\"berinse\",\n\"berith\",\n\"berley\",\n\"berlin\",\n\"berline\",\n\"berm\",\n\"berne\",\n\"berobed\",\n\"beroll\",\n\"beround\",\n\"berret\",\n\"berri\",\n\"berried\",\n\"berrier\",\n\"berry\",\n\"berseem\",\n\"berserk\",\n\"berth\",\n\"berthed\",\n\"berther\",\n\"bertram\",\n\"bertrum\",\n\"berust\",\n\"bervie\",\n\"berycid\",\n\"beryl\",\n\"bes\",\n\"besa\",\n\"besagne\",\n\"besaiel\",\n\"besaint\",\n\"besan\",\n\"besauce\",\n\"bescab\",\n\"bescarf\",\n\"bescent\",\n\"bescorn\",\n\"bescour\",\n\"bescurf\",\n\"beseam\",\n\"besee\",\n\"beseech\",\n\"beseem\",\n\"beseen\",\n\"beset\",\n\"beshade\",\n\"beshag\",\n\"beshake\",\n\"beshame\",\n\"beshear\",\n\"beshell\",\n\"beshine\",\n\"beshlik\",\n\"beshod\",\n\"beshout\",\n\"beshow\",\n\"beshrew\",\n\"beside\",\n\"besides\",\n\"besiege\",\n\"besigh\",\n\"besin\",\n\"besing\",\n\"besiren\",\n\"besit\",\n\"beslab\",\n\"beslap\",\n\"beslash\",\n\"beslave\",\n\"beslime\",\n\"beslow\",\n\"beslur\",\n\"besmear\",\n\"besmell\",\n\"besmile\",\n\"besmoke\",\n\"besmut\",\n\"besnare\",\n\"besneer\",\n\"besnow\",\n\"besnuff\",\n\"besogne\",\n\"besoil\",\n\"besom\",\n\"besomer\",\n\"besoot\",\n\"besot\",\n\"besoul\",\n\"besour\",\n\"bespate\",\n\"bespawl\",\n\"bespeak\",\n\"besped\",\n\"bespeed\",\n\"bespell\",\n\"bespend\",\n\"bespete\",\n\"bespew\",\n\"bespice\",\n\"bespill\",\n\"bespin\",\n\"bespit\",\n\"besplit\",\n\"bespoke\",\n\"bespot\",\n\"bespout\",\n\"bespray\",\n\"bespy\",\n\"besquib\",\n\"besra\",\n\"best\",\n\"bestab\",\n\"bestain\",\n\"bestamp\",\n\"bestar\",\n\"bestare\",\n\"bestay\",\n\"bestead\",\n\"besteer\",\n\"bester\",\n\"bestial\",\n\"bestick\",\n\"bestill\",\n\"bestink\",\n\"bestir\",\n\"bestock\",\n\"bestore\",\n\"bestorm\",\n\"bestove\",\n\"bestow\",\n\"bestraw\",\n\"bestrew\",\n\"bestuck\",\n\"bestud\",\n\"besugar\",\n\"besuit\",\n\"besully\",\n\"beswarm\",\n\"beswim\",\n\"bet\",\n\"beta\",\n\"betag\",\n\"betail\",\n\"betaine\",\n\"betalk\",\n\"betask\",\n\"betaxed\",\n\"betear\",\n\"beteela\",\n\"beteem\",\n\"betel\",\n\"beth\",\n\"bethel\",\n\"bethink\",\n\"bethumb\",\n\"bethump\",\n\"betide\",\n\"betimes\",\n\"betinge\",\n\"betire\",\n\"betis\",\n\"betitle\",\n\"betoil\",\n\"betoken\",\n\"betone\",\n\"betony\",\n\"betoss\",\n\"betowel\",\n\"betrace\",\n\"betrail\",\n\"betrap\",\n\"betray\",\n\"betread\",\n\"betrend\",\n\"betrim\",\n\"betroth\",\n\"betrunk\",\n\"betso\",\n\"betted\",\n\"better\",\n\"betters\",\n\"betting\",\n\"bettong\",\n\"bettor\",\n\"betty\",\n\"betulin\",\n\"betutor\",\n\"between\",\n\"betwine\",\n\"betwit\",\n\"betwixt\",\n\"beveil\",\n\"bevel\",\n\"beveled\",\n\"beveler\",\n\"bevenom\",\n\"bever\",\n\"beverse\",\n\"beveto\",\n\"bevined\",\n\"bevomit\",\n\"bevue\",\n\"bevy\",\n\"bewail\",\n\"bewall\",\n\"beware\",\n\"bewash\",\n\"bewaste\",\n\"bewater\",\n\"beweary\",\n\"beweep\",\n\"bewept\",\n\"bewest\",\n\"bewet\",\n\"bewhig\",\n\"bewhite\",\n\"bewidow\",\n\"bewig\",\n\"bewired\",\n\"bewitch\",\n\"bewith\",\n\"bework\",\n\"beworm\",\n\"beworn\",\n\"beworry\",\n\"bewrap\",\n\"bewray\",\n\"bewreck\",\n\"bewrite\",\n\"bey\",\n\"beydom\",\n\"beylic\",\n\"beyond\",\n\"beyship\",\n\"bezant\",\n\"bezanty\",\n\"bezel\",\n\"bezetta\",\n\"bezique\",\n\"bezoar\",\n\"bezzi\",\n\"bezzle\",\n\"bezzo\",\n\"bhabar\",\n\"bhakta\",\n\"bhakti\",\n\"bhalu\",\n\"bhandar\",\n\"bhang\",\n\"bhangi\",\n\"bhara\",\n\"bharal\",\n\"bhat\",\n\"bhava\",\n\"bheesty\",\n\"bhikku\",\n\"bhikshu\",\n\"bhoosa\",\n\"bhoy\",\n\"bhungi\",\n\"bhut\",\n\"biabo\",\n\"biacid\",\n\"biacuru\",\n\"bialate\",\n\"biallyl\",\n\"bianco\",\n\"biarchy\",\n\"bias\",\n\"biaxal\",\n\"biaxial\",\n\"bib\",\n\"bibasic\",\n\"bibb\",\n\"bibber\",\n\"bibble\",\n\"bibbler\",\n\"bibbons\",\n\"bibcock\",\n\"bibi\",\n\"bibiri\",\n\"bibless\",\n\"biblus\",\n\"bice\",\n\"biceps\",\n\"bicetyl\",\n\"bichir\",\n\"bichord\",\n\"bichy\",\n\"bick\",\n\"bicker\",\n\"bickern\",\n\"bicolor\",\n\"bicone\",\n\"biconic\",\n\"bicorn\",\n\"bicorne\",\n\"bicron\",\n\"bicycle\",\n\"bicyclo\",\n\"bid\",\n\"bidar\",\n\"bidarka\",\n\"bidcock\",\n\"bidder\",\n\"bidding\",\n\"biddy\",\n\"bide\",\n\"bident\",\n\"bider\",\n\"bidet\",\n\"biding\",\n\"bidri\",\n\"biduous\",\n\"bield\",\n\"bieldy\",\n\"bien\",\n\"bienly\",\n\"biennia\",\n\"bier\",\n\"bietle\",\n\"bifara\",\n\"bifer\",\n\"biff\",\n\"biffin\",\n\"bifid\",\n\"bifidly\",\n\"bifilar\",\n\"biflex\",\n\"bifocal\",\n\"bifoil\",\n\"bifold\",\n\"bifolia\",\n\"biform\",\n\"bifront\",\n\"big\",\n\"biga\",\n\"bigamic\",\n\"bigamy\",\n\"bigener\",\n\"bigeye\",\n\"bigg\",\n\"biggah\",\n\"biggen\",\n\"bigger\",\n\"biggest\",\n\"biggin\",\n\"biggish\",\n\"bigha\",\n\"bighead\",\n\"bighorn\",\n\"bight\",\n\"biglot\",\n\"bigness\",\n\"bignou\",\n\"bigot\",\n\"bigoted\",\n\"bigotry\",\n\"bigotty\",\n\"bigroot\",\n\"bigwig\",\n\"bija\",\n\"bijasal\",\n\"bijou\",\n\"bijoux\",\n\"bike\",\n\"bikh\",\n\"bikini\",\n\"bilabe\",\n\"bilalo\",\n\"bilbie\",\n\"bilbo\",\n\"bilby\",\n\"bilch\",\n\"bilcock\",\n\"bildar\",\n\"bilders\",\n\"bile\",\n\"bilge\",\n\"bilgy\",\n\"biliary\",\n\"biliate\",\n\"bilic\",\n\"bilify\",\n\"bilimbi\",\n\"bilio\",\n\"bilious\",\n\"bilith\",\n\"bilk\",\n\"bilker\",\n\"bill\",\n\"billa\",\n\"billbug\",\n\"billed\",\n\"biller\",\n\"billet\",\n\"billety\",\n\"billian\",\n\"billing\",\n\"billion\",\n\"billman\",\n\"billon\",\n\"billot\",\n\"billow\",\n\"billowy\",\n\"billy\",\n\"billyer\",\n\"bilo\",\n\"bilobe\",\n\"bilobed\",\n\"bilsh\",\n\"bilsted\",\n\"biltong\",\n\"bimalar\",\n\"bimanal\",\n\"bimane\",\n\"bimasty\",\n\"bimbil\",\n\"bimeby\",\n\"bimodal\",\n\"bin\",\n\"binal\",\n\"binary\",\n\"binate\",\n\"bind\",\n\"binder\",\n\"bindery\",\n\"binding\",\n\"bindle\",\n\"bindlet\",\n\"bindweb\",\n\"bine\",\n\"bing\",\n\"binge\",\n\"bingey\",\n\"binghi\",\n\"bingle\",\n\"bingo\",\n\"bingy\",\n\"binh\",\n\"bink\",\n\"binman\",\n\"binna\",\n\"binning\",\n\"binnite\",\n\"bino\",\n\"binocle\",\n\"binodal\",\n\"binode\",\n\"binotic\",\n\"binous\",\n\"bint\",\n\"binukau\",\n\"biod\",\n\"biodyne\",\n\"biogen\",\n\"biogeny\",\n\"bioherm\",\n\"biolith\",\n\"biology\",\n\"biome\",\n\"bion\",\n\"bionomy\",\n\"biopsic\",\n\"biopsy\",\n\"bioral\",\n\"biorgan\",\n\"bios\",\n\"biose\",\n\"biosis\",\n\"biota\",\n\"biotaxy\",\n\"biotic\",\n\"biotics\",\n\"biotin\",\n\"biotite\",\n\"biotome\",\n\"biotomy\",\n\"biotope\",\n\"biotype\",\n\"bioxide\",\n\"bipack\",\n\"biparty\",\n\"biped\",\n\"bipedal\",\n\"biphase\",\n\"biplane\",\n\"bipod\",\n\"bipolar\",\n\"biprism\",\n\"biprong\",\n\"birch\",\n\"birchen\",\n\"bird\",\n\"birddom\",\n\"birdeen\",\n\"birder\",\n\"birdie\",\n\"birding\",\n\"birdlet\",\n\"birdman\",\n\"birdy\",\n\"bireme\",\n\"biretta\",\n\"biri\",\n\"biriba\",\n\"birk\",\n\"birken\",\n\"birkie\",\n\"birl\",\n\"birle\",\n\"birler\",\n\"birlie\",\n\"birlinn\",\n\"birma\",\n\"birn\",\n\"birny\",\n\"birr\",\n\"birse\",\n\"birsle\",\n\"birsy\",\n\"birth\",\n\"birthy\",\n\"bis\",\n\"bisabol\",\n\"bisalt\",\n\"biscuit\",\n\"bisect\",\n\"bisexed\",\n\"bisext\",\n\"bishop\",\n\"bismar\",\n\"bismite\",\n\"bismuth\",\n\"bisnaga\",\n\"bison\",\n\"bispore\",\n\"bisque\",\n\"bissext\",\n\"bisson\",\n\"bistate\",\n\"bister\",\n\"bisti\",\n\"bistort\",\n\"bistro\",\n\"bit\",\n\"bitable\",\n\"bitch\",\n\"bite\",\n\"biter\",\n\"biti\",\n\"biting\",\n\"bitless\",\n\"bito\",\n\"bitolyl\",\n\"bitt\",\n\"bitted\",\n\"bitten\",\n\"bitter\",\n\"bittern\",\n\"bitters\",\n\"bittie\",\n\"bittock\",\n\"bitty\",\n\"bitume\",\n\"bitumed\",\n\"bitumen\",\n\"bitwise\",\n\"bityite\",\n\"bitypic\",\n\"biune\",\n\"biunial\",\n\"biunity\",\n\"biurate\",\n\"biurea\",\n\"biuret\",\n\"bivalve\",\n\"bivinyl\",\n\"bivious\",\n\"bivocal\",\n\"bivouac\",\n\"biwa\",\n\"bixin\",\n\"biz\",\n\"bizarre\",\n\"bizet\",\n\"bizonal\",\n\"bizone\",\n\"bizz\",\n\"blab\",\n\"blabber\",\n\"black\",\n\"blacken\",\n\"blacker\",\n\"blackey\",\n\"blackie\",\n\"blackit\",\n\"blackly\",\n\"blacky\",\n\"blad\",\n\"bladder\",\n\"blade\",\n\"bladed\",\n\"blader\",\n\"blading\",\n\"bladish\",\n\"blady\",\n\"blae\",\n\"blaff\",\n\"blaflum\",\n\"blah\",\n\"blain\",\n\"blair\",\n\"blake\",\n\"blame\",\n\"blamed\",\n\"blamer\",\n\"blaming\",\n\"blan\",\n\"blanc\",\n\"blanca\",\n\"blanch\",\n\"blanco\",\n\"bland\",\n\"blanda\",\n\"blandly\",\n\"blank\",\n\"blanked\",\n\"blanket\",\n\"blankly\",\n\"blanky\",\n\"blanque\",\n\"blare\",\n\"blarney\",\n\"blarnid\",\n\"blarny\",\n\"blart\",\n\"blas\",\n\"blase\",\n\"blash\",\n\"blashy\",\n\"blast\",\n\"blasted\",\n\"blaster\",\n\"blastid\",\n\"blastie\",\n\"blasty\",\n\"blat\",\n\"blatant\",\n\"blate\",\n\"blately\",\n\"blather\",\n\"blatta\",\n\"blatter\",\n\"blatti\",\n\"blattid\",\n\"blaubok\",\n\"blaver\",\n\"blaw\",\n\"blawort\",\n\"blay\",\n\"blaze\",\n\"blazer\",\n\"blazing\",\n\"blazon\",\n\"blazy\",\n\"bleach\",\n\"bleak\",\n\"bleakly\",\n\"bleaky\",\n\"blear\",\n\"bleared\",\n\"bleary\",\n\"bleat\",\n\"bleater\",\n\"bleaty\",\n\"bleb\",\n\"blebby\",\n\"bleck\",\n\"blee\",\n\"bleed\",\n\"bleeder\",\n\"bleery\",\n\"bleeze\",\n\"bleezy\",\n\"blellum\",\n\"blemish\",\n\"blench\",\n\"blend\",\n\"blende\",\n\"blended\",\n\"blender\",\n\"blendor\",\n\"blenny\",\n\"blent\",\n\"bleo\",\n\"blesbok\",\n\"bless\",\n\"blessed\",\n\"blesser\",\n\"blest\",\n\"blet\",\n\"blewits\",\n\"blibe\",\n\"blick\",\n\"blickey\",\n\"blight\",\n\"blighty\",\n\"blimp\",\n\"blimy\",\n\"blind\",\n\"blinded\",\n\"blinder\",\n\"blindly\",\n\"blink\",\n\"blinked\",\n\"blinker\",\n\"blinks\",\n\"blinky\",\n\"blinter\",\n\"blintze\",\n\"blip\",\n\"bliss\",\n\"blissom\",\n\"blister\",\n\"blite\",\n\"blithe\",\n\"blithen\",\n\"blither\",\n\"blitter\",\n\"blitz\",\n\"blizz\",\n\"blo\",\n\"bloat\",\n\"bloated\",\n\"bloater\",\n\"blob\",\n\"blobbed\",\n\"blobber\",\n\"blobby\",\n\"bloc\",\n\"block\",\n\"blocked\",\n\"blocker\",\n\"blocky\",\n\"blodite\",\n\"bloke\",\n\"blolly\",\n\"blonde\",\n\"blood\",\n\"blooded\",\n\"bloody\",\n\"blooey\",\n\"bloom\",\n\"bloomer\",\n\"bloomy\",\n\"bloop\",\n\"blooper\",\n\"blore\",\n\"blosmy\",\n\"blossom\",\n\"blot\",\n\"blotch\",\n\"blotchy\",\n\"blotter\",\n\"blotto\",\n\"blotty\",\n\"blouse\",\n\"bloused\",\n\"blout\",\n\"blow\",\n\"blowen\",\n\"blower\",\n\"blowfly\",\n\"blowgun\",\n\"blowing\",\n\"blown\",\n\"blowoff\",\n\"blowout\",\n\"blowth\",\n\"blowup\",\n\"blowy\",\n\"blowze\",\n\"blowzed\",\n\"blowzy\",\n\"blub\",\n\"blubber\",\n\"blucher\",\n\"blue\",\n\"bluecap\",\n\"bluecup\",\n\"blueing\",\n\"blueleg\",\n\"bluely\",\n\"bluer\",\n\"blues\",\n\"bluet\",\n\"bluetop\",\n\"bluey\",\n\"bluff\",\n\"bluffer\",\n\"bluffly\",\n\"bluffy\",\n\"bluggy\",\n\"bluing\",\n\"bluish\",\n\"bluism\",\n\"blunder\",\n\"blunge\",\n\"blunger\",\n\"blunk\",\n\"blunker\",\n\"blunks\",\n\"blunnen\",\n\"blunt\",\n\"blunter\",\n\"bluntie\",\n\"bluntly\",\n\"blup\",\n\"blur\",\n\"blurb\",\n\"blurred\",\n\"blurrer\",\n\"blurry\",\n\"blurt\",\n\"blush\",\n\"blusher\",\n\"blushy\",\n\"bluster\",\n\"blype\",\n\"bo\",\n\"boa\",\n\"boagane\",\n\"boar\",\n\"board\",\n\"boarder\",\n\"boardly\",\n\"boardy\",\n\"boarish\",\n\"boast\",\n\"boaster\",\n\"boat\",\n\"boatage\",\n\"boater\",\n\"boatful\",\n\"boatie\",\n\"boating\",\n\"boatlip\",\n\"boatly\",\n\"boatman\",\n\"bob\",\n\"boba\",\n\"bobac\",\n\"bobbed\",\n\"bobber\",\n\"bobbery\",\n\"bobbin\",\n\"bobbing\",\n\"bobbish\",\n\"bobble\",\n\"bobby\",\n\"bobcat\",\n\"bobcoat\",\n\"bobeche\",\n\"bobfly\",\n\"bobo\",\n\"bobotie\",\n\"bobsled\",\n\"bobstay\",\n\"bobtail\",\n\"bobwood\",\n\"bocal\",\n\"bocardo\",\n\"bocca\",\n\"boccale\",\n\"boccaro\",\n\"bocce\",\n\"boce\",\n\"bocher\",\n\"bock\",\n\"bocking\",\n\"bocoy\",\n\"bod\",\n\"bodach\",\n\"bode\",\n\"bodeful\",\n\"bodega\",\n\"boden\",\n\"boder\",\n\"bodge\",\n\"bodger\",\n\"bodgery\",\n\"bodhi\",\n\"bodice\",\n\"bodiced\",\n\"bodied\",\n\"bodier\",\n\"bodikin\",\n\"bodily\",\n\"boding\",\n\"bodkin\",\n\"bodle\",\n\"bodock\",\n\"body\",\n\"bog\",\n\"boga\",\n\"bogan\",\n\"bogard\",\n\"bogart\",\n\"bogey\",\n\"boggart\",\n\"boggin\",\n\"boggish\",\n\"boggle\",\n\"boggler\",\n\"boggy\",\n\"boghole\",\n\"bogie\",\n\"bogier\",\n\"bogland\",\n\"bogle\",\n\"boglet\",\n\"bogman\",\n\"bogmire\",\n\"bogo\",\n\"bogong\",\n\"bogtrot\",\n\"bogue\",\n\"bogum\",\n\"bogus\",\n\"bogway\",\n\"bogwood\",\n\"bogwort\",\n\"bogy\",\n\"bogydom\",\n\"bogyism\",\n\"bohawn\",\n\"bohea\",\n\"boho\",\n\"bohor\",\n\"bohunk\",\n\"boid\",\n\"boil\",\n\"boiled\",\n\"boiler\",\n\"boilery\",\n\"boiling\",\n\"boily\",\n\"boist\",\n\"bojite\",\n\"bojo\",\n\"bokadam\",\n\"bokard\",\n\"bokark\",\n\"boke\",\n\"bokom\",\n\"bola\",\n\"bolar\",\n\"bold\",\n\"bolden\",\n\"boldine\",\n\"boldly\",\n\"boldo\",\n\"bole\",\n\"boled\",\n\"boleite\",\n\"bolero\",\n\"bolete\",\n\"bolide\",\n\"bolimba\",\n\"bolis\",\n\"bolivar\",\n\"bolivia\",\n\"bolk\",\n\"boll\",\n\"bollard\",\n\"bolled\",\n\"boller\",\n\"bolling\",\n\"bollock\",\n\"bolly\",\n\"bolo\",\n\"boloman\",\n\"boloney\",\n\"bolson\",\n\"bolster\",\n\"bolt\",\n\"boltage\",\n\"boltant\",\n\"boltel\",\n\"bolter\",\n\"bolti\",\n\"bolting\",\n\"bolus\",\n\"bom\",\n\"boma\",\n\"bomb\",\n\"bombard\",\n\"bombast\",\n\"bombed\",\n\"bomber\",\n\"bombo\",\n\"bombola\",\n\"bombous\",\n\"bon\",\n\"bonaci\",\n\"bonagh\",\n\"bonaght\",\n\"bonair\",\n\"bonally\",\n\"bonang\",\n\"bonanza\",\n\"bonasus\",\n\"bonbon\",\n\"bonce\",\n\"bond\",\n\"bondage\",\n\"bondar\",\n\"bonded\",\n\"bonder\",\n\"bonding\",\n\"bondman\",\n\"bonduc\",\n\"bone\",\n\"boned\",\n\"bonedog\",\n\"bonelet\",\n\"boner\",\n\"boneset\",\n\"bonfire\",\n\"bong\",\n\"bongo\",\n\"boniata\",\n\"bonify\",\n\"bonito\",\n\"bonk\",\n\"bonnaz\",\n\"bonnet\",\n\"bonnily\",\n\"bonny\",\n\"bonsai\",\n\"bonus\",\n\"bonxie\",\n\"bony\",\n\"bonze\",\n\"bonzer\",\n\"bonzery\",\n\"bonzian\",\n\"boo\",\n\"boob\",\n\"boobery\",\n\"boobily\",\n\"boobook\",\n\"booby\",\n\"bood\",\n\"boodie\",\n\"boodle\",\n\"boodler\",\n\"boody\",\n\"boof\",\n\"booger\",\n\"boohoo\",\n\"boojum\",\n\"book\",\n\"bookdom\",\n\"booked\",\n\"booker\",\n\"bookery\",\n\"bookful\",\n\"bookie\",\n\"booking\",\n\"bookish\",\n\"bookism\",\n\"booklet\",\n\"bookman\",\n\"booky\",\n\"bool\",\n\"booly\",\n\"boolya\",\n\"boom\",\n\"boomage\",\n\"boomah\",\n\"boomdas\",\n\"boomer\",\n\"booming\",\n\"boomlet\",\n\"boomy\",\n\"boon\",\n\"boonk\",\n\"boopis\",\n\"boor\",\n\"boorish\",\n\"boort\",\n\"boose\",\n\"boost\",\n\"booster\",\n\"boosy\",\n\"boot\",\n\"bootboy\",\n\"booted\",\n\"bootee\",\n\"booter\",\n\"bootery\",\n\"bootful\",\n\"booth\",\n\"boother\",\n\"bootied\",\n\"booting\",\n\"bootleg\",\n\"boots\",\n\"booty\",\n\"booze\",\n\"boozed\",\n\"boozer\",\n\"boozily\",\n\"boozy\",\n\"bop\",\n\"bopeep\",\n\"boppist\",\n\"bopyrid\",\n\"bor\",\n\"bora\",\n\"borable\",\n\"boracic\",\n\"borage\",\n\"borak\",\n\"boral\",\n\"borasca\",\n\"borate\",\n\"borax\",\n\"bord\",\n\"bordage\",\n\"bordar\",\n\"bordel\",\n\"border\",\n\"bordure\",\n\"bore\",\n\"boread\",\n\"boreal\",\n\"borean\",\n\"boredom\",\n\"boree\",\n\"boreen\",\n\"boregat\",\n\"boreism\",\n\"borele\",\n\"borer\",\n\"borg\",\n\"borgh\",\n\"borh\",\n\"boric\",\n\"boride\",\n\"borine\",\n\"boring\",\n\"borish\",\n\"borism\",\n\"bority\",\n\"borize\",\n\"borlase\",\n\"born\",\n\"borne\",\n\"borneol\",\n\"borning\",\n\"bornite\",\n\"bornyl\",\n\"boro\",\n\"boron\",\n\"boronic\",\n\"borough\",\n\"borrel\",\n\"borrow\",\n\"borsch\",\n\"borscht\",\n\"borsht\",\n\"bort\",\n\"bortsch\",\n\"borty\",\n\"bortz\",\n\"borwort\",\n\"boryl\",\n\"borzoi\",\n\"boscage\",\n\"bosch\",\n\"bose\",\n\"boser\",\n\"bosh\",\n\"bosher\",\n\"bosk\",\n\"bosker\",\n\"bosket\",\n\"bosky\",\n\"bosn\",\n\"bosom\",\n\"bosomed\",\n\"bosomer\",\n\"bosomy\",\n\"boss\",\n\"bossage\",\n\"bossdom\",\n\"bossed\",\n\"bosser\",\n\"bosset\",\n\"bossing\",\n\"bossism\",\n\"bosslet\",\n\"bossy\",\n\"boston\",\n\"bostryx\",\n\"bosun\",\n\"bot\",\n\"bota\",\n\"botanic\",\n\"botany\",\n\"botargo\",\n\"botch\",\n\"botched\",\n\"botcher\",\n\"botchka\",\n\"botchy\",\n\"bote\",\n\"botella\",\n\"boterol\",\n\"botfly\",\n\"both\",\n\"bother\",\n\"bothros\",\n\"bothway\",\n\"bothy\",\n\"botonee\",\n\"botong\",\n\"bott\",\n\"bottine\",\n\"bottle\",\n\"bottled\",\n\"bottler\",\n\"bottom\",\n\"botulin\",\n\"bouchal\",\n\"bouche\",\n\"boucher\",\n\"boud\",\n\"boudoir\",\n\"bougar\",\n\"bouge\",\n\"bouget\",\n\"bough\",\n\"boughed\",\n\"bought\",\n\"boughy\",\n\"bougie\",\n\"bouk\",\n\"boukit\",\n\"boulder\",\n\"boule\",\n\"boultel\",\n\"boulter\",\n\"boun\",\n\"bounce\",\n\"bouncer\",\n\"bound\",\n\"bounded\",\n\"bounden\",\n\"bounder\",\n\"boundly\",\n\"bounty\",\n\"bouquet\",\n\"bourbon\",\n\"bourd\",\n\"bourder\",\n\"bourdon\",\n\"bourg\",\n\"bourn\",\n\"bourock\",\n\"bourse\",\n\"bouse\",\n\"bouser\",\n\"bousy\",\n\"bout\",\n\"boutade\",\n\"bouto\",\n\"bouw\",\n\"bovate\",\n\"bovid\",\n\"bovine\",\n\"bovoid\",\n\"bow\",\n\"bowable\",\n\"bowback\",\n\"bowbent\",\n\"bowboy\",\n\"bowed\",\n\"bowel\",\n\"boweled\",\n\"bowels\",\n\"bower\",\n\"bowery\",\n\"bowet\",\n\"bowfin\",\n\"bowhead\",\n\"bowie\",\n\"bowing\",\n\"bowk\",\n\"bowkail\",\n\"bowker\",\n\"bowknot\",\n\"bowl\",\n\"bowla\",\n\"bowleg\",\n\"bowler\",\n\"bowless\",\n\"bowlful\",\n\"bowlike\",\n\"bowline\",\n\"bowling\",\n\"bowls\",\n\"bowly\",\n\"bowman\",\n\"bowpin\",\n\"bowshot\",\n\"bowwood\",\n\"bowwort\",\n\"bowwow\",\n\"bowyer\",\n\"boxbush\",\n\"boxcar\",\n\"boxen\",\n\"boxer\",\n\"boxfish\",\n\"boxful\",\n\"boxhaul\",\n\"boxhead\",\n\"boxing\",\n\"boxlike\",\n\"boxman\",\n\"boxty\",\n\"boxwood\",\n\"boxwork\",\n\"boxy\",\n\"boy\",\n\"boyang\",\n\"boyar\",\n\"boyard\",\n\"boycott\",\n\"boydom\",\n\"boyer\",\n\"boyhood\",\n\"boyish\",\n\"boyism\",\n\"boyla\",\n\"boylike\",\n\"boyship\",\n\"boza\",\n\"bozal\",\n\"bozo\",\n\"bozze\",\n\"bra\",\n\"brab\",\n\"brabant\",\n\"brabble\",\n\"braca\",\n\"braccia\",\n\"braccio\",\n\"brace\",\n\"braced\",\n\"bracer\",\n\"bracero\",\n\"braces\",\n\"brach\",\n\"brachet\",\n\"bracing\",\n\"brack\",\n\"bracken\",\n\"bracker\",\n\"bracket\",\n\"bracky\",\n\"bract\",\n\"bractea\",\n\"bracted\",\n\"brad\",\n\"bradawl\",\n\"bradsot\",\n\"brae\",\n\"braeman\",\n\"brag\",\n\"braggat\",\n\"bragger\",\n\"bragget\",\n\"bragite\",\n\"braid\",\n\"braided\",\n\"braider\",\n\"brail\",\n\"brain\",\n\"brainer\",\n\"brainge\",\n\"brains\",\n\"brainy\",\n\"braird\",\n\"brairo\",\n\"braise\",\n\"brake\",\n\"braker\",\n\"brakie\",\n\"braky\",\n\"bramble\",\n\"brambly\",\n\"bran\",\n\"branch\",\n\"branchi\",\n\"branchy\",\n\"brand\",\n\"branded\",\n\"brander\",\n\"brandy\",\n\"brangle\",\n\"branial\",\n\"brank\",\n\"brankie\",\n\"branle\",\n\"branner\",\n\"branny\",\n\"bransle\",\n\"brant\",\n\"brash\",\n\"brashy\",\n\"brasque\",\n\"brass\",\n\"brasse\",\n\"brasser\",\n\"brasset\",\n\"brassic\",\n\"brassie\",\n\"brassy\",\n\"brat\",\n\"brattie\",\n\"brattle\",\n\"brauna\",\n\"bravade\",\n\"bravado\",\n\"brave\",\n\"bravely\",\n\"braver\",\n\"bravery\",\n\"braving\",\n\"bravish\",\n\"bravo\",\n\"bravura\",\n\"braw\",\n\"brawl\",\n\"brawler\",\n\"brawly\",\n\"brawlys\",\n\"brawn\",\n\"brawned\",\n\"brawner\",\n\"brawny\",\n\"braws\",\n\"braxy\",\n\"bray\",\n\"brayer\",\n\"brayera\",\n\"braza\",\n\"braze\",\n\"brazen\",\n\"brazer\",\n\"brazera\",\n\"brazier\",\n\"brazil\",\n\"breach\",\n\"breachy\",\n\"bread\",\n\"breaden\",\n\"breadth\",\n\"breaghe\",\n\"break\",\n\"breakax\",\n\"breaker\",\n\"breakup\",\n\"bream\",\n\"breards\",\n\"breast\",\n\"breath\",\n\"breathe\",\n\"breathy\",\n\"breba\",\n\"breccia\",\n\"brecham\",\n\"breck\",\n\"brecken\",\n\"bred\",\n\"brede\",\n\"bredi\",\n\"bree\",\n\"breech\",\n\"breed\",\n\"breeder\",\n\"breedy\",\n\"breek\",\n\"breeze\",\n\"breezy\",\n\"bregma\",\n\"brehon\",\n\"brei\",\n\"brekkle\",\n\"brelaw\",\n\"breme\",\n\"bremely\",\n\"brent\",\n\"brephic\",\n\"bret\",\n\"breth\",\n\"brett\",\n\"breva\",\n\"breve\",\n\"brevet\",\n\"brevier\",\n\"brevit\",\n\"brevity\",\n\"brew\",\n\"brewage\",\n\"brewer\",\n\"brewery\",\n\"brewing\",\n\"brewis\",\n\"brewst\",\n\"brey\",\n\"briar\",\n\"bribe\",\n\"bribee\",\n\"briber\",\n\"bribery\",\n\"brichen\",\n\"brick\",\n\"brickel\",\n\"bricken\",\n\"brickle\",\n\"brickly\",\n\"bricky\",\n\"bricole\",\n\"bridal\",\n\"bridale\",\n\"bride\",\n\"bridely\",\n\"bridge\",\n\"bridged\",\n\"bridger\",\n\"bridle\",\n\"bridled\",\n\"bridler\",\n\"bridoon\",\n\"brief\",\n\"briefly\",\n\"briefs\",\n\"brier\",\n\"briered\",\n\"briery\",\n\"brieve\",\n\"brig\",\n\"brigade\",\n\"brigand\",\n\"bright\",\n\"brill\",\n\"brills\",\n\"brim\",\n\"brimful\",\n\"briming\",\n\"brimmed\",\n\"brimmer\",\n\"brin\",\n\"brine\",\n\"briner\",\n\"bring\",\n\"bringal\",\n\"bringer\",\n\"brinish\",\n\"brinjal\",\n\"brink\",\n\"briny\",\n\"brioche\",\n\"brique\",\n\"brisk\",\n\"brisken\",\n\"brisket\",\n\"briskly\",\n\"brisque\",\n\"briss\",\n\"bristle\",\n\"bristly\",\n\"brisure\",\n\"brit\",\n\"brith\",\n\"brither\",\n\"britska\",\n\"britten\",\n\"brittle\",\n\"brizz\",\n\"broach\",\n\"broad\",\n\"broadax\",\n\"broaden\",\n\"broadly\",\n\"brob\",\n\"brocade\",\n\"brocard\",\n\"broch\",\n\"brochan\",\n\"broche\",\n\"brocho\",\n\"brock\",\n\"brocked\",\n\"brocket\",\n\"brockle\",\n\"brod\",\n\"brodder\",\n\"brog\",\n\"brogan\",\n\"brogger\",\n\"broggle\",\n\"brogue\",\n\"broguer\",\n\"broider\",\n\"broigne\",\n\"broil\",\n\"broiler\",\n\"brokage\",\n\"broke\",\n\"broken\",\n\"broker\",\n\"broking\",\n\"brolga\",\n\"broll\",\n\"brolly\",\n\"broma\",\n\"bromal\",\n\"bromate\",\n\"brome\",\n\"bromic\",\n\"bromide\",\n\"bromine\",\n\"bromism\",\n\"bromite\",\n\"bromize\",\n\"bromoil\",\n\"bromol\",\n\"bromous\",\n\"bronc\",\n\"bronchi\",\n\"bronco\",\n\"bronk\",\n\"bronze\",\n\"bronzed\",\n\"bronzen\",\n\"bronzer\",\n\"bronzy\",\n\"broo\",\n\"brooch\",\n\"brood\",\n\"brooder\",\n\"broody\",\n\"brook\",\n\"brooked\",\n\"brookie\",\n\"brooky\",\n\"brool\",\n\"broom\",\n\"broomer\",\n\"broomy\",\n\"broon\",\n\"broose\",\n\"brose\",\n\"brosot\",\n\"brosy\",\n\"brot\",\n\"brotan\",\n\"brotany\",\n\"broth\",\n\"brothel\",\n\"brother\",\n\"brothy\",\n\"brough\",\n\"brought\",\n\"brow\",\n\"browden\",\n\"browed\",\n\"browis\",\n\"browman\",\n\"brown\",\n\"browner\",\n\"brownie\",\n\"brownly\",\n\"browny\",\n\"browse\",\n\"browser\",\n\"browst\",\n\"bruang\",\n\"brucia\",\n\"brucina\",\n\"brucine\",\n\"brucite\",\n\"bruckle\",\n\"brugh\",\n\"bruin\",\n\"bruise\",\n\"bruiser\",\n\"bruit\",\n\"bruiter\",\n\"bruke\",\n\"brulee\",\n\"brulyie\",\n\"brumal\",\n\"brumby\",\n\"brume\",\n\"brumous\",\n\"brunch\",\n\"brunet\",\n\"brunt\",\n\"bruscus\",\n\"brush\",\n\"brushed\",\n\"brusher\",\n\"brushes\",\n\"brushet\",\n\"brushy\",\n\"brusque\",\n\"brustle\",\n\"brut\",\n\"brutage\",\n\"brutal\",\n\"brute\",\n\"brutely\",\n\"brutify\",\n\"bruting\",\n\"brutish\",\n\"brutism\",\n\"brutter\",\n\"bruzz\",\n\"bryonin\",\n\"bryony\",\n\"bu\",\n\"bual\",\n\"buaze\",\n\"bub\",\n\"buba\",\n\"bubal\",\n\"bubalis\",\n\"bubble\",\n\"bubbler\",\n\"bubbly\",\n\"bubby\",\n\"bubinga\",\n\"bubo\",\n\"buboed\",\n\"bubonic\",\n\"bubukle\",\n\"bucare\",\n\"bucca\",\n\"buccal\",\n\"buccan\",\n\"buccate\",\n\"buccina\",\n\"buccula\",\n\"buchite\",\n\"buchu\",\n\"buck\",\n\"bucked\",\n\"buckeen\",\n\"bucker\",\n\"bucket\",\n\"buckety\",\n\"buckeye\",\n\"buckie\",\n\"bucking\",\n\"buckish\",\n\"buckle\",\n\"buckled\",\n\"buckler\",\n\"bucklum\",\n\"bucko\",\n\"buckpot\",\n\"buckra\",\n\"buckram\",\n\"bucksaw\",\n\"bucky\",\n\"bucolic\",\n\"bucrane\",\n\"bud\",\n\"buda\",\n\"buddage\",\n\"budder\",\n\"buddhi\",\n\"budding\",\n\"buddle\",\n\"buddler\",\n\"buddy\",\n\"budge\",\n\"budger\",\n\"budget\",\n\"budless\",\n\"budlet\",\n\"budlike\",\n\"budmash\",\n\"budtime\",\n\"budwood\",\n\"budworm\",\n\"budzat\",\n\"bufagin\",\n\"buff\",\n\"buffalo\",\n\"buffed\",\n\"buffer\",\n\"buffet\",\n\"buffing\",\n\"buffle\",\n\"buffont\",\n\"buffoon\",\n\"buffy\",\n\"bufidin\",\n\"bufo\",\n\"bug\",\n\"bugaboo\",\n\"bugan\",\n\"bugbane\",\n\"bugbear\",\n\"bugbite\",\n\"bugdom\",\n\"bugfish\",\n\"bugger\",\n\"buggery\",\n\"buggy\",\n\"bughead\",\n\"bugle\",\n\"bugled\",\n\"bugler\",\n\"buglet\",\n\"bugloss\",\n\"bugre\",\n\"bugseed\",\n\"bugweed\",\n\"bugwort\",\n\"buhl\",\n\"buhr\",\n\"build\",\n\"builder\",\n\"buildup\",\n\"built\",\n\"buirdly\",\n\"buisson\",\n\"buist\",\n\"bukh\",\n\"bukshi\",\n\"bulak\",\n\"bulb\",\n\"bulbar\",\n\"bulbed\",\n\"bulbil\",\n\"bulblet\",\n\"bulbose\",\n\"bulbous\",\n\"bulbul\",\n\"bulbule\",\n\"bulby\",\n\"bulchin\",\n\"bulge\",\n\"bulger\",\n\"bulgy\",\n\"bulimia\",\n\"bulimic\",\n\"bulimy\",\n\"bulk\",\n\"bulked\",\n\"bulker\",\n\"bulkily\",\n\"bulkish\",\n\"bulky\",\n\"bull\",\n\"bulla\",\n\"bullace\",\n\"bullan\",\n\"bullary\",\n\"bullate\",\n\"bullbat\",\n\"bulldog\",\n\"buller\",\n\"bullet\",\n\"bullety\",\n\"bulling\",\n\"bullion\",\n\"bullish\",\n\"bullism\",\n\"bullit\",\n\"bullnut\",\n\"bullock\",\n\"bullous\",\n\"bullule\",\n\"bully\",\n\"bulrush\",\n\"bulse\",\n\"bult\",\n\"bulter\",\n\"bultey\",\n\"bultong\",\n\"bultow\",\n\"bulwand\",\n\"bulwark\",\n\"bum\",\n\"bumbaze\",\n\"bumbee\",\n\"bumble\",\n\"bumbler\",\n\"bumbo\",\n\"bumboat\",\n\"bumicky\",\n\"bummalo\",\n\"bummed\",\n\"bummer\",\n\"bummie\",\n\"bumming\",\n\"bummler\",\n\"bummock\",\n\"bump\",\n\"bumpee\",\n\"bumper\",\n\"bumpily\",\n\"bumping\",\n\"bumpkin\",\n\"bumpy\",\n\"bumtrap\",\n\"bumwood\",\n\"bun\",\n\"buna\",\n\"buncal\",\n\"bunce\",\n\"bunch\",\n\"buncher\",\n\"bunchy\",\n\"bund\",\n\"bunder\",\n\"bundle\",\n\"bundler\",\n\"bundlet\",\n\"bundook\",\n\"bundy\",\n\"bung\",\n\"bungee\",\n\"bungey\",\n\"bungfu\",\n\"bungle\",\n\"bungler\",\n\"bungo\",\n\"bungy\",\n\"bunion\",\n\"bunk\",\n\"bunker\",\n\"bunkery\",\n\"bunkie\",\n\"bunko\",\n\"bunkum\",\n\"bunnell\",\n\"bunny\",\n\"bunt\",\n\"buntal\",\n\"bunted\",\n\"bunter\",\n\"bunting\",\n\"bunton\",\n\"bunty\",\n\"bunya\",\n\"bunyah\",\n\"bunyip\",\n\"buoy\",\n\"buoyage\",\n\"buoyant\",\n\"bur\",\n\"buran\",\n\"burao\",\n\"burbank\",\n\"burbark\",\n\"burble\",\n\"burbler\",\n\"burbly\",\n\"burbot\",\n\"burbush\",\n\"burd\",\n\"burden\",\n\"burdie\",\n\"burdock\",\n\"burdon\",\n\"bure\",\n\"bureau\",\n\"bureaux\",\n\"burel\",\n\"burele\",\n\"buret\",\n\"burette\",\n\"burfish\",\n\"burg\",\n\"burgage\",\n\"burgall\",\n\"burgee\",\n\"burgeon\",\n\"burgess\",\n\"burgh\",\n\"burghal\",\n\"burgher\",\n\"burglar\",\n\"burgle\",\n\"burgoo\",\n\"burgul\",\n\"burgus\",\n\"burhead\",\n\"buri\",\n\"burial\",\n\"burian\",\n\"buried\",\n\"burier\",\n\"burin\",\n\"burion\",\n\"buriti\",\n\"burka\",\n\"burke\",\n\"burker\",\n\"burl\",\n\"burlap\",\n\"burled\",\n\"burler\",\n\"burlet\",\n\"burlily\",\n\"burly\",\n\"burmite\",\n\"burn\",\n\"burned\",\n\"burner\",\n\"burnet\",\n\"burnie\",\n\"burning\",\n\"burnish\",\n\"burnous\",\n\"burnout\",\n\"burnt\",\n\"burnut\",\n\"burny\",\n\"buro\",\n\"burp\",\n\"burr\",\n\"burrah\",\n\"burred\",\n\"burrel\",\n\"burrer\",\n\"burring\",\n\"burrish\",\n\"burrito\",\n\"burro\",\n\"burrow\",\n\"burry\",\n\"bursa\",\n\"bursal\",\n\"bursar\",\n\"bursary\",\n\"bursate\",\n\"burse\",\n\"burseed\",\n\"burst\",\n\"burster\",\n\"burt\",\n\"burton\",\n\"burucha\",\n\"burweed\",\n\"bury\",\n\"burying\",\n\"bus\",\n\"busby\",\n\"buscarl\",\n\"bush\",\n\"bushed\",\n\"bushel\",\n\"busher\",\n\"bushful\",\n\"bushi\",\n\"bushily\",\n\"bushing\",\n\"bushlet\",\n\"bushwa\",\n\"bushy\",\n\"busied\",\n\"busily\",\n\"busine\",\n\"busk\",\n\"busked\",\n\"busker\",\n\"busket\",\n\"buskin\",\n\"buskle\",\n\"busky\",\n\"busman\",\n\"buss\",\n\"busser\",\n\"bussock\",\n\"bussu\",\n\"bust\",\n\"bustard\",\n\"busted\",\n\"bustee\",\n\"buster\",\n\"bustic\",\n\"bustle\",\n\"bustled\",\n\"bustler\",\n\"busy\",\n\"busying\",\n\"busyish\",\n\"but\",\n\"butanal\",\n\"butane\",\n\"butanol\",\n\"butch\",\n\"butcher\",\n\"butein\",\n\"butene\",\n\"butenyl\",\n\"butic\",\n\"butine\",\n\"butler\",\n\"butlery\",\n\"butment\",\n\"butoxy\",\n\"butoxyl\",\n\"butt\",\n\"butte\",\n\"butter\",\n\"buttery\",\n\"butting\",\n\"buttle\",\n\"buttock\",\n\"button\",\n\"buttons\",\n\"buttony\",\n\"butty\",\n\"butyl\",\n\"butylic\",\n\"butyne\",\n\"butyr\",\n\"butyral\",\n\"butyric\",\n\"butyrin\",\n\"butyryl\",\n\"buxerry\",\n\"buxom\",\n\"buxomly\",\n\"buy\",\n\"buyable\",\n\"buyer\",\n\"buzane\",\n\"buzz\",\n\"buzzard\",\n\"buzzer\",\n\"buzzies\",\n\"buzzing\",\n\"buzzle\",\n\"buzzwig\",\n\"buzzy\",\n\"by\",\n\"bycoket\",\n\"bye\",\n\"byee\",\n\"byeman\",\n\"byepath\",\n\"byerite\",\n\"bygane\",\n\"bygo\",\n\"bygoing\",\n\"bygone\",\n\"byhand\",\n\"bylaw\",\n\"byname\",\n\"byon\",\n\"byous\",\n\"byously\",\n\"bypass\",\n\"bypast\",\n\"bypath\",\n\"byplay\",\n\"byre\",\n\"byreman\",\n\"byrlaw\",\n\"byrnie\",\n\"byroad\",\n\"byrrus\",\n\"bysen\",\n\"byspell\",\n\"byssal\",\n\"byssin\",\n\"byssine\",\n\"byssoid\",\n\"byssus\",\n\"byth\",\n\"bytime\",\n\"bywalk\",\n\"byway\",\n\"bywoner\",\n\"byword\",\n\"bywork\",\n\"c\",\n\"ca\",\n\"caam\",\n\"caama\",\n\"caaming\",\n\"caapeba\",\n\"cab\",\n\"caba\",\n\"cabaan\",\n\"caback\",\n\"cabaho\",\n\"cabal\",\n\"cabala\",\n\"cabalic\",\n\"caban\",\n\"cabana\",\n\"cabaret\",\n\"cabas\",\n\"cabbage\",\n\"cabbagy\",\n\"cabber\",\n\"cabble\",\n\"cabbler\",\n\"cabby\",\n\"cabda\",\n\"caber\",\n\"cabezon\",\n\"cabin\",\n\"cabinet\",\n\"cabio\",\n\"cable\",\n\"cabled\",\n\"cabler\",\n\"cablet\",\n\"cabling\",\n\"cabman\",\n\"cabob\",\n\"cabocle\",\n\"cabook\",\n\"caboose\",\n\"cabot\",\n\"cabree\",\n\"cabrit\",\n\"cabuya\",\n\"cacam\",\n\"cacao\",\n\"cachaza\",\n\"cache\",\n\"cachet\",\n\"cachexy\",\n\"cachou\",\n\"cachrys\",\n\"cacique\",\n\"cack\",\n\"cackle\",\n\"cackler\",\n\"cacodyl\",\n\"cacoepy\",\n\"caconym\",\n\"cacoon\",\n\"cacti\",\n\"cactoid\",\n\"cacur\",\n\"cad\",\n\"cadamba\",\n\"cadaver\",\n\"cadbait\",\n\"cadbit\",\n\"cadbote\",\n\"caddice\",\n\"caddie\",\n\"caddis\",\n\"caddish\",\n\"caddle\",\n\"caddow\",\n\"caddy\",\n\"cade\",\n\"cadelle\",\n\"cadence\",\n\"cadency\",\n\"cadent\",\n\"cadenza\",\n\"cader\",\n\"caderas\",\n\"cadet\",\n\"cadetcy\",\n\"cadette\",\n\"cadew\",\n\"cadge\",\n\"cadger\",\n\"cadgily\",\n\"cadgy\",\n\"cadi\",\n\"cadism\",\n\"cadjan\",\n\"cadlock\",\n\"cadmia\",\n\"cadmic\",\n\"cadmide\",\n\"cadmium\",\n\"cados\",\n\"cadrans\",\n\"cadre\",\n\"cadua\",\n\"caduac\",\n\"caduca\",\n\"cadus\",\n\"cadweed\",\n\"caeca\",\n\"caecal\",\n\"caecum\",\n\"caeoma\",\n\"caesura\",\n\"cafeneh\",\n\"cafenet\",\n\"caffa\",\n\"caffeic\",\n\"caffeol\",\n\"caffiso\",\n\"caffle\",\n\"caffoy\",\n\"cafh\",\n\"cafiz\",\n\"caftan\",\n\"cag\",\n\"cage\",\n\"caged\",\n\"cageful\",\n\"cageman\",\n\"cager\",\n\"cagey\",\n\"caggy\",\n\"cagily\",\n\"cagit\",\n\"cagmag\",\n\"cahiz\",\n\"cahoot\",\n\"cahot\",\n\"cahow\",\n\"caickle\",\n\"caid\",\n\"caiman\",\n\"caimito\",\n\"cain\",\n\"caique\",\n\"caird\",\n\"cairn\",\n\"cairned\",\n\"cairny\",\n\"caisson\",\n\"caitiff\",\n\"cajeput\",\n\"cajole\",\n\"cajoler\",\n\"cajuela\",\n\"cajun\",\n\"cajuput\",\n\"cake\",\n\"cakebox\",\n\"caker\",\n\"cakette\",\n\"cakey\",\n\"caky\",\n\"cal\",\n\"calaba\",\n\"calaber\",\n\"calade\",\n\"calais\",\n\"calalu\",\n\"calamus\",\n\"calash\",\n\"calcar\",\n\"calced\",\n\"calcic\",\n\"calcify\",\n\"calcine\",\n\"calcite\",\n\"calcium\",\n\"calculi\",\n\"calden\",\n\"caldron\",\n\"calean\",\n\"calends\",\n\"calepin\",\n\"calf\",\n\"calfish\",\n\"caliber\",\n\"calibre\",\n\"calices\",\n\"calicle\",\n\"calico\",\n\"calid\",\n\"caliga\",\n\"caligo\",\n\"calinda\",\n\"calinut\",\n\"calipee\",\n\"caliper\",\n\"caliph\",\n\"caliver\",\n\"calix\",\n\"calk\",\n\"calkage\",\n\"calker\",\n\"calkin\",\n\"calking\",\n\"call\",\n\"callant\",\n\"callboy\",\n\"caller\",\n\"callet\",\n\"calli\",\n\"callid\",\n\"calling\",\n\"callo\",\n\"callose\",\n\"callous\",\n\"callow\",\n\"callus\",\n\"calm\",\n\"calmant\",\n\"calmer\",\n\"calmly\",\n\"calmy\",\n\"calomba\",\n\"calomel\",\n\"calool\",\n\"calor\",\n\"caloric\",\n\"calorie\",\n\"caloris\",\n\"calotte\",\n\"caloyer\",\n\"calp\",\n\"calpac\",\n\"calpack\",\n\"caltrap\",\n\"caltrop\",\n\"calumba\",\n\"calumet\",\n\"calumny\",\n\"calve\",\n\"calved\",\n\"calver\",\n\"calves\",\n\"calvish\",\n\"calvity\",\n\"calvous\",\n\"calx\",\n\"calyces\",\n\"calycle\",\n\"calymma\",\n\"calypso\",\n\"calyx\",\n\"cam\",\n\"camaca\",\n\"camagon\",\n\"camail\",\n\"caman\",\n\"camansi\",\n\"camara\",\n\"camass\",\n\"camata\",\n\"camb\",\n\"cambaye\",\n\"camber\",\n\"cambial\",\n\"cambism\",\n\"cambist\",\n\"cambium\",\n\"cambrel\",\n\"cambuca\",\n\"came\",\n\"cameist\",\n\"camel\",\n\"camelry\",\n\"cameo\",\n\"camera\",\n\"cameral\",\n\"camilla\",\n\"camion\",\n\"camise\",\n\"camisia\",\n\"camlet\",\n\"cammed\",\n\"cammock\",\n\"camoodi\",\n\"camp\",\n\"campana\",\n\"campane\",\n\"camper\",\n\"campho\",\n\"camphol\",\n\"camphor\",\n\"campion\",\n\"cample\",\n\"campo\",\n\"campody\",\n\"campoo\",\n\"campus\",\n\"camus\",\n\"camused\",\n\"camwood\",\n\"can\",\n\"canaba\",\n\"canada\",\n\"canadol\",\n\"canal\",\n\"canamo\",\n\"canape\",\n\"canard\",\n\"canari\",\n\"canarin\",\n\"canary\",\n\"canasta\",\n\"canaut\",\n\"cancan\",\n\"cancel\",\n\"cancer\",\n\"canch\",\n\"cancrum\",\n\"cand\",\n\"candela\",\n\"candent\",\n\"candid\",\n\"candied\",\n\"candier\",\n\"candify\",\n\"candiru\",\n\"candle\",\n\"candler\",\n\"candock\",\n\"candor\",\n\"candroy\",\n\"candy\",\n\"candys\",\n\"cane\",\n\"canel\",\n\"canella\",\n\"canelo\",\n\"caner\",\n\"canette\",\n\"canful\",\n\"cangan\",\n\"cangia\",\n\"cangle\",\n\"cangler\",\n\"cangue\",\n\"canhoop\",\n\"canid\",\n\"canille\",\n\"caninal\",\n\"canine\",\n\"caninus\",\n\"canions\",\n\"canjac\",\n\"cank\",\n\"canker\",\n\"cankery\",\n\"canman\",\n\"canna\",\n\"cannach\",\n\"canned\",\n\"cannel\",\n\"canner\",\n\"cannery\",\n\"cannet\",\n\"cannily\",\n\"canning\",\n\"cannon\",\n\"cannot\",\n\"cannula\",\n\"canny\",\n\"canoe\",\n\"canon\",\n\"canonic\",\n\"canonry\",\n\"canopic\",\n\"canopy\",\n\"canroy\",\n\"canso\",\n\"cant\",\n\"cantala\",\n\"cantar\",\n\"cantara\",\n\"cantaro\",\n\"cantata\",\n\"canted\",\n\"canteen\",\n\"canter\",\n\"canthal\",\n\"canthus\",\n\"cantic\",\n\"cantico\",\n\"cantily\",\n\"cantina\",\n\"canting\",\n\"cantion\",\n\"cantish\",\n\"cantle\",\n\"cantlet\",\n\"canto\",\n\"canton\",\n\"cantoon\",\n\"cantor\",\n\"cantred\",\n\"cantref\",\n\"cantrip\",\n\"cantus\",\n\"canty\",\n\"canun\",\n\"canvas\",\n\"canvass\",\n\"cany\",\n\"canyon\",\n\"canzon\",\n\"caoba\",\n\"cap\",\n\"capable\",\n\"capably\",\n\"capanna\",\n\"capanne\",\n\"capax\",\n\"capcase\",\n\"cape\",\n\"caped\",\n\"capel\",\n\"capelet\",\n\"capelin\",\n\"caper\",\n\"caperer\",\n\"capes\",\n\"capful\",\n\"caph\",\n\"caphar\",\n\"caphite\",\n\"capias\",\n\"capicha\",\n\"capital\",\n\"capitan\",\n\"capivi\",\n\"capkin\",\n\"capless\",\n\"caplin\",\n\"capman\",\n\"capmint\",\n\"capomo\",\n\"capon\",\n\"caporal\",\n\"capot\",\n\"capote\",\n\"capped\",\n\"capper\",\n\"cappie\",\n\"capping\",\n\"capple\",\n\"cappy\",\n\"caprate\",\n\"capreol\",\n\"capric\",\n\"caprice\",\n\"caprid\",\n\"caprin\",\n\"caprine\",\n\"caproic\",\n\"caproin\",\n\"caprone\",\n\"caproyl\",\n\"capryl\",\n\"capsa\",\n\"capsid\",\n\"capsize\",\n\"capstan\",\n\"capsula\",\n\"capsule\",\n\"captain\",\n\"caption\",\n\"captive\",\n\"captor\",\n\"capture\",\n\"capuche\",\n\"capulet\",\n\"capulin\",\n\"car\",\n\"carabao\",\n\"carabid\",\n\"carabin\",\n\"carabus\",\n\"caracal\",\n\"caracol\",\n\"caract\",\n\"carafe\",\n\"caraibe\",\n\"caraipi\",\n\"caramba\",\n\"caramel\",\n\"caranda\",\n\"carane\",\n\"caranna\",\n\"carapax\",\n\"carapo\",\n\"carat\",\n\"caratch\",\n\"caravan\",\n\"caravel\",\n\"caraway\",\n\"carbarn\",\n\"carbeen\",\n\"carbene\",\n\"carbide\",\n\"carbine\",\n\"carbo\",\n\"carbon\",\n\"carbona\",\n\"carbora\",\n\"carboxy\",\n\"carboy\",\n\"carbro\",\n\"carbure\",\n\"carbyl\",\n\"carcake\",\n\"carcass\",\n\"carceag\",\n\"carcel\",\n\"carcoon\",\n\"card\",\n\"cardecu\",\n\"carded\",\n\"cardel\",\n\"carder\",\n\"cardia\",\n\"cardiac\",\n\"cardial\",\n\"cardin\",\n\"carding\",\n\"cardo\",\n\"cardol\",\n\"cardon\",\n\"cardona\",\n\"cardoon\",\n\"care\",\n\"careen\",\n\"career\",\n\"careful\",\n\"carene\",\n\"carer\",\n\"caress\",\n\"carest\",\n\"caret\",\n\"carfare\",\n\"carfax\",\n\"carful\",\n\"carga\",\n\"cargo\",\n\"carhop\",\n\"cariama\",\n\"caribou\",\n\"carid\",\n\"caries\",\n\"carina\",\n\"carinal\",\n\"cariole\",\n\"carious\",\n\"cark\",\n\"carking\",\n\"carkled\",\n\"carl\",\n\"carless\",\n\"carlet\",\n\"carlie\",\n\"carlin\",\n\"carline\",\n\"carling\",\n\"carlish\",\n\"carload\",\n\"carlot\",\n\"carls\",\n\"carman\",\n\"carmele\",\n\"carmine\",\n\"carmot\",\n\"carnage\",\n\"carnal\",\n\"carnate\",\n\"carneol\",\n\"carney\",\n\"carnic\",\n\"carnify\",\n\"carnose\",\n\"carnous\",\n\"caroa\",\n\"carob\",\n\"caroba\",\n\"caroche\",\n\"carol\",\n\"caroler\",\n\"caroli\",\n\"carolin\",\n\"carolus\",\n\"carom\",\n\"carone\",\n\"caronic\",\n\"caroome\",\n\"caroon\",\n\"carotic\",\n\"carotid\",\n\"carotin\",\n\"carouse\",\n\"carp\",\n\"carpal\",\n\"carpale\",\n\"carpel\",\n\"carpent\",\n\"carper\",\n\"carpet\",\n\"carpid\",\n\"carping\",\n\"carpium\",\n\"carport\",\n\"carpos\",\n\"carpus\",\n\"carr\",\n\"carrack\",\n\"carrel\",\n\"carrick\",\n\"carried\",\n\"carrier\",\n\"carrion\",\n\"carrizo\",\n\"carroch\",\n\"carrot\",\n\"carroty\",\n\"carrow\",\n\"carry\",\n\"carse\",\n\"carshop\",\n\"carsick\",\n\"cart\",\n\"cartage\",\n\"carte\",\n\"cartel\",\n\"carter\",\n\"cartful\",\n\"cartman\",\n\"carton\",\n\"cartoon\",\n\"cartway\",\n\"carty\",\n\"carua\",\n\"carucal\",\n\"carval\",\n\"carve\",\n\"carvel\",\n\"carven\",\n\"carvene\",\n\"carver\",\n\"carving\",\n\"carvol\",\n\"carvone\",\n\"carvyl\",\n\"caryl\",\n\"casaba\",\n\"casabe\",\n\"casal\",\n\"casalty\",\n\"casate\",\n\"casaun\",\n\"casava\",\n\"casave\",\n\"casavi\",\n\"casbah\",\n\"cascade\",\n\"cascado\",\n\"cascara\",\n\"casco\",\n\"cascol\",\n\"case\",\n\"casease\",\n\"caseate\",\n\"casebox\",\n\"cased\",\n\"caseful\",\n\"casefy\",\n\"caseic\",\n\"casein\",\n\"caseose\",\n\"caseous\",\n\"caser\",\n\"casern\",\n\"caseum\",\n\"cash\",\n\"casha\",\n\"cashaw\",\n\"cashbox\",\n\"cashboy\",\n\"cashel\",\n\"cashew\",\n\"cashier\",\n\"casing\",\n\"casino\",\n\"casiri\",\n\"cask\",\n\"casket\",\n\"casking\",\n\"casque\",\n\"casqued\",\n\"casquet\",\n\"cass\",\n\"cassady\",\n\"casse\",\n\"cassena\",\n\"cassia\",\n\"cassie\",\n\"cassina\",\n\"cassine\",\n\"cassino\",\n\"cassis\",\n\"cassock\",\n\"casson\",\n\"cassoon\",\n\"cast\",\n\"caste\",\n\"caster\",\n\"castice\",\n\"casting\",\n\"castle\",\n\"castled\",\n\"castlet\",\n\"castock\",\n\"castoff\",\n\"castor\",\n\"castory\",\n\"castra\",\n\"castral\",\n\"castrum\",\n\"castuli\",\n\"casual\",\n\"casuary\",\n\"casuist\",\n\"casula\",\n\"cat\",\n\"catalpa\",\n\"catan\",\n\"catapan\",\n\"cataria\",\n\"catarrh\",\n\"catasta\",\n\"catbird\",\n\"catboat\",\n\"catcall\",\n\"catch\",\n\"catcher\",\n\"catchup\",\n\"catchy\",\n\"catclaw\",\n\"catdom\",\n\"cate\",\n\"catechu\",\n\"catella\",\n\"catena\",\n\"catenae\",\n\"cater\",\n\"cateran\",\n\"caterer\",\n\"caterva\",\n\"cateye\",\n\"catface\",\n\"catfall\",\n\"catfish\",\n\"catfoot\",\n\"catgut\",\n\"cathead\",\n\"cathect\",\n\"catheti\",\n\"cathin\",\n\"cathine\",\n\"cathion\",\n\"cathode\",\n\"cathole\",\n\"cathood\",\n\"cathop\",\n\"cathro\",\n\"cation\",\n\"cativo\",\n\"catjang\",\n\"catkin\",\n\"catlap\",\n\"catlike\",\n\"catlin\",\n\"catling\",\n\"catmint\",\n\"catnip\",\n\"catpipe\",\n\"catskin\",\n\"catstep\",\n\"catsup\",\n\"cattabu\",\n\"cattail\",\n\"cattalo\",\n\"cattery\",\n\"cattily\",\n\"catting\",\n\"cattish\",\n\"cattle\",\n\"catty\",\n\"catvine\",\n\"catwalk\",\n\"catwise\",\n\"catwood\",\n\"catwort\",\n\"caubeen\",\n\"cauboge\",\n\"cauch\",\n\"caucho\",\n\"caucus\",\n\"cauda\",\n\"caudad\",\n\"caudae\",\n\"caudal\",\n\"caudata\",\n\"caudate\",\n\"caudex\",\n\"caudle\",\n\"caught\",\n\"cauk\",\n\"caul\",\n\"cauld\",\n\"caules\",\n\"cauline\",\n\"caulis\",\n\"caulome\",\n\"caulote\",\n\"caum\",\n\"cauma\",\n\"caunch\",\n\"caup\",\n\"caupo\",\n\"caurale\",\n\"causal\",\n\"causate\",\n\"cause\",\n\"causer\",\n\"causey\",\n\"causing\",\n\"causse\",\n\"causson\",\n\"caustic\",\n\"cautel\",\n\"cauter\",\n\"cautery\",\n\"caution\",\n\"cautivo\",\n\"cava\",\n\"cavae\",\n\"caval\",\n\"cavalla\",\n\"cavalry\",\n\"cavate\",\n\"cave\",\n\"caveat\",\n\"cavel\",\n\"cavelet\",\n\"cavern\",\n\"cavetto\",\n\"caviar\",\n\"cavie\",\n\"cavil\",\n\"caviler\",\n\"caving\",\n\"cavings\",\n\"cavish\",\n\"cavity\",\n\"caviya\",\n\"cavort\",\n\"cavus\",\n\"cavy\",\n\"caw\",\n\"cawk\",\n\"cawky\",\n\"cawney\",\n\"cawquaw\",\n\"caxiri\",\n\"caxon\",\n\"cay\",\n\"cayenne\",\n\"cayman\",\n\"caza\",\n\"cazimi\",\n\"ce\",\n\"cearin\",\n\"cease\",\n\"ceasmic\",\n\"cebell\",\n\"cebian\",\n\"cebid\",\n\"cebil\",\n\"cebine\",\n\"ceboid\",\n\"cebur\",\n\"cecils\",\n\"cecity\",\n\"cedar\",\n\"cedared\",\n\"cedarn\",\n\"cedary\",\n\"cede\",\n\"cedent\",\n\"ceder\",\n\"cedilla\",\n\"cedrat\",\n\"cedrate\",\n\"cedre\",\n\"cedrene\",\n\"cedrin\",\n\"cedrine\",\n\"cedrium\",\n\"cedrol\",\n\"cedron\",\n\"cedry\",\n\"cedula\",\n\"cee\",\n\"ceibo\",\n\"ceil\",\n\"ceile\",\n\"ceiler\",\n\"ceilidh\",\n\"ceiling\",\n\"celadon\",\n\"celemin\",\n\"celery\",\n\"celesta\",\n\"celeste\",\n\"celiac\",\n\"celite\",\n\"cell\",\n\"cella\",\n\"cellae\",\n\"cellar\",\n\"celled\",\n\"cellist\",\n\"cello\",\n\"celloid\",\n\"cellose\",\n\"cellule\",\n\"celsian\",\n\"celt\",\n\"celtium\",\n\"celtuce\",\n\"cembalo\",\n\"cement\",\n\"cenacle\",\n\"cendre\",\n\"cenoby\",\n\"cense\",\n\"censer\",\n\"censive\",\n\"censor\",\n\"censual\",\n\"censure\",\n\"census\",\n\"cent\",\n\"centage\",\n\"cental\",\n\"centare\",\n\"centaur\",\n\"centavo\",\n\"centena\",\n\"center\",\n\"centiar\",\n\"centile\",\n\"centime\",\n\"centimo\",\n\"centner\",\n\"cento\",\n\"centrad\",\n\"central\",\n\"centric\",\n\"centrum\",\n\"centry\",\n\"centum\",\n\"century\",\n\"ceorl\",\n\"cep\",\n\"cepa\",\n\"cepe\",\n\"cephid\",\n\"ceps\",\n\"ceptor\",\n\"cequi\",\n\"cerago\",\n\"ceral\",\n\"ceramal\",\n\"ceramic\",\n\"ceras\",\n\"cerasin\",\n\"cerata\",\n\"cerate\",\n\"cerated\",\n\"cercal\",\n\"cerci\",\n\"cercus\",\n\"cere\",\n\"cereal\",\n\"cerebra\",\n\"cered\",\n\"cereous\",\n\"cerer\",\n\"ceresin\",\n\"cerevis\",\n\"ceria\",\n\"ceric\",\n\"ceride\",\n\"cerillo\",\n\"ceriman\",\n\"cerin\",\n\"cerine\",\n\"ceriops\",\n\"cerise\",\n\"cerite\",\n\"cerium\",\n\"cermet\",\n\"cern\",\n\"cero\",\n\"ceroma\",\n\"cerote\",\n\"cerotic\",\n\"cerotin\",\n\"cerous\",\n\"cerrero\",\n\"cerrial\",\n\"cerris\",\n\"certain\",\n\"certie\",\n\"certify\",\n\"certis\",\n\"certy\",\n\"cerule\",\n\"cerumen\",\n\"ceruse\",\n\"cervid\",\n\"cervine\",\n\"cervix\",\n\"cervoid\",\n\"ceryl\",\n\"cesious\",\n\"cesium\",\n\"cess\",\n\"cesser\",\n\"cession\",\n\"cessor\",\n\"cesspit\",\n\"cest\",\n\"cestode\",\n\"cestoid\",\n\"cestrum\",\n\"cestus\",\n\"cetane\",\n\"cetene\",\n\"ceti\",\n\"cetic\",\n\"cetin\",\n\"cetyl\",\n\"cetylic\",\n\"cevine\",\n\"cha\",\n\"chaa\",\n\"chab\",\n\"chabot\",\n\"chabouk\",\n\"chabuk\",\n\"chacate\",\n\"chack\",\n\"chacker\",\n\"chackle\",\n\"chacma\",\n\"chacona\",\n\"chacte\",\n\"chad\",\n\"chaeta\",\n\"chafe\",\n\"chafer\",\n\"chafery\",\n\"chaff\",\n\"chaffer\",\n\"chaffy\",\n\"chaft\",\n\"chafted\",\n\"chagan\",\n\"chagrin\",\n\"chaguar\",\n\"chagul\",\n\"chahar\",\n\"chai\",\n\"chain\",\n\"chained\",\n\"chainer\",\n\"chainon\",\n\"chair\",\n\"chairer\",\n\"chais\",\n\"chaise\",\n\"chaitya\",\n\"chaja\",\n\"chaka\",\n\"chakar\",\n\"chakari\",\n\"chakazi\",\n\"chakdar\",\n\"chakobu\",\n\"chakra\",\n\"chakram\",\n\"chaksi\",\n\"chal\",\n\"chalaco\",\n\"chalana\",\n\"chalaza\",\n\"chalaze\",\n\"chalcid\",\n\"chalcon\",\n\"chalcus\",\n\"chalder\",\n\"chalet\",\n\"chalice\",\n\"chalk\",\n\"chalker\",\n\"chalky\",\n\"challah\",\n\"challie\",\n\"challis\",\n\"chalmer\",\n\"chalon\",\n\"chalone\",\n\"chalque\",\n\"chalta\",\n\"chalutz\",\n\"cham\",\n\"chamal\",\n\"chamar\",\n\"chamber\",\n\"chambul\",\n\"chamfer\",\n\"chamiso\",\n\"chamite\",\n\"chamma\",\n\"chamois\",\n\"champ\",\n\"champac\",\n\"champer\",\n\"champy\",\n\"chance\",\n\"chancel\",\n\"chancer\",\n\"chanche\",\n\"chanco\",\n\"chancre\",\n\"chancy\",\n\"chandam\",\n\"chandi\",\n\"chandoo\",\n\"chandu\",\n\"chandul\",\n\"chang\",\n\"changa\",\n\"changar\",\n\"change\",\n\"changer\",\n\"chank\",\n\"channel\",\n\"channer\",\n\"chanson\",\n\"chanst\",\n\"chant\",\n\"chanter\",\n\"chantey\",\n\"chantry\",\n\"chao\",\n\"chaos\",\n\"chaotic\",\n\"chap\",\n\"chapah\",\n\"chape\",\n\"chapeau\",\n\"chaped\",\n\"chapel\",\n\"chapin\",\n\"chaplet\",\n\"chapman\",\n\"chapped\",\n\"chapper\",\n\"chappie\",\n\"chappin\",\n\"chappow\",\n\"chappy\",\n\"chaps\",\n\"chapt\",\n\"chapter\",\n\"char\",\n\"charac\",\n\"charade\",\n\"charas\",\n\"charbon\",\n\"chard\",\n\"chare\",\n\"charer\",\n\"charet\",\n\"charge\",\n\"chargee\",\n\"charger\",\n\"charier\",\n\"charily\",\n\"chariot\",\n\"charism\",\n\"charity\",\n\"chark\",\n\"charka\",\n\"charkha\",\n\"charm\",\n\"charmel\",\n\"charmer\",\n\"charnel\",\n\"charpit\",\n\"charpoy\",\n\"charqui\",\n\"charr\",\n\"charry\",\n\"chart\",\n\"charter\",\n\"charuk\",\n\"chary\",\n\"chase\",\n\"chaser\",\n\"chasing\",\n\"chasm\",\n\"chasma\",\n\"chasmal\",\n\"chasmed\",\n\"chasmic\",\n\"chasmy\",\n\"chasse\",\n\"chassis\",\n\"chaste\",\n\"chasten\",\n\"chat\",\n\"chataka\",\n\"chateau\",\n\"chati\",\n\"chatta\",\n\"chattel\",\n\"chatter\",\n\"chatty\",\n\"chauk\",\n\"chaus\",\n\"chaute\",\n\"chauth\",\n\"chavish\",\n\"chaw\",\n\"chawan\",\n\"chawer\",\n\"chawk\",\n\"chawl\",\n\"chay\",\n\"chaya\",\n\"chayote\",\n\"chazan\",\n\"che\",\n\"cheap\",\n\"cheapen\",\n\"cheaply\",\n\"cheat\",\n\"cheatee\",\n\"cheater\",\n\"chebec\",\n\"chebel\",\n\"chebog\",\n\"chebule\",\n\"check\",\n\"checked\",\n\"checker\",\n\"checkup\",\n\"checky\",\n\"cheder\",\n\"chee\",\n\"cheecha\",\n\"cheek\",\n\"cheeker\",\n\"cheeky\",\n\"cheep\",\n\"cheeper\",\n\"cheepy\",\n\"cheer\",\n\"cheered\",\n\"cheerer\",\n\"cheerio\",\n\"cheerly\",\n\"cheery\",\n\"cheese\",\n\"cheeser\",\n\"cheesy\",\n\"cheet\",\n\"cheetah\",\n\"cheeter\",\n\"cheetie\",\n\"chef\",\n\"chegoe\",\n\"chegre\",\n\"cheir\",\n\"chekan\",\n\"cheke\",\n\"cheki\",\n\"chekmak\",\n\"chela\",\n\"chelate\",\n\"chelem\",\n\"chelide\",\n\"chello\",\n\"chelone\",\n\"chelp\",\n\"chelys\",\n\"chemic\",\n\"chemis\",\n\"chemise\",\n\"chemism\",\n\"chemist\",\n\"chena\",\n\"chende\",\n\"cheng\",\n\"chenica\",\n\"cheque\",\n\"cherem\",\n\"cherish\",\n\"cheroot\",\n\"cherry\",\n\"chert\",\n\"cherte\",\n\"cherty\",\n\"cherub\",\n\"chervil\",\n\"cheson\",\n\"chess\",\n\"chessel\",\n\"chesser\",\n\"chest\",\n\"chester\",\n\"chesty\",\n\"cheth\",\n\"chettik\",\n\"chetty\",\n\"chevage\",\n\"cheval\",\n\"cheve\",\n\"cheven\",\n\"chevin\",\n\"chevise\",\n\"chevon\",\n\"chevron\",\n\"chevy\",\n\"chew\",\n\"chewer\",\n\"chewink\",\n\"chewy\",\n\"cheyney\",\n\"chhatri\",\n\"chi\",\n\"chia\",\n\"chiasm\",\n\"chiasma\",\n\"chiaus\",\n\"chibouk\",\n\"chibrit\",\n\"chic\",\n\"chicane\",\n\"chichi\",\n\"chick\",\n\"chicken\",\n\"chicker\",\n\"chicky\",\n\"chicle\",\n\"chico\",\n\"chicory\",\n\"chicot\",\n\"chicote\",\n\"chid\",\n\"chidden\",\n\"chide\",\n\"chider\",\n\"chiding\",\n\"chidra\",\n\"chief\",\n\"chiefly\",\n\"chield\",\n\"chien\",\n\"chiffer\",\n\"chiffon\",\n\"chiggak\",\n\"chigger\",\n\"chignon\",\n\"chigoe\",\n\"chih\",\n\"chihfu\",\n\"chikara\",\n\"chil\",\n\"child\",\n\"childe\",\n\"childed\",\n\"childly\",\n\"chile\",\n\"chili\",\n\"chiliad\",\n\"chill\",\n\"chilla\",\n\"chilled\",\n\"chiller\",\n\"chillo\",\n\"chillum\",\n\"chilly\",\n\"chiloma\",\n\"chilver\",\n\"chimble\",\n\"chime\",\n\"chimer\",\n\"chimera\",\n\"chimney\",\n\"chin\",\n\"china\",\n\"chinar\",\n\"chinch\",\n\"chincha\",\n\"chinche\",\n\"chine\",\n\"chined\",\n\"ching\",\n\"chingma\",\n\"chinik\",\n\"chinin\",\n\"chink\",\n\"chinker\",\n\"chinkle\",\n\"chinks\",\n\"chinky\",\n\"chinnam\",\n\"chinned\",\n\"chinny\",\n\"chino\",\n\"chinoa\",\n\"chinol\",\n\"chinse\",\n\"chint\",\n\"chintz\",\n\"chip\",\n\"chiplet\",\n\"chipped\",\n\"chipper\",\n\"chippy\",\n\"chips\",\n\"chiral\",\n\"chirata\",\n\"chiripa\",\n\"chirk\",\n\"chirm\",\n\"chiro\",\n\"chirp\",\n\"chirper\",\n\"chirpy\",\n\"chirr\",\n\"chirrup\",\n\"chisel\",\n\"chit\",\n\"chitak\",\n\"chital\",\n\"chitin\",\n\"chiton\",\n\"chitose\",\n\"chitra\",\n\"chitter\",\n\"chitty\",\n\"chive\",\n\"chivey\",\n\"chkalik\",\n\"chlamyd\",\n\"chlamys\",\n\"chlor\",\n\"chloral\",\n\"chlore\",\n\"chloric\",\n\"chloryl\",\n\"cho\",\n\"choana\",\n\"choate\",\n\"choaty\",\n\"chob\",\n\"choca\",\n\"chocard\",\n\"chocho\",\n\"chock\",\n\"chocker\",\n\"choel\",\n\"choenix\",\n\"choffer\",\n\"choga\",\n\"chogak\",\n\"chogset\",\n\"choice\",\n\"choicy\",\n\"choil\",\n\"choiler\",\n\"choir\",\n\"chokage\",\n\"choke\",\n\"choker\",\n\"choking\",\n\"chokra\",\n\"choky\",\n\"chol\",\n\"chola\",\n\"cholane\",\n\"cholate\",\n\"chold\",\n\"choleic\",\n\"choler\",\n\"cholera\",\n\"choli\",\n\"cholic\",\n\"choline\",\n\"cholla\",\n\"choller\",\n\"cholum\",\n\"chomp\",\n\"chondre\",\n\"chonta\",\n\"choop\",\n\"choose\",\n\"chooser\",\n\"choosy\",\n\"chop\",\n\"chopa\",\n\"chopin\",\n\"chopine\",\n\"chopped\",\n\"chopper\",\n\"choppy\",\n\"choragy\",\n\"choral\",\n\"chord\",\n\"chorda\",\n\"chordal\",\n\"chorded\",\n\"chore\",\n\"chorea\",\n\"choreal\",\n\"choree\",\n\"choregy\",\n\"choreic\",\n\"choreus\",\n\"chorial\",\n\"choric\",\n\"chorine\",\n\"chorion\",\n\"chorism\",\n\"chorist\",\n\"chorogi\",\n\"choroid\",\n\"chorook\",\n\"chort\",\n\"chorten\",\n\"chortle\",\n\"chorus\",\n\"choryos\",\n\"chose\",\n\"chosen\",\n\"chott\",\n\"chough\",\n\"chouka\",\n\"choup\",\n\"chous\",\n\"chouse\",\n\"chouser\",\n\"chow\",\n\"chowder\",\n\"chowk\",\n\"chowry\",\n\"choya\",\n\"chria\",\n\"chrism\",\n\"chrisma\",\n\"chrisom\",\n\"chroma\",\n\"chrome\",\n\"chromic\",\n\"chromid\",\n\"chromo\",\n\"chromy\",\n\"chromyl\",\n\"chronal\",\n\"chronic\",\n\"chrotta\",\n\"chrysal\",\n\"chrysid\",\n\"chrysin\",\n\"chub\",\n\"chubbed\",\n\"chubby\",\n\"chuck\",\n\"chucker\",\n\"chuckle\",\n\"chucky\",\n\"chuddar\",\n\"chufa\",\n\"chuff\",\n\"chuffy\",\n\"chug\",\n\"chugger\",\n\"chuhra\",\n\"chukar\",\n\"chukker\",\n\"chukor\",\n\"chulan\",\n\"chullpa\",\n\"chum\",\n\"chummer\",\n\"chummy\",\n\"chump\",\n\"chumpy\",\n\"chun\",\n\"chunari\",\n\"chunga\",\n\"chunk\",\n\"chunky\",\n\"chunner\",\n\"chunnia\",\n\"chunter\",\n\"chupak\",\n\"chupon\",\n\"church\",\n\"churchy\",\n\"churel\",\n\"churl\",\n\"churled\",\n\"churly\",\n\"churm\",\n\"churn\",\n\"churr\",\n\"churrus\",\n\"chut\",\n\"chute\",\n\"chuter\",\n\"chutney\",\n\"chyack\",\n\"chyak\",\n\"chyle\",\n\"chylify\",\n\"chyloid\",\n\"chylous\",\n\"chymase\",\n\"chyme\",\n\"chymia\",\n\"chymic\",\n\"chymify\",\n\"chymous\",\n\"chypre\",\n\"chytra\",\n\"chytrid\",\n\"cibol\",\n\"cibory\",\n\"ciboule\",\n\"cicad\",\n\"cicada\",\n\"cicadid\",\n\"cicala\",\n\"cicely\",\n\"cicer\",\n\"cichlid\",\n\"cidarid\",\n\"cidaris\",\n\"cider\",\n\"cig\",\n\"cigala\",\n\"cigar\",\n\"cigua\",\n\"cilia\",\n\"ciliary\",\n\"ciliate\",\n\"cilice\",\n\"cilium\",\n\"cimbia\",\n\"cimelia\",\n\"cimex\",\n\"cimicid\",\n\"cimline\",\n\"cinch\",\n\"cincher\",\n\"cinclis\",\n\"cinct\",\n\"cinder\",\n\"cindery\",\n\"cine\",\n\"cinel\",\n\"cinema\",\n\"cinene\",\n\"cineole\",\n\"cinerea\",\n\"cingle\",\n\"cinnyl\",\n\"cinque\",\n\"cinter\",\n\"cinuran\",\n\"cion\",\n\"cipher\",\n\"cipo\",\n\"cipolin\",\n\"cippus\",\n\"circa\",\n\"circle\",\n\"circled\",\n\"circler\",\n\"circlet\",\n\"circuit\",\n\"circus\",\n\"circusy\",\n\"cirque\",\n\"cirrate\",\n\"cirri\",\n\"cirrose\",\n\"cirrous\",\n\"cirrus\",\n\"cirsoid\",\n\"ciruela\",\n\"cisco\",\n\"cise\",\n\"cisele\",\n\"cissing\",\n\"cissoid\",\n\"cist\",\n\"cista\",\n\"cistae\",\n\"cisted\",\n\"cistern\",\n\"cistic\",\n\"cit\",\n\"citable\",\n\"citadel\",\n\"citator\",\n\"cite\",\n\"citee\",\n\"citer\",\n\"citess\",\n\"cithara\",\n\"cither\",\n\"citied\",\n\"citify\",\n\"citizen\",\n\"citole\",\n\"citral\",\n\"citrate\",\n\"citrean\",\n\"citrene\",\n\"citric\",\n\"citril\",\n\"citrin\",\n\"citrine\",\n\"citron\",\n\"citrous\",\n\"citrus\",\n\"cittern\",\n\"citua\",\n\"city\",\n\"citydom\",\n\"cityful\",\n\"cityish\",\n\"cive\",\n\"civet\",\n\"civic\",\n\"civics\",\n\"civil\",\n\"civilly\",\n\"civism\",\n\"civvy\",\n\"cixiid\",\n\"clabber\",\n\"clachan\",\n\"clack\",\n\"clacker\",\n\"clacket\",\n\"clad\",\n\"cladine\",\n\"cladode\",\n\"cladose\",\n\"cladus\",\n\"clag\",\n\"claggum\",\n\"claggy\",\n\"claim\",\n\"claimer\",\n\"clairce\",\n\"claith\",\n\"claiver\",\n\"clam\",\n\"clamant\",\n\"clamb\",\n\"clamber\",\n\"clame\",\n\"clamer\",\n\"clammed\",\n\"clammer\",\n\"clammy\",\n\"clamor\",\n\"clamp\",\n\"clamper\",\n\"clan\",\n\"clang\",\n\"clangor\",\n\"clank\",\n\"clanned\",\n\"clap\",\n\"clapnet\",\n\"clapped\",\n\"clapper\",\n\"clapt\",\n\"claque\",\n\"claquer\",\n\"clarain\",\n\"claret\",\n\"clarify\",\n\"clarin\",\n\"clarion\",\n\"clarity\",\n\"clark\",\n\"claro\",\n\"clart\",\n\"clarty\",\n\"clary\",\n\"clash\",\n\"clasher\",\n\"clashy\",\n\"clasp\",\n\"clasper\",\n\"claspt\",\n\"class\",\n\"classed\",\n\"classer\",\n\"classes\",\n\"classic\",\n\"classis\",\n\"classy\",\n\"clastic\",\n\"clat\",\n\"clatch\",\n\"clatter\",\n\"clatty\",\n\"claught\",\n\"clausal\",\n\"clause\",\n\"claut\",\n\"clava\",\n\"claval\",\n\"clavate\",\n\"clave\",\n\"clavel\",\n\"claver\",\n\"clavial\",\n\"clavier\",\n\"claviol\",\n\"clavis\",\n\"clavola\",\n\"clavus\",\n\"clavy\",\n\"claw\",\n\"clawed\",\n\"clawer\",\n\"clawk\",\n\"clawker\",\n\"clay\",\n\"clayen\",\n\"clayer\",\n\"clayey\",\n\"clayish\",\n\"clayman\",\n\"claypan\",\n\"cleach\",\n\"clead\",\n\"cleaded\",\n\"cleam\",\n\"cleamer\",\n\"clean\",\n\"cleaner\",\n\"cleanly\",\n\"cleanse\",\n\"cleanup\",\n\"clear\",\n\"clearer\",\n\"clearly\",\n\"cleat\",\n\"cleave\",\n\"cleaver\",\n\"cleche\",\n\"cleck\",\n\"cled\",\n\"cledge\",\n\"cledgy\",\n\"clee\",\n\"cleek\",\n\"cleeked\",\n\"cleeky\",\n\"clef\",\n\"cleft\",\n\"clefted\",\n\"cleg\",\n\"clem\",\n\"clement\",\n\"clench\",\n\"cleoid\",\n\"clep\",\n\"clergy\",\n\"cleric\",\n\"clerid\",\n\"clerisy\",\n\"clerk\",\n\"clerkly\",\n\"cleruch\",\n\"cletch\",\n\"cleuch\",\n\"cleve\",\n\"clever\",\n\"clevis\",\n\"clew\",\n\"cliack\",\n\"cliche\",\n\"click\",\n\"clicker\",\n\"clicket\",\n\"clicky\",\n\"cliency\",\n\"client\",\n\"cliff\",\n\"cliffed\",\n\"cliffy\",\n\"clift\",\n\"clifty\",\n\"clima\",\n\"climata\",\n\"climate\",\n\"climath\",\n\"climax\",\n\"climb\",\n\"climber\",\n\"clime\",\n\"clinal\",\n\"clinch\",\n\"cline\",\n\"cling\",\n\"clinger\",\n\"clingy\",\n\"clinia\",\n\"clinic\",\n\"clinium\",\n\"clink\",\n\"clinker\",\n\"clinkum\",\n\"clinoid\",\n\"clint\",\n\"clinty\",\n\"clip\",\n\"clipei\",\n\"clipeus\",\n\"clipped\",\n\"clipper\",\n\"clips\",\n\"clipse\",\n\"clipt\",\n\"clique\",\n\"cliquy\",\n\"clisere\",\n\"clit\",\n\"clitch\",\n\"clite\",\n\"clites\",\n\"clithe\",\n\"clitia\",\n\"clition\",\n\"clitter\",\n\"clival\",\n\"clive\",\n\"clivers\",\n\"clivis\",\n\"clivus\",\n\"cloaca\",\n\"cloacal\",\n\"cloak\",\n\"cloaked\",\n\"cloam\",\n\"cloamen\",\n\"cloamer\",\n\"clobber\",\n\"clochan\",\n\"cloche\",\n\"clocher\",\n\"clock\",\n\"clocked\",\n\"clocker\",\n\"clod\",\n\"clodder\",\n\"cloddy\",\n\"clodlet\",\n\"cloff\",\n\"clog\",\n\"clogger\",\n\"cloggy\",\n\"cloghad\",\n\"clogwyn\",\n\"cloit\",\n\"clomb\",\n\"clomben\",\n\"clonal\",\n\"clone\",\n\"clonic\",\n\"clonism\",\n\"clonus\",\n\"cloof\",\n\"cloop\",\n\"cloot\",\n\"clootie\",\n\"clop\",\n\"close\",\n\"closed\",\n\"closely\",\n\"closen\",\n\"closer\",\n\"closet\",\n\"closh\",\n\"closish\",\n\"closter\",\n\"closure\",\n\"clot\",\n\"clotbur\",\n\"clote\",\n\"cloth\",\n\"clothe\",\n\"clothes\",\n\"clothy\",\n\"clotter\",\n\"clotty\",\n\"cloture\",\n\"cloud\",\n\"clouded\",\n\"cloudy\",\n\"clough\",\n\"clour\",\n\"clout\",\n\"clouted\",\n\"clouter\",\n\"clouty\",\n\"clove\",\n\"cloven\",\n\"clovene\",\n\"clover\",\n\"clovery\",\n\"clow\",\n\"clown\",\n\"cloy\",\n\"cloyer\",\n\"cloying\",\n\"club\",\n\"clubbed\",\n\"clubber\",\n\"clubby\",\n\"clubdom\",\n\"clubman\",\n\"cluck\",\n\"clue\",\n\"cluff\",\n\"clump\",\n\"clumpy\",\n\"clumse\",\n\"clumsy\",\n\"clunch\",\n\"clung\",\n\"clunk\",\n\"clupeid\",\n\"cluster\",\n\"clutch\",\n\"cluther\",\n\"clutter\",\n\"cly\",\n\"clyer\",\n\"clype\",\n\"clypeal\",\n\"clypeus\",\n\"clysis\",\n\"clysma\",\n\"clysmic\",\n\"clyster\",\n\"cnemial\",\n\"cnemis\",\n\"cnicin\",\n\"cnida\",\n\"coabode\",\n\"coach\",\n\"coachee\",\n\"coacher\",\n\"coachy\",\n\"coact\",\n\"coactor\",\n\"coadapt\",\n\"coadmit\",\n\"coadore\",\n\"coaged\",\n\"coagent\",\n\"coagula\",\n\"coaid\",\n\"coaita\",\n\"coak\",\n\"coakum\",\n\"coal\",\n\"coalbag\",\n\"coalbin\",\n\"coalbox\",\n\"coaler\",\n\"coalify\",\n\"coalize\",\n\"coalpit\",\n\"coaly\",\n\"coaming\",\n\"coannex\",\n\"coapt\",\n\"coarb\",\n\"coarse\",\n\"coarsen\",\n\"coast\",\n\"coastal\",\n\"coaster\",\n\"coat\",\n\"coated\",\n\"coatee\",\n\"coater\",\n\"coati\",\n\"coatie\",\n\"coating\",\n\"coax\",\n\"coaxal\",\n\"coaxer\",\n\"coaxial\",\n\"coaxing\",\n\"coaxy\",\n\"cob\",\n\"cobaea\",\n\"cobalt\",\n\"cobang\",\n\"cobbed\",\n\"cobber\",\n\"cobbing\",\n\"cobble\",\n\"cobbler\",\n\"cobbly\",\n\"cobbra\",\n\"cobby\",\n\"cobcab\",\n\"cobego\",\n\"cobhead\",\n\"cobia\",\n\"cobiron\",\n\"coble\",\n\"cobless\",\n\"cobloaf\",\n\"cobnut\",\n\"cobola\",\n\"cobourg\",\n\"cobra\",\n\"coburg\",\n\"cobweb\",\n\"cobwork\",\n\"coca\",\n\"cocaine\",\n\"cocash\",\n\"cocause\",\n\"coccal\",\n\"cocci\",\n\"coccid\",\n\"cocco\",\n\"coccoid\",\n\"coccous\",\n\"coccule\",\n\"coccus\",\n\"coccyx\",\n\"cochal\",\n\"cochief\",\n\"cochlea\",\n\"cock\",\n\"cockade\",\n\"cockal\",\n\"cocked\",\n\"cocker\",\n\"cocket\",\n\"cockeye\",\n\"cockily\",\n\"cocking\",\n\"cockish\",\n\"cockle\",\n\"cockled\",\n\"cockler\",\n\"cocklet\",\n\"cockly\",\n\"cockney\",\n\"cockpit\",\n\"cockshy\",\n\"cockup\",\n\"cocky\",\n\"coco\",\n\"cocoa\",\n\"cocoach\",\n\"coconut\",\n\"cocoon\",\n\"cocotte\",\n\"coctile\",\n\"coction\",\n\"cocuisa\",\n\"cocullo\",\n\"cocuyo\",\n\"cod\",\n\"coda\",\n\"codbank\",\n\"codder\",\n\"codding\",\n\"coddle\",\n\"coddler\",\n\"code\",\n\"codeine\",\n\"coder\",\n\"codex\",\n\"codfish\",\n\"codger\",\n\"codhead\",\n\"codical\",\n\"codices\",\n\"codicil\",\n\"codify\",\n\"codilla\",\n\"codille\",\n\"codist\",\n\"codling\",\n\"codman\",\n\"codo\",\n\"codol\",\n\"codon\",\n\"codworm\",\n\"coe\",\n\"coecal\",\n\"coecum\",\n\"coed\",\n\"coelar\",\n\"coelder\",\n\"coelect\",\n\"coelho\",\n\"coelia\",\n\"coeliac\",\n\"coelian\",\n\"coelin\",\n\"coeline\",\n\"coelom\",\n\"coeloma\",\n\"coempt\",\n\"coenact\",\n\"coenjoy\",\n\"coenobe\",\n\"coequal\",\n\"coerce\",\n\"coercer\",\n\"coetus\",\n\"coeval\",\n\"coexert\",\n\"coexist\",\n\"coff\",\n\"coffee\",\n\"coffer\",\n\"coffin\",\n\"coffle\",\n\"coffret\",\n\"coft\",\n\"cog\",\n\"cogence\",\n\"cogency\",\n\"cogener\",\n\"cogent\",\n\"cogged\",\n\"cogger\",\n\"coggie\",\n\"cogging\",\n\"coggle\",\n\"coggly\",\n\"coghle\",\n\"cogman\",\n\"cognac\",\n\"cognate\",\n\"cognize\",\n\"cogon\",\n\"cogonal\",\n\"cograil\",\n\"cogroad\",\n\"cogue\",\n\"cogway\",\n\"cogwood\",\n\"cohabit\",\n\"coheir\",\n\"cohere\",\n\"coherer\",\n\"cohibit\",\n\"coho\",\n\"cohoba\",\n\"cohol\",\n\"cohort\",\n\"cohosh\",\n\"cohune\",\n\"coif\",\n\"coifed\",\n\"coign\",\n\"coigue\",\n\"coil\",\n\"coiled\",\n\"coiler\",\n\"coiling\",\n\"coin\",\n\"coinage\",\n\"coiner\",\n\"coinfer\",\n\"coining\",\n\"cointer\",\n\"coiny\",\n\"coir\",\n\"coital\",\n\"coition\",\n\"coiture\",\n\"coitus\",\n\"cojudge\",\n\"cojuror\",\n\"coke\",\n\"cokeman\",\n\"coker\",\n\"cokery\",\n\"coking\",\n\"coky\",\n\"col\",\n\"cola\",\n\"colane\",\n\"colarin\",\n\"colate\",\n\"colauxe\",\n\"colback\",\n\"cold\",\n\"colder\",\n\"coldish\",\n\"coldly\",\n\"cole\",\n\"coletit\",\n\"coleur\",\n\"coli\",\n\"colibri\",\n\"colic\",\n\"colical\",\n\"colicky\",\n\"colima\",\n\"colin\",\n\"coling\",\n\"colitic\",\n\"colitis\",\n\"colk\",\n\"coll\",\n\"collage\",\n\"collar\",\n\"collard\",\n\"collare\",\n\"collate\",\n\"collaud\",\n\"collect\",\n\"colleen\",\n\"college\",\n\"collery\",\n\"collet\",\n\"colley\",\n\"collide\",\n\"collie\",\n\"collied\",\n\"collier\",\n\"collin\",\n\"colline\",\n\"colling\",\n\"collins\",\n\"collock\",\n\"colloid\",\n\"collop\",\n\"collude\",\n\"collum\",\n\"colly\",\n\"collyba\",\n\"colmar\",\n\"colobin\",\n\"colon\",\n\"colonel\",\n\"colonic\",\n\"colony\",\n\"color\",\n\"colored\",\n\"colorer\",\n\"colorin\",\n\"colors\",\n\"colory\",\n\"coloss\",\n\"colossi\",\n\"colove\",\n\"colp\",\n\"colpeo\",\n\"colport\",\n\"colpus\",\n\"colt\",\n\"colter\",\n\"coltish\",\n\"colugo\",\n\"columbo\",\n\"column\",\n\"colunar\",\n\"colure\",\n\"coly\",\n\"colyone\",\n\"colytic\",\n\"colyum\",\n\"colza\",\n\"coma\",\n\"comaker\",\n\"comal\",\n\"comamie\",\n\"comanic\",\n\"comart\",\n\"comate\",\n\"comb\",\n\"combat\",\n\"combed\",\n\"comber\",\n\"combine\",\n\"combing\",\n\"comble\",\n\"comboy\",\n\"combure\",\n\"combust\",\n\"comby\",\n\"come\",\n\"comedic\",\n\"comedo\",\n\"comedy\",\n\"comely\",\n\"comenic\",\n\"comer\",\n\"comes\",\n\"comet\",\n\"cometic\",\n\"comfit\",\n\"comfort\",\n\"comfrey\",\n\"comfy\",\n\"comic\",\n\"comical\",\n\"comicry\",\n\"coming\",\n\"comino\",\n\"comism\",\n\"comital\",\n\"comitia\",\n\"comity\",\n\"comma\",\n\"command\",\n\"commend\",\n\"comment\",\n\"commie\",\n\"commit\",\n\"commix\",\n\"commixt\",\n\"commode\",\n\"common\",\n\"commons\",\n\"commot\",\n\"commove\",\n\"communa\",\n\"commune\",\n\"commute\",\n\"comoid\",\n\"comose\",\n\"comourn\",\n\"comous\",\n\"compact\",\n\"company\",\n\"compare\",\n\"compart\",\n\"compass\",\n\"compear\",\n\"compeer\",\n\"compel\",\n\"compend\",\n\"compete\",\n\"compile\",\n\"complex\",\n\"complin\",\n\"complot\",\n\"comply\",\n\"compo\",\n\"compoer\",\n\"compole\",\n\"compone\",\n\"compony\",\n\"comport\",\n\"compos\",\n\"compose\",\n\"compost\",\n\"compote\",\n\"compreg\",\n\"compter\",\n\"compute\",\n\"comrade\",\n\"con\",\n\"conacre\",\n\"conal\",\n\"conamed\",\n\"conatus\",\n\"concave\",\n\"conceal\",\n\"concede\",\n\"conceit\",\n\"concent\",\n\"concept\",\n\"concern\",\n\"concert\",\n\"conch\",\n\"concha\",\n\"conchal\",\n\"conche\",\n\"conched\",\n\"concher\",\n\"conchy\",\n\"concile\",\n\"concise\",\n\"concoct\",\n\"concord\",\n\"concupy\",\n\"concur\",\n\"concuss\",\n\"cond\",\n\"condemn\",\n\"condign\",\n\"condite\",\n\"condole\",\n\"condone\",\n\"condor\",\n\"conduce\",\n\"conduct\",\n\"conduit\",\n\"condyle\",\n\"cone\",\n\"coned\",\n\"coneen\",\n\"coneine\",\n\"conelet\",\n\"coner\",\n\"cones\",\n\"confab\",\n\"confact\",\n\"confect\",\n\"confess\",\n\"confide\",\n\"confine\",\n\"confirm\",\n\"confix\",\n\"conflow\",\n\"conflux\",\n\"conform\",\n\"confuse\",\n\"confute\",\n\"conga\",\n\"congeal\",\n\"congee\",\n\"conger\",\n\"congest\",\n\"congius\",\n\"congou\",\n\"conic\",\n\"conical\",\n\"conicle\",\n\"conics\",\n\"conidia\",\n\"conifer\",\n\"conima\",\n\"conin\",\n\"conine\",\n\"conject\",\n\"conjoin\",\n\"conjure\",\n\"conjury\",\n\"conk\",\n\"conker\",\n\"conkers\",\n\"conky\",\n\"conn\",\n\"connach\",\n\"connate\",\n\"connect\",\n\"conner\",\n\"connex\",\n\"conning\",\n\"connive\",\n\"connote\",\n\"conoid\",\n\"conopid\",\n\"conquer\",\n\"conred\",\n\"consent\",\n\"consign\",\n\"consist\",\n\"consol\",\n\"console\",\n\"consort\",\n\"conspue\",\n\"constat\",\n\"consul\",\n\"consult\",\n\"consume\",\n\"consute\",\n\"contact\",\n\"contain\",\n\"conte\",\n\"contect\",\n\"contemn\",\n\"content\",\n\"conter\",\n\"contest\",\n\"context\",\n\"contise\",\n\"conto\",\n\"contort\",\n\"contour\",\n\"contra\",\n\"control\",\n\"contund\",\n\"contuse\",\n\"conure\",\n\"conus\",\n\"conusee\",\n\"conusor\",\n\"conuzee\",\n\"conuzor\",\n\"convect\",\n\"convene\",\n\"convent\",\n\"convert\",\n\"conveth\",\n\"convex\",\n\"convey\",\n\"convict\",\n\"convive\",\n\"convoke\",\n\"convoy\",\n\"cony\",\n\"coo\",\n\"cooba\",\n\"coodle\",\n\"cooee\",\n\"cooer\",\n\"coof\",\n\"cooing\",\n\"cooja\",\n\"cook\",\n\"cookdom\",\n\"cookee\",\n\"cooker\",\n\"cookery\",\n\"cooking\",\n\"cookish\",\n\"cookout\",\n\"cooky\",\n\"cool\",\n\"coolant\",\n\"coolen\",\n\"cooler\",\n\"coolie\",\n\"cooling\",\n\"coolish\",\n\"coolly\",\n\"coolth\",\n\"coolung\",\n\"cooly\",\n\"coom\",\n\"coomb\",\n\"coomy\",\n\"coon\",\n\"cooncan\",\n\"coonily\",\n\"coontie\",\n\"coony\",\n\"coop\",\n\"cooper\",\n\"coopery\",\n\"cooree\",\n\"coorie\",\n\"cooser\",\n\"coost\",\n\"coot\",\n\"cooter\",\n\"coothay\",\n\"cootie\",\n\"cop\",\n\"copa\",\n\"copable\",\n\"copaene\",\n\"copaiba\",\n\"copaiye\",\n\"copal\",\n\"copalm\",\n\"copart\",\n\"coparty\",\n\"cope\",\n\"copei\",\n\"copeman\",\n\"copen\",\n\"copepod\",\n\"coper\",\n\"coperta\",\n\"copied\",\n\"copier\",\n\"copilot\",\n\"coping\",\n\"copious\",\n\"copis\",\n\"copist\",\n\"copita\",\n\"copolar\",\n\"copped\",\n\"copper\",\n\"coppery\",\n\"coppet\",\n\"coppice\",\n\"coppin\",\n\"copping\",\n\"copple\",\n\"coppled\",\n\"coppy\",\n\"copr\",\n\"copra\",\n\"coprose\",\n\"copse\",\n\"copsing\",\n\"copsy\",\n\"copter\",\n\"copula\",\n\"copular\",\n\"copus\",\n\"copy\",\n\"copycat\",\n\"copyism\",\n\"copyist\",\n\"copyman\",\n\"coque\",\n\"coquet\",\n\"coquina\",\n\"coquita\",\n\"coquito\",\n\"cor\",\n\"cora\",\n\"corach\",\n\"coracle\",\n\"corah\",\n\"coraise\",\n\"coral\",\n\"coraled\",\n\"coram\",\n\"coranto\",\n\"corban\",\n\"corbeau\",\n\"corbeil\",\n\"corbel\",\n\"corbie\",\n\"corbula\",\n\"corcass\",\n\"corcir\",\n\"cord\",\n\"cordage\",\n\"cordant\",\n\"cordate\",\n\"cordax\",\n\"corded\",\n\"cordel\",\n\"corder\",\n\"cordial\",\n\"cordies\",\n\"cording\",\n\"cordite\",\n\"cordoba\",\n\"cordon\",\n\"cordy\",\n\"cordyl\",\n\"core\",\n\"corebel\",\n\"cored\",\n\"coreid\",\n\"coreign\",\n\"corella\",\n\"corer\",\n\"corf\",\n\"corge\",\n\"corgi\",\n\"corial\",\n\"coriin\",\n\"coring\",\n\"corinne\",\n\"corium\",\n\"cork\",\n\"corkage\",\n\"corke\",\n\"corked\",\n\"corker\",\n\"corking\",\n\"corkish\",\n\"corkite\",\n\"corky\",\n\"corm\",\n\"cormel\",\n\"cormoid\",\n\"cormous\",\n\"cormus\",\n\"corn\",\n\"cornage\",\n\"cornbin\",\n\"corncob\",\n\"cornea\",\n\"corneal\",\n\"cornein\",\n\"cornel\",\n\"corner\",\n\"cornet\",\n\"corneum\",\n\"cornic\",\n\"cornice\",\n\"cornin\",\n\"corning\",\n\"cornu\",\n\"cornual\",\n\"cornule\",\n\"cornute\",\n\"cornuto\",\n\"corny\",\n\"coroa\",\n\"corody\",\n\"corol\",\n\"corolla\",\n\"corona\",\n\"coronad\",\n\"coronae\",\n\"coronal\",\n\"coroner\",\n\"coronet\",\n\"corozo\",\n\"corp\",\n\"corpora\",\n\"corps\",\n\"corpse\",\n\"corpus\",\n\"corrade\",\n\"corral\",\n\"correal\",\n\"correct\",\n\"corrie\",\n\"corrige\",\n\"corrode\",\n\"corrupt\",\n\"corsac\",\n\"corsage\",\n\"corsair\",\n\"corse\",\n\"corset\",\n\"corsie\",\n\"corsite\",\n\"corta\",\n\"cortege\",\n\"cortex\",\n\"cortez\",\n\"cortin\",\n\"cortina\",\n\"coruco\",\n\"coruler\",\n\"corupay\",\n\"corver\",\n\"corvina\",\n\"corvine\",\n\"corvoid\",\n\"coryl\",\n\"corylin\",\n\"corymb\",\n\"coryza\",\n\"cos\",\n\"cosaque\",\n\"coscet\",\n\"coseat\",\n\"cosec\",\n\"cosech\",\n\"coseism\",\n\"coset\",\n\"cosh\",\n\"cosher\",\n\"coshery\",\n\"cosily\",\n\"cosine\",\n\"cosmic\",\n\"cosmism\",\n\"cosmist\",\n\"cosmos\",\n\"coss\",\n\"cossas\",\n\"cosse\",\n\"cosset\",\n\"cossid\",\n\"cost\",\n\"costa\",\n\"costal\",\n\"costar\",\n\"costard\",\n\"costate\",\n\"costean\",\n\"coster\",\n\"costing\",\n\"costive\",\n\"costly\",\n\"costrel\",\n\"costula\",\n\"costume\",\n\"cosy\",\n\"cot\",\n\"cotch\",\n\"cote\",\n\"coteful\",\n\"coterie\",\n\"coth\",\n\"cothe\",\n\"cothish\",\n\"cothon\",\n\"cothurn\",\n\"cothy\",\n\"cotidal\",\n\"cotise\",\n\"cotland\",\n\"cotman\",\n\"coto\",\n\"cotoin\",\n\"cotoro\",\n\"cotrine\",\n\"cotset\",\n\"cotta\",\n\"cottage\",\n\"cotte\",\n\"cotted\",\n\"cotter\",\n\"cottid\",\n\"cottier\",\n\"cottoid\",\n\"cotton\",\n\"cottony\",\n\"cotty\",\n\"cotuit\",\n\"cotula\",\n\"cotutor\",\n\"cotwin\",\n\"cotwist\",\n\"cotyla\",\n\"cotylar\",\n\"cotype\",\n\"couac\",\n\"coucal\",\n\"couch\",\n\"couched\",\n\"couchee\",\n\"coucher\",\n\"couchy\",\n\"coude\",\n\"coudee\",\n\"coue\",\n\"cougar\",\n\"cough\",\n\"cougher\",\n\"cougnar\",\n\"coul\",\n\"could\",\n\"coulee\",\n\"coulomb\",\n\"coulure\",\n\"couma\",\n\"coumara\",\n\"council\",\n\"counite\",\n\"counsel\",\n\"count\",\n\"counter\",\n\"countor\",\n\"country\",\n\"county\",\n\"coup\",\n\"coupage\",\n\"coupe\",\n\"couped\",\n\"coupee\",\n\"couper\",\n\"couple\",\n\"coupled\",\n\"coupler\",\n\"couplet\",\n\"coupon\",\n\"coupure\",\n\"courage\",\n\"courant\",\n\"courap\",\n\"courb\",\n\"courge\",\n\"courida\",\n\"courier\",\n\"couril\",\n\"courlan\",\n\"course\",\n\"coursed\",\n\"courser\",\n\"court\",\n\"courter\",\n\"courtin\",\n\"courtly\",\n\"cousin\",\n\"cousiny\",\n\"coutel\",\n\"couter\",\n\"couth\",\n\"couthie\",\n\"coutil\",\n\"couvade\",\n\"couxia\",\n\"covado\",\n\"cove\",\n\"coved\",\n\"covent\",\n\"cover\",\n\"covered\",\n\"coverer\",\n\"covert\",\n\"covet\",\n\"coveter\",\n\"covey\",\n\"covid\",\n\"covin\",\n\"coving\",\n\"covisit\",\n\"covite\",\n\"cow\",\n\"cowal\",\n\"coward\",\n\"cowardy\",\n\"cowbane\",\n\"cowbell\",\n\"cowbind\",\n\"cowbird\",\n\"cowboy\",\n\"cowdie\",\n\"coween\",\n\"cower\",\n\"cowfish\",\n\"cowgate\",\n\"cowgram\",\n\"cowhage\",\n\"cowheel\",\n\"cowherb\",\n\"cowherd\",\n\"cowhide\",\n\"cowhorn\",\n\"cowish\",\n\"cowitch\",\n\"cowl\",\n\"cowle\",\n\"cowled\",\n\"cowlick\",\n\"cowlike\",\n\"cowling\",\n\"cowman\",\n\"cowpath\",\n\"cowpea\",\n\"cowpen\",\n\"cowpock\",\n\"cowpox\",\n\"cowrie\",\n\"cowroid\",\n\"cowshed\",\n\"cowskin\",\n\"cowslip\",\n\"cowtail\",\n\"cowweed\",\n\"cowy\",\n\"cowyard\",\n\"cox\",\n\"coxa\",\n\"coxal\",\n\"coxcomb\",\n\"coxite\",\n\"coxitis\",\n\"coxy\",\n\"coy\",\n\"coyan\",\n\"coydog\",\n\"coyish\",\n\"coyly\",\n\"coyness\",\n\"coynye\",\n\"coyo\",\n\"coyol\",\n\"coyote\",\n\"coypu\",\n\"coyure\",\n\"coz\",\n\"coze\",\n\"cozen\",\n\"cozener\",\n\"cozier\",\n\"cozily\",\n\"cozy\",\n\"crab\",\n\"crabbed\",\n\"crabber\",\n\"crabby\",\n\"craber\",\n\"crablet\",\n\"crabman\",\n\"crack\",\n\"cracked\",\n\"cracker\",\n\"crackle\",\n\"crackly\",\n\"cracky\",\n\"craddy\",\n\"cradge\",\n\"cradle\",\n\"cradler\",\n\"craft\",\n\"crafty\",\n\"crag\",\n\"craggan\",\n\"cragged\",\n\"craggy\",\n\"craichy\",\n\"crain\",\n\"craisey\",\n\"craizey\",\n\"crajuru\",\n\"crake\",\n\"crakow\",\n\"cram\",\n\"crambe\",\n\"crambid\",\n\"cramble\",\n\"crambly\",\n\"crambo\",\n\"crammer\",\n\"cramp\",\n\"cramped\",\n\"cramper\",\n\"crampet\",\n\"crampon\",\n\"crampy\",\n\"cran\",\n\"cranage\",\n\"crance\",\n\"crane\",\n\"craner\",\n\"craney\",\n\"crania\",\n\"craniad\",\n\"cranial\",\n\"cranian\",\n\"cranic\",\n\"cranium\",\n\"crank\",\n\"cranked\",\n\"cranker\",\n\"crankle\",\n\"crankly\",\n\"crankum\",\n\"cranky\",\n\"crannog\",\n\"cranny\",\n\"crants\",\n\"crap\",\n\"crapaud\",\n\"crape\",\n\"crappie\",\n\"crappin\",\n\"crapple\",\n\"crappo\",\n\"craps\",\n\"crapy\",\n\"crare\",\n\"crash\",\n\"crasher\",\n\"crasis\",\n\"crass\",\n\"crassly\",\n\"cratch\",\n\"crate\",\n\"crater\",\n\"craunch\",\n\"cravat\",\n\"crave\",\n\"craven\",\n\"craver\",\n\"craving\",\n\"cravo\",\n\"craw\",\n\"crawdad\",\n\"crawful\",\n\"crawl\",\n\"crawler\",\n\"crawley\",\n\"crawly\",\n\"crawm\",\n\"crawtae\",\n\"crayer\",\n\"crayon\",\n\"craze\",\n\"crazed\",\n\"crazily\",\n\"crazy\",\n\"crea\",\n\"creagh\",\n\"creaght\",\n\"creak\",\n\"creaker\",\n\"creaky\",\n\"cream\",\n\"creamer\",\n\"creamy\",\n\"creance\",\n\"creant\",\n\"crease\",\n\"creaser\",\n\"creasy\",\n\"creat\",\n\"create\",\n\"creatic\",\n\"creator\",\n\"creche\",\n\"credent\",\n\"credit\",\n\"cree\",\n\"creed\",\n\"creedal\",\n\"creeded\",\n\"creek\",\n\"creeker\",\n\"creeky\",\n\"creel\",\n\"creeler\",\n\"creem\",\n\"creen\",\n\"creep\",\n\"creeper\",\n\"creepie\",\n\"creepy\",\n\"creese\",\n\"creesh\",\n\"creeshy\",\n\"cremate\",\n\"cremone\",\n\"cremor\",\n\"cremule\",\n\"crena\",\n\"crenate\",\n\"crenel\",\n\"crenele\",\n\"crenic\",\n\"crenula\",\n\"creole\",\n\"creosol\",\n\"crepe\",\n\"crepine\",\n\"crepon\",\n\"crept\",\n\"crepy\",\n\"cresol\",\n\"cresoxy\",\n\"cress\",\n\"cressed\",\n\"cresset\",\n\"cresson\",\n\"cressy\",\n\"crest\",\n\"crested\",\n\"cresyl\",\n\"creta\",\n\"cretic\",\n\"cretify\",\n\"cretin\",\n\"cretion\",\n\"crevice\",\n\"crew\",\n\"crewel\",\n\"crewer\",\n\"crewman\",\n\"crib\",\n\"cribber\",\n\"cribble\",\n\"cribo\",\n\"cribral\",\n\"cric\",\n\"crick\",\n\"cricket\",\n\"crickey\",\n\"crickle\",\n\"cricoid\",\n\"cried\",\n\"crier\",\n\"criey\",\n\"crig\",\n\"crile\",\n\"crime\",\n\"crimine\",\n\"crimp\",\n\"crimper\",\n\"crimple\",\n\"crimpy\",\n\"crimson\",\n\"crin\",\n\"crinal\",\n\"crine\",\n\"crined\",\n\"crinet\",\n\"cringe\",\n\"cringer\",\n\"cringle\",\n\"crinite\",\n\"crink\",\n\"crinkle\",\n\"crinkly\",\n\"crinoid\",\n\"crinose\",\n\"crinula\",\n\"cripes\",\n\"cripple\",\n\"cripply\",\n\"crises\",\n\"crisic\",\n\"crisis\",\n\"crisp\",\n\"crisped\",\n\"crisper\",\n\"crisply\",\n\"crispy\",\n\"criss\",\n\"crissal\",\n\"crissum\",\n\"crista\",\n\"critch\",\n\"crith\",\n\"critic\",\n\"crizzle\",\n\"cro\",\n\"croak\",\n\"croaker\",\n\"croaky\",\n\"croc\",\n\"crocard\",\n\"croceic\",\n\"crocein\",\n\"croche\",\n\"crochet\",\n\"croci\",\n\"crocin\",\n\"crock\",\n\"crocker\",\n\"crocket\",\n\"crocky\",\n\"crocus\",\n\"croft\",\n\"crofter\",\n\"crome\",\n\"crone\",\n\"cronet\",\n\"cronish\",\n\"cronk\",\n\"crony\",\n\"crood\",\n\"croodle\",\n\"crook\",\n\"crooked\",\n\"crooken\",\n\"crookle\",\n\"crool\",\n\"croon\",\n\"crooner\",\n\"crop\",\n\"cropman\",\n\"croppa\",\n\"cropper\",\n\"croppie\",\n\"croppy\",\n\"croquet\",\n\"crore\",\n\"crosa\",\n\"crosier\",\n\"crosnes\",\n\"cross\",\n\"crosse\",\n\"crossed\",\n\"crosser\",\n\"crossly\",\n\"crotal\",\n\"crotalo\",\n\"crotch\",\n\"crotchy\",\n\"crotin\",\n\"crottle\",\n\"crotyl\",\n\"crouch\",\n\"croup\",\n\"croupal\",\n\"croupe\",\n\"croupy\",\n\"crouse\",\n\"crout\",\n\"croute\",\n\"crouton\",\n\"crow\",\n\"crowbar\",\n\"crowd\",\n\"crowded\",\n\"crowder\",\n\"crowdy\",\n\"crower\",\n\"crowhop\",\n\"crowing\",\n\"crowl\",\n\"crown\",\n\"crowned\",\n\"crowner\",\n\"crowtoe\",\n\"croy\",\n\"croyden\",\n\"croydon\",\n\"croze\",\n\"crozer\",\n\"crozzle\",\n\"crozzly\",\n\"crubeen\",\n\"cruce\",\n\"cruces\",\n\"cruche\",\n\"crucial\",\n\"crucian\",\n\"crucify\",\n\"crucily\",\n\"cruck\",\n\"crude\",\n\"crudely\",\n\"crudity\",\n\"cruel\",\n\"cruelly\",\n\"cruels\",\n\"cruelty\",\n\"cruent\",\n\"cruet\",\n\"cruety\",\n\"cruise\",\n\"cruiser\",\n\"cruive\",\n\"cruller\",\n\"crum\",\n\"crumb\",\n\"crumber\",\n\"crumble\",\n\"crumbly\",\n\"crumby\",\n\"crumen\",\n\"crumlet\",\n\"crummie\",\n\"crummy\",\n\"crump\",\n\"crumper\",\n\"crumpet\",\n\"crumple\",\n\"crumply\",\n\"crumpy\",\n\"crunch\",\n\"crunchy\",\n\"crunk\",\n\"crunkle\",\n\"crunode\",\n\"crunt\",\n\"cruor\",\n\"crupper\",\n\"crural\",\n\"crureus\",\n\"crus\",\n\"crusade\",\n\"crusado\",\n\"cruse\",\n\"crush\",\n\"crushed\",\n\"crusher\",\n\"crusie\",\n\"crusily\",\n\"crust\",\n\"crusta\",\n\"crustal\",\n\"crusted\",\n\"cruster\",\n\"crusty\",\n\"crutch\",\n\"cruth\",\n\"crutter\",\n\"crux\",\n\"cry\",\n\"cryable\",\n\"crybaby\",\n\"crying\",\n\"cryogen\",\n\"cryosel\",\n\"crypt\",\n\"crypta\",\n\"cryptal\",\n\"crypted\",\n\"cryptic\",\n\"crystal\",\n\"crystic\",\n\"csardas\",\n\"ctene\",\n\"ctenoid\",\n\"cuadra\",\n\"cuarta\",\n\"cub\",\n\"cubage\",\n\"cubbing\",\n\"cubbish\",\n\"cubby\",\n\"cubdom\",\n\"cube\",\n\"cubeb\",\n\"cubelet\",\n\"cuber\",\n\"cubhood\",\n\"cubi\",\n\"cubic\",\n\"cubica\",\n\"cubical\",\n\"cubicle\",\n\"cubicly\",\n\"cubism\",\n\"cubist\",\n\"cubit\",\n\"cubital\",\n\"cubited\",\n\"cubito\",\n\"cubitus\",\n\"cuboid\",\n\"cuck\",\n\"cuckold\",\n\"cuckoo\",\n\"cuculla\",\n\"cud\",\n\"cudava\",\n\"cudbear\",\n\"cudden\",\n\"cuddle\",\n\"cuddly\",\n\"cuddy\",\n\"cudgel\",\n\"cudweed\",\n\"cue\",\n\"cueball\",\n\"cueca\",\n\"cueist\",\n\"cueman\",\n\"cuerda\",\n\"cuesta\",\n\"cuff\",\n\"cuffer\",\n\"cuffin\",\n\"cuffy\",\n\"cuinage\",\n\"cuir\",\n\"cuirass\",\n\"cuisine\",\n\"cuisse\",\n\"cuissen\",\n\"cuisten\",\n\"cuke\",\n\"culbut\",\n\"culebra\",\n\"culet\",\n\"culeus\",\n\"culgee\",\n\"culicid\",\n\"cull\",\n\"culla\",\n\"cullage\",\n\"culler\",\n\"cullet\",\n\"culling\",\n\"cullion\",\n\"cullis\",\n\"cully\",\n\"culm\",\n\"culmen\",\n\"culmy\",\n\"culotte\",\n\"culpa\",\n\"culpose\",\n\"culprit\",\n\"cult\",\n\"cultch\",\n\"cultic\",\n\"cultish\",\n\"cultism\",\n\"cultist\",\n\"cultual\",\n\"culture\",\n\"cultus\",\n\"culver\",\n\"culvert\",\n\"cum\",\n\"cumal\",\n\"cumay\",\n\"cumbent\",\n\"cumber\",\n\"cumbha\",\n\"cumbly\",\n\"cumbre\",\n\"cumbu\",\n\"cumene\",\n\"cumenyl\",\n\"cumhal\",\n\"cumic\",\n\"cumidin\",\n\"cumin\",\n\"cuminal\",\n\"cuminic\",\n\"cuminol\",\n\"cuminyl\",\n\"cummer\",\n\"cummin\",\n\"cumol\",\n\"cump\",\n\"cumshaw\",\n\"cumular\",\n\"cumuli\",\n\"cumulus\",\n\"cumyl\",\n\"cuneal\",\n\"cuneate\",\n\"cunette\",\n\"cuneus\",\n\"cunila\",\n\"cunjah\",\n\"cunjer\",\n\"cunner\",\n\"cunning\",\n\"cunye\",\n\"cuorin\",\n\"cup\",\n\"cupay\",\n\"cupcake\",\n\"cupel\",\n\"cupeler\",\n\"cupful\",\n\"cuphead\",\n\"cupidon\",\n\"cupless\",\n\"cupman\",\n\"cupmate\",\n\"cupola\",\n\"cupolar\",\n\"cupped\",\n\"cupper\",\n\"cupping\",\n\"cuppy\",\n\"cuprene\",\n\"cupric\",\n\"cupride\",\n\"cuprite\",\n\"cuproid\",\n\"cuprose\",\n\"cuprous\",\n\"cuprum\",\n\"cupseed\",\n\"cupula\",\n\"cupule\",\n\"cur\",\n\"curable\",\n\"curably\",\n\"curacao\",\n\"curacy\",\n\"curare\",\n\"curate\",\n\"curatel\",\n\"curatic\",\n\"curator\",\n\"curb\",\n\"curber\",\n\"curbing\",\n\"curby\",\n\"curcas\",\n\"curch\",\n\"curd\",\n\"curdle\",\n\"curdler\",\n\"curdly\",\n\"curdy\",\n\"cure\",\n\"curer\",\n\"curette\",\n\"curfew\",\n\"curial\",\n\"curiate\",\n\"curie\",\n\"curin\",\n\"curine\",\n\"curing\",\n\"curio\",\n\"curiosa\",\n\"curioso\",\n\"curious\",\n\"curite\",\n\"curium\",\n\"curl\",\n\"curled\",\n\"curler\",\n\"curlew\",\n\"curlike\",\n\"curlily\",\n\"curling\",\n\"curly\",\n\"curn\",\n\"curney\",\n\"curnock\",\n\"curple\",\n\"curr\",\n\"currach\",\n\"currack\",\n\"curragh\",\n\"currant\",\n\"current\",\n\"curried\",\n\"currier\",\n\"currish\",\n\"curry\",\n\"cursal\",\n\"curse\",\n\"cursed\",\n\"curser\",\n\"curship\",\n\"cursive\",\n\"cursor\",\n\"cursory\",\n\"curst\",\n\"curstly\",\n\"cursus\",\n\"curt\",\n\"curtail\",\n\"curtain\",\n\"curtal\",\n\"curtate\",\n\"curtesy\",\n\"curtly\",\n\"curtsy\",\n\"curua\",\n\"curuba\",\n\"curule\",\n\"cururo\",\n\"curvant\",\n\"curvate\",\n\"curve\",\n\"curved\",\n\"curver\",\n\"curvet\",\n\"curvity\",\n\"curvous\",\n\"curvy\",\n\"cuscus\",\n\"cusec\",\n\"cush\",\n\"cushag\",\n\"cushat\",\n\"cushaw\",\n\"cushion\",\n\"cushy\",\n\"cusie\",\n\"cusk\",\n\"cusp\",\n\"cuspal\",\n\"cuspate\",\n\"cusped\",\n\"cuspid\",\n\"cuspule\",\n\"cuss\",\n\"cussed\",\n\"cusser\",\n\"cusso\",\n\"custard\",\n\"custody\",\n\"custom\",\n\"customs\",\n\"cut\",\n\"cutaway\",\n\"cutback\",\n\"cutch\",\n\"cutcher\",\n\"cute\",\n\"cutely\",\n\"cutheal\",\n\"cuticle\",\n\"cutie\",\n\"cutin\",\n\"cutis\",\n\"cutitis\",\n\"cutlass\",\n\"cutler\",\n\"cutlery\",\n\"cutlet\",\n\"cutling\",\n\"cutlips\",\n\"cutoff\",\n\"cutout\",\n\"cutover\",\n\"cuttage\",\n\"cuttail\",\n\"cutted\",\n\"cutter\",\n\"cutting\",\n\"cuttle\",\n\"cuttler\",\n\"cuttoo\",\n\"cutty\",\n\"cutup\",\n\"cutweed\",\n\"cutwork\",\n\"cutworm\",\n\"cuvette\",\n\"cuvy\",\n\"cuya\",\n\"cwierc\",\n\"cwm\",\n\"cyan\",\n\"cyanate\",\n\"cyanean\",\n\"cyanic\",\n\"cyanide\",\n\"cyanin\",\n\"cyanine\",\n\"cyanite\",\n\"cyanize\",\n\"cyanol\",\n\"cyanole\",\n\"cyanose\",\n\"cyanus\",\n\"cyath\",\n\"cyathos\",\n\"cyathus\",\n\"cycad\",\n\"cyclane\",\n\"cyclar\",\n\"cyclas\",\n\"cycle\",\n\"cyclene\",\n\"cycler\",\n\"cyclian\",\n\"cyclic\",\n\"cyclide\",\n\"cycling\",\n\"cyclism\",\n\"cyclist\",\n\"cyclize\",\n\"cycloid\",\n\"cyclone\",\n\"cyclope\",\n\"cyclopy\",\n\"cyclose\",\n\"cyclus\",\n\"cyesis\",\n\"cygnet\",\n\"cygnine\",\n\"cyke\",\n\"cylix\",\n\"cyma\",\n\"cymar\",\n\"cymba\",\n\"cymbal\",\n\"cymbalo\",\n\"cymbate\",\n\"cyme\",\n\"cymelet\",\n\"cymene\",\n\"cymling\",\n\"cymoid\",\n\"cymose\",\n\"cymous\",\n\"cymule\",\n\"cynebot\",\n\"cynic\",\n\"cynical\",\n\"cynipid\",\n\"cynism\",\n\"cynoid\",\n\"cyp\",\n\"cypre\",\n\"cypres\",\n\"cypress\",\n\"cyprine\",\n\"cypsela\",\n\"cyrus\",\n\"cyst\",\n\"cystal\",\n\"cysted\",\n\"cystic\",\n\"cystid\",\n\"cystine\",\n\"cystis\",\n\"cystoid\",\n\"cystoma\",\n\"cystose\",\n\"cystous\",\n\"cytase\",\n\"cytasic\",\n\"cytitis\",\n\"cytode\",\n\"cytoid\",\n\"cytoma\",\n\"cyton\",\n\"cytost\",\n\"cytula\",\n\"czar\",\n\"czardas\",\n\"czardom\",\n\"czarian\",\n\"czaric\",\n\"czarina\",\n\"czarish\",\n\"czarism\",\n\"czarist\",\n\"d\",\n\"da\",\n\"daalder\",\n\"dab\",\n\"dabb\",\n\"dabba\",\n\"dabber\",\n\"dabble\",\n\"dabbler\",\n\"dabby\",\n\"dablet\",\n\"daboia\",\n\"daboya\",\n\"dabster\",\n\"dace\",\n\"dacite\",\n\"dacitic\",\n\"dacker\",\n\"dacoit\",\n\"dacoity\",\n\"dacryon\",\n\"dactyl\",\n\"dad\",\n\"dada\",\n\"dadap\",\n\"dadder\",\n\"daddle\",\n\"daddock\",\n\"daddy\",\n\"dade\",\n\"dado\",\n\"dae\",\n\"daedal\",\n\"daemon\",\n\"daemony\",\n\"daer\",\n\"daff\",\n\"daffery\",\n\"daffing\",\n\"daffish\",\n\"daffle\",\n\"daffy\",\n\"daft\",\n\"daftly\",\n\"dag\",\n\"dagaba\",\n\"dagame\",\n\"dagassa\",\n\"dagesh\",\n\"dagga\",\n\"dagger\",\n\"daggers\",\n\"daggle\",\n\"daggly\",\n\"daggy\",\n\"daghesh\",\n\"daglock\",\n\"dagoba\",\n\"dags\",\n\"dah\",\n\"dahoon\",\n\"daidle\",\n\"daidly\",\n\"daiker\",\n\"daikon\",\n\"daily\",\n\"daimen\",\n\"daimio\",\n\"daimon\",\n\"dain\",\n\"daincha\",\n\"dainty\",\n\"daira\",\n\"dairi\",\n\"dairy\",\n\"dais\",\n\"daisied\",\n\"daisy\",\n\"daitya\",\n\"daiva\",\n\"dak\",\n\"daker\",\n\"dakir\",\n\"dal\",\n\"dalar\",\n\"dale\",\n\"daleman\",\n\"daler\",\n\"daleth\",\n\"dali\",\n\"dalk\",\n\"dallack\",\n\"dalle\",\n\"dalles\",\n\"dallier\",\n\"dally\",\n\"dalt\",\n\"dalteen\",\n\"dalton\",\n\"dam\",\n\"dama\",\n\"damage\",\n\"damager\",\n\"damages\",\n\"daman\",\n\"damask\",\n\"damasse\",\n\"dambose\",\n\"dambrod\",\n\"dame\",\n\"damiana\",\n\"damie\",\n\"damier\",\n\"damine\",\n\"damlike\",\n\"dammar\",\n\"damme\",\n\"dammer\",\n\"dammish\",\n\"damn\",\n\"damned\",\n\"damner\",\n\"damnify\",\n\"damning\",\n\"damnous\",\n\"damp\",\n\"dampang\",\n\"damped\",\n\"dampen\",\n\"damper\",\n\"damping\",\n\"dampish\",\n\"damply\",\n\"dampy\",\n\"damsel\",\n\"damson\",\n\"dan\",\n\"danaid\",\n\"danaide\",\n\"danaine\",\n\"danaite\",\n\"dance\",\n\"dancer\",\n\"dancery\",\n\"dancing\",\n\"dand\",\n\"danda\",\n\"dander\",\n\"dandify\",\n\"dandily\",\n\"dandle\",\n\"dandler\",\n\"dandy\",\n\"dang\",\n\"danger\",\n\"dangle\",\n\"dangler\",\n\"danglin\",\n\"danio\",\n\"dank\",\n\"dankish\",\n\"dankly\",\n\"danli\",\n\"danner\",\n\"dannock\",\n\"dansant\",\n\"danta\",\n\"danton\",\n\"dao\",\n\"daoine\",\n\"dap\",\n\"daphnin\",\n\"dapicho\",\n\"dapico\",\n\"dapifer\",\n\"dapper\",\n\"dapple\",\n\"dappled\",\n\"dar\",\n\"darac\",\n\"daraf\",\n\"darat\",\n\"darbha\",\n\"darby\",\n\"dardaol\",\n\"dare\",\n\"dareall\",\n\"dareful\",\n\"darer\",\n\"daresay\",\n\"darg\",\n\"dargah\",\n\"darger\",\n\"dargue\",\n\"dari\",\n\"daribah\",\n\"daric\",\n\"daring\",\n\"dariole\",\n\"dark\",\n\"darken\",\n\"darkful\",\n\"darkish\",\n\"darkle\",\n\"darkly\",\n\"darky\",\n\"darling\",\n\"darn\",\n\"darned\",\n\"darnel\",\n\"darner\",\n\"darnex\",\n\"darning\",\n\"daroga\",\n\"daroo\",\n\"darr\",\n\"darrein\",\n\"darst\",\n\"dart\",\n\"dartars\",\n\"darter\",\n\"darting\",\n\"dartle\",\n\"dartman\",\n\"dartoic\",\n\"dartoid\",\n\"dartos\",\n\"dartre\",\n\"darts\",\n\"darzee\",\n\"das\",\n\"dash\",\n\"dashed\",\n\"dashee\",\n\"dasheen\",\n\"dasher\",\n\"dashing\",\n\"dashpot\",\n\"dashy\",\n\"dasi\",\n\"dasnt\",\n\"dassie\",\n\"dassy\",\n\"dastard\",\n\"dastur\",\n\"dasturi\",\n\"dasyure\",\n\"data\",\n\"datable\",\n\"datably\",\n\"dataria\",\n\"datary\",\n\"datch\",\n\"datcha\",\n\"date\",\n\"dater\",\n\"datil\",\n\"dating\",\n\"dation\",\n\"datival\",\n\"dative\",\n\"dattock\",\n\"datum\",\n\"daturic\",\n\"daub\",\n\"daube\",\n\"dauber\",\n\"daubery\",\n\"daubing\",\n\"dauby\",\n\"daud\",\n\"daunch\",\n\"dauncy\",\n\"daunt\",\n\"daunter\",\n\"daunton\",\n\"dauphin\",\n\"daut\",\n\"dautie\",\n\"dauw\",\n\"davach\",\n\"daven\",\n\"daver\",\n\"daverdy\",\n\"davit\",\n\"davoch\",\n\"davy\",\n\"davyne\",\n\"daw\",\n\"dawdle\",\n\"dawdler\",\n\"dawdy\",\n\"dawish\",\n\"dawkin\",\n\"dawn\",\n\"dawning\",\n\"dawny\",\n\"dawtet\",\n\"dawtit\",\n\"dawut\",\n\"day\",\n\"dayal\",\n\"daybeam\",\n\"daybook\",\n\"daydawn\",\n\"dayfly\",\n\"dayless\",\n\"daylit\",\n\"daylong\",\n\"dayman\",\n\"daymare\",\n\"daymark\",\n\"dayroom\",\n\"days\",\n\"daysman\",\n\"daystar\",\n\"daytale\",\n\"daytide\",\n\"daytime\",\n\"dayward\",\n\"daywork\",\n\"daywrit\",\n\"daze\",\n\"dazed\",\n\"dazedly\",\n\"dazy\",\n\"dazzle\",\n\"dazzler\",\n\"de\",\n\"deacon\",\n\"dead\",\n\"deaden\",\n\"deader\",\n\"deadeye\",\n\"deading\",\n\"deadish\",\n\"deadly\",\n\"deadman\",\n\"deadpan\",\n\"deadpay\",\n\"deaf\",\n\"deafen\",\n\"deafish\",\n\"deafly\",\n\"deair\",\n\"deal\",\n\"dealate\",\n\"dealer\",\n\"dealing\",\n\"dealt\",\n\"dean\",\n\"deaner\",\n\"deanery\",\n\"deaness\",\n\"dear\",\n\"dearie\",\n\"dearly\",\n\"dearth\",\n\"deary\",\n\"deash\",\n\"deasil\",\n\"death\",\n\"deathin\",\n\"deathly\",\n\"deathy\",\n\"deave\",\n\"deavely\",\n\"deb\",\n\"debacle\",\n\"debadge\",\n\"debar\",\n\"debark\",\n\"debase\",\n\"debaser\",\n\"debate\",\n\"debater\",\n\"debauch\",\n\"debby\",\n\"debeige\",\n\"deben\",\n\"debile\",\n\"debind\",\n\"debit\",\n\"debord\",\n\"debosh\",\n\"debouch\",\n\"debride\",\n\"debrief\",\n\"debris\",\n\"debt\",\n\"debtee\",\n\"debtful\",\n\"debtor\",\n\"debunk\",\n\"debus\",\n\"debut\",\n\"decad\",\n\"decadal\",\n\"decade\",\n\"decadic\",\n\"decafid\",\n\"decagon\",\n\"decal\",\n\"decamp\",\n\"decan\",\n\"decanal\",\n\"decane\",\n\"decani\",\n\"decant\",\n\"decap\",\n\"decapod\",\n\"decarch\",\n\"decare\",\n\"decart\",\n\"decast\",\n\"decate\",\n\"decator\",\n\"decatyl\",\n\"decay\",\n\"decayed\",\n\"decayer\",\n\"decease\",\n\"deceit\",\n\"deceive\",\n\"decence\",\n\"decency\",\n\"decene\",\n\"decent\",\n\"decenyl\",\n\"decern\",\n\"decess\",\n\"deciare\",\n\"decibel\",\n\"decide\",\n\"decided\",\n\"decider\",\n\"decidua\",\n\"decil\",\n\"decile\",\n\"decima\",\n\"decimal\",\n\"deck\",\n\"decke\",\n\"decked\",\n\"deckel\",\n\"decker\",\n\"deckie\",\n\"decking\",\n\"deckle\",\n\"declaim\",\n\"declare\",\n\"declass\",\n\"decline\",\n\"declive\",\n\"decoat\",\n\"decoct\",\n\"decode\",\n\"decoic\",\n\"decoke\",\n\"decolor\",\n\"decorum\",\n\"decoy\",\n\"decoyer\",\n\"decream\",\n\"decree\",\n\"decreer\",\n\"decreet\",\n\"decrete\",\n\"decrew\",\n\"decrial\",\n\"decried\",\n\"decrier\",\n\"decrown\",\n\"decry\",\n\"decuman\",\n\"decuple\",\n\"decuria\",\n\"decurve\",\n\"decury\",\n\"decus\",\n\"decyl\",\n\"decylic\",\n\"decyne\",\n\"dedimus\",\n\"dedo\",\n\"deduce\",\n\"deduct\",\n\"dee\",\n\"deed\",\n\"deedbox\",\n\"deedeed\",\n\"deedful\",\n\"deedily\",\n\"deedy\",\n\"deem\",\n\"deemer\",\n\"deemie\",\n\"deep\",\n\"deepen\",\n\"deeping\",\n\"deepish\",\n\"deeply\",\n\"deer\",\n\"deerdog\",\n\"deerlet\",\n\"deevey\",\n\"deface\",\n\"defacer\",\n\"defalk\",\n\"defame\",\n\"defamed\",\n\"defamer\",\n\"defassa\",\n\"defat\",\n\"default\",\n\"defease\",\n\"defeat\",\n\"defect\",\n\"defence\",\n\"defend\",\n\"defense\",\n\"defer\",\n\"defial\",\n\"defiant\",\n\"defiber\",\n\"deficit\",\n\"defier\",\n\"defile\",\n\"defiled\",\n\"defiler\",\n\"define\",\n\"defined\",\n\"definer\",\n\"deflate\",\n\"deflect\",\n\"deflesh\",\n\"deflex\",\n\"defog\",\n\"deforce\",\n\"deform\",\n\"defoul\",\n\"defraud\",\n\"defray\",\n\"defrock\",\n\"defrost\",\n\"deft\",\n\"deftly\",\n\"defunct\",\n\"defuse\",\n\"defy\",\n\"deg\",\n\"degas\",\n\"degauss\",\n\"degerm\",\n\"degged\",\n\"degger\",\n\"deglaze\",\n\"degorge\",\n\"degrade\",\n\"degrain\",\n\"degree\",\n\"degu\",\n\"degum\",\n\"degust\",\n\"dehair\",\n\"dehisce\",\n\"dehorn\",\n\"dehors\",\n\"dehort\",\n\"dehull\",\n\"dehusk\",\n\"deice\",\n\"deicer\",\n\"deicide\",\n\"deictic\",\n\"deific\",\n\"deifier\",\n\"deiform\",\n\"deify\",\n\"deign\",\n\"deink\",\n\"deinos\",\n\"deiseal\",\n\"deism\",\n\"deist\",\n\"deistic\",\n\"deity\",\n\"deject\",\n\"dejecta\",\n\"dejeune\",\n\"dekko\",\n\"dekle\",\n\"delaine\",\n\"delapse\",\n\"delate\",\n\"delater\",\n\"delator\",\n\"delawn\",\n\"delay\",\n\"delayer\",\n\"dele\",\n\"delead\",\n\"delenda\",\n\"delete\",\n\"delf\",\n\"delft\",\n\"delible\",\n\"delict\",\n\"delight\",\n\"delime\",\n\"delimit\",\n\"delint\",\n\"deliver\",\n\"dell\",\n\"deloul\",\n\"delouse\",\n\"delta\",\n\"deltaic\",\n\"deltal\",\n\"deltic\",\n\"deltoid\",\n\"delude\",\n\"deluder\",\n\"deluge\",\n\"deluxe\",\n\"delve\",\n\"delver\",\n\"demagog\",\n\"demal\",\n\"demand\",\n\"demarch\",\n\"demark\",\n\"demast\",\n\"deme\",\n\"demean\",\n\"demency\",\n\"dement\",\n\"demerit\",\n\"demesne\",\n\"demi\",\n\"demibob\",\n\"demidog\",\n\"demigod\",\n\"demihag\",\n\"demiman\",\n\"demiowl\",\n\"demiox\",\n\"demiram\",\n\"demirep\",\n\"demise\",\n\"demiss\",\n\"demit\",\n\"demivol\",\n\"demob\",\n\"demoded\",\n\"demoid\",\n\"demon\",\n\"demonic\",\n\"demonry\",\n\"demos\",\n\"demote\",\n\"demotic\",\n\"demount\",\n\"demulce\",\n\"demure\",\n\"demy\",\n\"den\",\n\"denaro\",\n\"denary\",\n\"denat\",\n\"denda\",\n\"dendral\",\n\"dendric\",\n\"dendron\",\n\"dene\",\n\"dengue\",\n\"denial\",\n\"denier\",\n\"denim\",\n\"denizen\",\n\"dennet\",\n\"denote\",\n\"dense\",\n\"densely\",\n\"densen\",\n\"densher\",\n\"densify\",\n\"density\",\n\"dent\",\n\"dental\",\n\"dentale\",\n\"dentary\",\n\"dentata\",\n\"dentate\",\n\"dentel\",\n\"denter\",\n\"dentex\",\n\"dentil\",\n\"dentile\",\n\"dentin\",\n\"dentine\",\n\"dentist\",\n\"dentoid\",\n\"denture\",\n\"denty\",\n\"denude\",\n\"denuder\",\n\"deny\",\n\"deodand\",\n\"deodara\",\n\"deota\",\n\"depa\",\n\"depaint\",\n\"depark\",\n\"depart\",\n\"depas\",\n\"depass\",\n\"depend\",\n\"depeter\",\n\"dephase\",\n\"depict\",\n\"deplane\",\n\"deplete\",\n\"deplore\",\n\"deploy\",\n\"deplume\",\n\"deplump\",\n\"depoh\",\n\"depone\",\n\"deport\",\n\"deposal\",\n\"depose\",\n\"deposer\",\n\"deposit\",\n\"depot\",\n\"deprave\",\n\"depress\",\n\"deprint\",\n\"deprive\",\n\"depside\",\n\"depth\",\n\"depthen\",\n\"depute\",\n\"deputy\",\n\"dequeen\",\n\"derah\",\n\"deraign\",\n\"derail\",\n\"derange\",\n\"derat\",\n\"derate\",\n\"derater\",\n\"deray\",\n\"derby\",\n\"dere\",\n\"dereism\",\n\"deric\",\n\"deride\",\n\"derider\",\n\"derival\",\n\"derive\",\n\"derived\",\n\"deriver\",\n\"derm\",\n\"derma\",\n\"dermad\",\n\"dermal\",\n\"dermic\",\n\"dermis\",\n\"dermoid\",\n\"dermol\",\n\"dern\",\n\"dernier\",\n\"derout\",\n\"derrick\",\n\"derride\",\n\"derries\",\n\"derry\",\n\"dertrum\",\n\"derust\",\n\"dervish\",\n\"desalt\",\n\"desand\",\n\"descale\",\n\"descant\",\n\"descend\",\n\"descent\",\n\"descort\",\n\"descry\",\n\"deseed\",\n\"deseret\",\n\"desert\",\n\"deserve\",\n\"desex\",\n\"desi\",\n\"desight\",\n\"design\",\n\"desire\",\n\"desired\",\n\"desirer\",\n\"desist\",\n\"desize\",\n\"desk\",\n\"deslime\",\n\"desma\",\n\"desman\",\n\"desmic\",\n\"desmid\",\n\"desmine\",\n\"desmoid\",\n\"desmoma\",\n\"desmon\",\n\"despair\",\n\"despect\",\n\"despise\",\n\"despite\",\n\"despoil\",\n\"despond\",\n\"despot\",\n\"dess\",\n\"dessa\",\n\"dessert\",\n\"dessil\",\n\"destain\",\n\"destine\",\n\"destiny\",\n\"destour\",\n\"destroy\",\n\"desuete\",\n\"desugar\",\n\"desyl\",\n\"detach\",\n\"detail\",\n\"detain\",\n\"detar\",\n\"detax\",\n\"detect\",\n\"detent\",\n\"deter\",\n\"deterge\",\n\"detest\",\n\"detin\",\n\"detinet\",\n\"detinue\",\n\"detour\",\n\"detract\",\n\"detrain\",\n\"detrude\",\n\"detune\",\n\"detur\",\n\"deuce\",\n\"deuced\",\n\"deul\",\n\"deuton\",\n\"dev\",\n\"deva\",\n\"devall\",\n\"devalue\",\n\"devance\",\n\"devast\",\n\"devata\",\n\"develin\",\n\"develop\",\n\"devest\",\n\"deviant\",\n\"deviate\",\n\"device\",\n\"devil\",\n\"deviled\",\n\"deviler\",\n\"devilet\",\n\"devilry\",\n\"devily\",\n\"devious\",\n\"devisal\",\n\"devise\",\n\"devisee\",\n\"deviser\",\n\"devisor\",\n\"devoice\",\n\"devoid\",\n\"devoir\",\n\"devolve\",\n\"devote\",\n\"devoted\",\n\"devotee\",\n\"devoter\",\n\"devour\",\n\"devout\",\n\"devow\",\n\"devvel\",\n\"dew\",\n\"dewan\",\n\"dewanee\",\n\"dewater\",\n\"dewax\",\n\"dewbeam\",\n\"dewclaw\",\n\"dewcup\",\n\"dewdamp\",\n\"dewdrop\",\n\"dewer\",\n\"dewfall\",\n\"dewily\",\n\"dewlap\",\n\"dewless\",\n\"dewlike\",\n\"dewool\",\n\"deworm\",\n\"dewret\",\n\"dewtry\",\n\"dewworm\",\n\"dewy\",\n\"dexter\",\n\"dextrad\",\n\"dextral\",\n\"dextran\",\n\"dextrin\",\n\"dextro\",\n\"dey\",\n\"deyship\",\n\"dezinc\",\n\"dha\",\n\"dhabb\",\n\"dhai\",\n\"dhak\",\n\"dhamnoo\",\n\"dhan\",\n\"dhangar\",\n\"dhanuk\",\n\"dhanush\",\n\"dharana\",\n\"dharani\",\n\"dharma\",\n\"dharna\",\n\"dhaura\",\n\"dhauri\",\n\"dhava\",\n\"dhaw\",\n\"dheri\",\n\"dhobi\",\n\"dhole\",\n\"dhoni\",\n\"dhoon\",\n\"dhoti\",\n\"dhoul\",\n\"dhow\",\n\"dhu\",\n\"dhunchi\",\n\"dhurra\",\n\"dhyal\",\n\"dhyana\",\n\"di\",\n\"diabase\",\n\"diacid\",\n\"diacle\",\n\"diacope\",\n\"diact\",\n\"diactin\",\n\"diadem\",\n\"diaderm\",\n\"diaene\",\n\"diagram\",\n\"dial\",\n\"dialect\",\n\"dialer\",\n\"dialin\",\n\"dialing\",\n\"dialist\",\n\"dialkyl\",\n\"diallel\",\n\"diallyl\",\n\"dialyze\",\n\"diamb\",\n\"diambic\",\n\"diamide\",\n\"diamine\",\n\"diamond\",\n\"dian\",\n\"diander\",\n\"dianite\",\n\"diapase\",\n\"diapasm\",\n\"diaper\",\n\"diaplex\",\n\"diapsid\",\n\"diarch\",\n\"diarchy\",\n\"diarial\",\n\"diarian\",\n\"diarist\",\n\"diarize\",\n\"diary\",\n\"diastem\",\n\"diaster\",\n\"diasyrm\",\n\"diatom\",\n\"diaulic\",\n\"diaulos\",\n\"diaxial\",\n\"diaxon\",\n\"diazide\",\n\"diazine\",\n\"diazoic\",\n\"diazole\",\n\"diazoma\",\n\"dib\",\n\"dibase\",\n\"dibasic\",\n\"dibatag\",\n\"dibber\",\n\"dibble\",\n\"dibbler\",\n\"dibbuk\",\n\"dibhole\",\n\"dibrach\",\n\"dibrom\",\n\"dibs\",\n\"dicast\",\n\"dice\",\n\"dicebox\",\n\"dicecup\",\n\"diceman\",\n\"dicer\",\n\"dicetyl\",\n\"dich\",\n\"dichas\",\n\"dichord\",\n\"dicing\",\n\"dick\",\n\"dickens\",\n\"dicker\",\n\"dickey\",\n\"dicky\",\n\"dicolic\",\n\"dicolon\",\n\"dicot\",\n\"dicotyl\",\n\"dicta\",\n\"dictate\",\n\"dictic\",\n\"diction\",\n\"dictum\",\n\"dicycle\",\n\"did\",\n\"didder\",\n\"diddle\",\n\"diddler\",\n\"diddy\",\n\"didelph\",\n\"didie\",\n\"didine\",\n\"didle\",\n\"didna\",\n\"didnt\",\n\"didromy\",\n\"didst\",\n\"didym\",\n\"didymia\",\n\"didymus\",\n\"die\",\n\"dieb\",\n\"dieback\",\n\"diedral\",\n\"diedric\",\n\"diehard\",\n\"dielike\",\n\"diem\",\n\"diene\",\n\"dier\",\n\"diesel\",\n\"diesis\",\n\"diet\",\n\"dietal\",\n\"dietary\",\n\"dieter\",\n\"diethyl\",\n\"dietic\",\n\"dietics\",\n\"dietine\",\n\"dietist\",\n\"diewise\",\n\"diffame\",\n\"differ\",\n\"diffide\",\n\"difform\",\n\"diffuse\",\n\"dig\",\n\"digamma\",\n\"digamy\",\n\"digenic\",\n\"digeny\",\n\"digest\",\n\"digger\",\n\"digging\",\n\"dight\",\n\"dighter\",\n\"digit\",\n\"digital\",\n\"digitus\",\n\"diglot\",\n\"diglyph\",\n\"digmeat\",\n\"dignify\",\n\"dignity\",\n\"digram\",\n\"digraph\",\n\"digress\",\n\"digs\",\n\"dihalo\",\n\"diiamb\",\n\"diiodo\",\n\"dika\",\n\"dikage\",\n\"dike\",\n\"diker\",\n\"diketo\",\n\"dikkop\",\n\"dilate\",\n\"dilated\",\n\"dilater\",\n\"dilator\",\n\"dildo\",\n\"dilemma\",\n\"dilker\",\n\"dill\",\n\"dilli\",\n\"dillier\",\n\"dilling\",\n\"dillue\",\n\"dilluer\",\n\"dilly\",\n\"dilo\",\n\"dilogy\",\n\"diluent\",\n\"dilute\",\n\"diluted\",\n\"dilutee\",\n\"diluter\",\n\"dilutor\",\n\"diluvia\",\n\"dim\",\n\"dimber\",\n\"dimble\",\n\"dime\",\n\"dimer\",\n\"dimeran\",\n\"dimeric\",\n\"dimeter\",\n\"dimiss\",\n\"dimit\",\n\"dimity\",\n\"dimly\",\n\"dimmed\",\n\"dimmer\",\n\"dimmest\",\n\"dimmet\",\n\"dimmish\",\n\"dimness\",\n\"dimoric\",\n\"dimorph\",\n\"dimple\",\n\"dimply\",\n\"dimps\",\n\"dimpsy\",\n\"din\",\n\"dinar\",\n\"dinder\",\n\"dindle\",\n\"dine\",\n\"diner\",\n\"dineric\",\n\"dinero\",\n\"dinette\",\n\"ding\",\n\"dingar\",\n\"dingbat\",\n\"dinge\",\n\"dingee\",\n\"dinghee\",\n\"dinghy\",\n\"dingily\",\n\"dingle\",\n\"dingly\",\n\"dingo\",\n\"dingus\",\n\"dingy\",\n\"dinic\",\n\"dinical\",\n\"dining\",\n\"dinitro\",\n\"dink\",\n\"dinkey\",\n\"dinkum\",\n\"dinky\",\n\"dinmont\",\n\"dinner\",\n\"dinnery\",\n\"dinomic\",\n\"dinsome\",\n\"dint\",\n\"dinus\",\n\"diobely\",\n\"diobol\",\n\"diocese\",\n\"diode\",\n\"diodont\",\n\"dioecy\",\n\"diol\",\n\"dionise\",\n\"dionym\",\n\"diopter\",\n\"dioptra\",\n\"dioptry\",\n\"diorama\",\n\"diorite\",\n\"diose\",\n\"diosmin\",\n\"diota\",\n\"diotic\",\n\"dioxane\",\n\"dioxide\",\n\"dioxime\",\n\"dioxy\",\n\"dip\",\n\"dipetto\",\n\"diphase\",\n\"diphead\",\n\"diplex\",\n\"diploe\",\n\"diploic\",\n\"diploid\",\n\"diplois\",\n\"diploma\",\n\"diplont\",\n\"diplopy\",\n\"dipnoan\",\n\"dipnoid\",\n\"dipode\",\n\"dipodic\",\n\"dipody\",\n\"dipolar\",\n\"dipole\",\n\"diporpa\",\n\"dipped\",\n\"dipper\",\n\"dipping\",\n\"dipsas\",\n\"dipsey\",\n\"dipter\",\n\"diptote\",\n\"diptych\",\n\"dipware\",\n\"dipygus\",\n\"dipylon\",\n\"dipyre\",\n\"dird\",\n\"dirdum\",\n\"dire\",\n\"direct\",\n\"direful\",\n\"direly\",\n\"dirempt\",\n\"dirge\",\n\"dirgler\",\n\"dirhem\",\n\"dirk\",\n\"dirl\",\n\"dirndl\",\n\"dirt\",\n\"dirten\",\n\"dirtily\",\n\"dirty\",\n\"dis\",\n\"disable\",\n\"disagio\",\n\"disally\",\n\"disarm\",\n\"disavow\",\n\"disawa\",\n\"disazo\",\n\"disband\",\n\"disbar\",\n\"disbark\",\n\"disbody\",\n\"disbud\",\n\"disbury\",\n\"disc\",\n\"discage\",\n\"discal\",\n\"discard\",\n\"discase\",\n\"discept\",\n\"discern\",\n\"discerp\",\n\"discoid\",\n\"discord\",\n\"discous\",\n\"discus\",\n\"discuss\",\n\"disdain\",\n\"disdub\",\n\"disease\",\n\"disedge\",\n\"diseme\",\n\"disemic\",\n\"disfame\",\n\"disfen\",\n\"disgig\",\n\"disglut\",\n\"disgood\",\n\"disgown\",\n\"disgulf\",\n\"disgust\",\n\"dish\",\n\"dished\",\n\"dishelm\",\n\"disher\",\n\"dishful\",\n\"dishome\",\n\"dishorn\",\n\"dishpan\",\n\"dishrag\",\n\"disject\",\n\"disjoin\",\n\"disjune\",\n\"disk\",\n\"disleaf\",\n\"dislike\",\n\"dislimn\",\n\"dislink\",\n\"dislip\",\n\"disload\",\n\"dislove\",\n\"dismain\",\n\"dismal\",\n\"disman\",\n\"dismark\",\n\"dismask\",\n\"dismast\",\n\"dismay\",\n\"disme\",\n\"dismiss\",\n\"disna\",\n\"disnest\",\n\"disnew\",\n\"disobey\",\n\"disodic\",\n\"disomic\",\n\"disomus\",\n\"disorb\",\n\"disown\",\n\"dispark\",\n\"dispart\",\n\"dispel\",\n\"dispend\",\n\"display\",\n\"dispone\",\n\"dispope\",\n\"disport\",\n\"dispose\",\n\"dispost\",\n\"dispulp\",\n\"dispute\",\n\"disrank\",\n\"disrate\",\n\"disring\",\n\"disrobe\",\n\"disroof\",\n\"disroot\",\n\"disrump\",\n\"disrupt\",\n\"diss\",\n\"disseat\",\n\"dissect\",\n\"dissent\",\n\"dissert\",\n\"dissoul\",\n\"dissuit\",\n\"distad\",\n\"distaff\",\n\"distain\",\n\"distal\",\n\"distale\",\n\"distant\",\n\"distend\",\n\"distent\",\n\"distich\",\n\"distill\",\n\"distome\",\n\"distort\",\n\"distune\",\n\"disturb\",\n\"disturn\",\n\"disuse\",\n\"diswood\",\n\"disyoke\",\n\"dit\",\n\"dita\",\n\"dital\",\n\"ditch\",\n\"ditcher\",\n\"dite\",\n\"diter\",\n\"dither\",\n\"dithery\",\n\"dithion\",\n\"ditolyl\",\n\"ditone\",\n\"dittamy\",\n\"dittany\",\n\"dittay\",\n\"dittied\",\n\"ditto\",\n\"ditty\",\n\"diurnal\",\n\"diurne\",\n\"div\",\n\"diva\",\n\"divan\",\n\"divata\",\n\"dive\",\n\"divel\",\n\"diver\",\n\"diverge\",\n\"divers\",\n\"diverse\",\n\"divert\",\n\"divest\",\n\"divide\",\n\"divided\",\n\"divider\",\n\"divine\",\n\"diviner\",\n\"diving\",\n\"divinyl\",\n\"divisor\",\n\"divorce\",\n\"divot\",\n\"divoto\",\n\"divulge\",\n\"divulse\",\n\"divus\",\n\"divvy\",\n\"diwata\",\n\"dixie\",\n\"dixit\",\n\"dixy\",\n\"dizain\",\n\"dizen\",\n\"dizoic\",\n\"dizzard\",\n\"dizzily\",\n\"dizzy\",\n\"djave\",\n\"djehad\",\n\"djerib\",\n\"djersa\",\n\"do\",\n\"doab\",\n\"doable\",\n\"doarium\",\n\"doat\",\n\"doated\",\n\"doater\",\n\"doating\",\n\"doatish\",\n\"dob\",\n\"dobbed\",\n\"dobber\",\n\"dobbin\",\n\"dobbing\",\n\"dobby\",\n\"dobe\",\n\"dobla\",\n\"doblon\",\n\"dobra\",\n\"dobrao\",\n\"dobson\",\n\"doby\",\n\"doc\",\n\"docent\",\n\"docible\",\n\"docile\",\n\"docity\",\n\"dock\",\n\"dockage\",\n\"docken\",\n\"docker\",\n\"docket\",\n\"dockize\",\n\"dockman\",\n\"docmac\",\n\"doctor\",\n\"doctrix\",\n\"dod\",\n\"dodd\",\n\"doddart\",\n\"dodded\",\n\"dodder\",\n\"doddery\",\n\"doddie\",\n\"dodding\",\n\"doddle\",\n\"doddy\",\n\"dodecyl\",\n\"dodge\",\n\"dodger\",\n\"dodgery\",\n\"dodgily\",\n\"dodgy\",\n\"dodkin\",\n\"dodlet\",\n\"dodman\",\n\"dodo\",\n\"dodoism\",\n\"dodrans\",\n\"doe\",\n\"doebird\",\n\"doeglic\",\n\"doer\",\n\"does\",\n\"doeskin\",\n\"doesnt\",\n\"doest\",\n\"doff\",\n\"doffer\",\n\"dog\",\n\"dogal\",\n\"dogate\",\n\"dogbane\",\n\"dogbite\",\n\"dogblow\",\n\"dogboat\",\n\"dogbolt\",\n\"dogbush\",\n\"dogcart\",\n\"dogdom\",\n\"doge\",\n\"dogedom\",\n\"dogface\",\n\"dogfall\",\n\"dogfish\",\n\"dogfoot\",\n\"dogged\",\n\"dogger\",\n\"doggery\",\n\"doggess\",\n\"doggish\",\n\"doggo\",\n\"doggone\",\n\"doggrel\",\n\"doggy\",\n\"doghead\",\n\"doghole\",\n\"doghood\",\n\"dogie\",\n\"dogless\",\n\"doglike\",\n\"dogly\",\n\"dogma\",\n\"dogman\",\n\"dogmata\",\n\"dogs\",\n\"dogship\",\n\"dogskin\",\n\"dogtail\",\n\"dogtie\",\n\"dogtrot\",\n\"dogvane\",\n\"dogwood\",\n\"dogy\",\n\"doigt\",\n\"doiled\",\n\"doily\",\n\"doina\",\n\"doing\",\n\"doings\",\n\"doit\",\n\"doited\",\n\"doitkin\",\n\"doke\",\n\"dokhma\",\n\"dola\",\n\"dolabra\",\n\"dolcan\",\n\"dolcian\",\n\"dolcino\",\n\"doldrum\",\n\"dole\",\n\"doleful\",\n\"dolent\",\n\"doless\",\n\"doli\",\n\"dolia\",\n\"dolina\",\n\"doline\",\n\"dolium\",\n\"doll\",\n\"dollar\",\n\"dolldom\",\n\"dollier\",\n\"dollish\",\n\"dollop\",\n\"dolly\",\n\"dolman\",\n\"dolmen\",\n\"dolor\",\n\"dolose\",\n\"dolous\",\n\"dolphin\",\n\"dolt\",\n\"doltish\",\n\"dom\",\n\"domain\",\n\"domal\",\n\"domba\",\n\"dome\",\n\"doment\",\n\"domer\",\n\"domett\",\n\"domic\",\n\"domical\",\n\"domine\",\n\"dominie\",\n\"domino\",\n\"dominus\",\n\"domite\",\n\"domitic\",\n\"domn\",\n\"domnei\",\n\"domoid\",\n\"dompt\",\n\"domy\",\n\"don\",\n\"donable\",\n\"donary\",\n\"donate\",\n\"donated\",\n\"donatee\",\n\"donator\",\n\"donax\",\n\"done\",\n\"donee\",\n\"doney\",\n\"dong\",\n\"donga\",\n\"dongon\",\n\"donjon\",\n\"donkey\",\n\"donna\",\n\"donnert\",\n\"donnish\",\n\"donnism\",\n\"donnot\",\n\"donor\",\n\"donship\",\n\"donsie\",\n\"dont\",\n\"donum\",\n\"doob\",\n\"doocot\",\n\"doodab\",\n\"doodad\",\n\"doodle\",\n\"doodler\",\n\"dooja\",\n\"dook\",\n\"dooket\",\n\"dookit\",\n\"dool\",\n\"doolee\",\n\"dooley\",\n\"dooli\",\n\"doolie\",\n\"dooly\",\n\"doom\",\n\"doomage\",\n\"doomer\",\n\"doomful\",\n\"dooms\",\n\"doon\",\n\"door\",\n\"doorba\",\n\"doorboy\",\n\"doored\",\n\"doorman\",\n\"doorway\",\n\"dop\",\n\"dopa\",\n\"dopatta\",\n\"dope\",\n\"doper\",\n\"dopey\",\n\"dopper\",\n\"doppia\",\n\"dor\",\n\"dorab\",\n\"dorad\",\n\"dorado\",\n\"doree\",\n\"dorhawk\",\n\"doria\",\n\"dorje\",\n\"dorlach\",\n\"dorlot\",\n\"dorm\",\n\"dormant\",\n\"dormer\",\n\"dormie\",\n\"dormy\",\n\"dorn\",\n\"dorneck\",\n\"dornic\",\n\"dornick\",\n\"dornock\",\n\"dorp\",\n\"dorsad\",\n\"dorsal\",\n\"dorsale\",\n\"dorsel\",\n\"dorser\",\n\"dorsum\",\n\"dorter\",\n\"dorts\",\n\"dorty\",\n\"doruck\",\n\"dory\",\n\"dos\",\n\"dosa\",\n\"dosadh\",\n\"dosage\",\n\"dose\",\n\"doser\",\n\"dosis\",\n\"doss\",\n\"dossal\",\n\"dossel\",\n\"dosser\",\n\"dossier\",\n\"dossil\",\n\"dossman\",\n\"dot\",\n\"dotage\",\n\"dotal\",\n\"dotard\",\n\"dotardy\",\n\"dotate\",\n\"dotchin\",\n\"dote\",\n\"doted\",\n\"doter\",\n\"doting\",\n\"dotish\",\n\"dotkin\",\n\"dotless\",\n\"dotlike\",\n\"dotted\",\n\"dotter\",\n\"dottily\",\n\"dotting\",\n\"dottle\",\n\"dottler\",\n\"dotty\",\n\"doty\",\n\"douar\",\n\"double\",\n\"doubled\",\n\"doubler\",\n\"doublet\",\n\"doubly\",\n\"doubt\",\n\"doubter\",\n\"douc\",\n\"douce\",\n\"doucely\",\n\"doucet\",\n\"douche\",\n\"doucin\",\n\"doucine\",\n\"doudle\",\n\"dough\",\n\"dought\",\n\"doughty\",\n\"doughy\",\n\"doum\",\n\"doup\",\n\"douping\",\n\"dour\",\n\"dourine\",\n\"dourly\",\n\"douse\",\n\"douser\",\n\"dout\",\n\"douter\",\n\"doutous\",\n\"dove\",\n\"dovecot\",\n\"dovekey\",\n\"dovekie\",\n\"dovelet\",\n\"dover\",\n\"dovish\",\n\"dow\",\n\"dowable\",\n\"dowager\",\n\"dowcet\",\n\"dowd\",\n\"dowdily\",\n\"dowdy\",\n\"dowed\",\n\"dowel\",\n\"dower\",\n\"doweral\",\n\"dowery\",\n\"dowf\",\n\"dowie\",\n\"dowily\",\n\"dowitch\",\n\"dowl\",\n\"dowlas\",\n\"dowless\",\n\"down\",\n\"downby\",\n\"downcry\",\n\"downcut\",\n\"downer\",\n\"downily\",\n\"downlie\",\n\"downset\",\n\"downway\",\n\"downy\",\n\"dowp\",\n\"dowry\",\n\"dowse\",\n\"dowser\",\n\"dowset\",\n\"doxa\",\n\"doxy\",\n\"doze\",\n\"dozed\",\n\"dozen\",\n\"dozener\",\n\"dozenth\",\n\"dozer\",\n\"dozily\",\n\"dozy\",\n\"dozzled\",\n\"drab\",\n\"drabbet\",\n\"drabble\",\n\"drabby\",\n\"drably\",\n\"drachm\",\n\"drachma\",\n\"dracma\",\n\"draff\",\n\"draffy\",\n\"draft\",\n\"draftee\",\n\"drafter\",\n\"drafty\",\n\"drag\",\n\"dragade\",\n\"dragbar\",\n\"dragged\",\n\"dragger\",\n\"draggle\",\n\"draggly\",\n\"draggy\",\n\"dragman\",\n\"dragnet\",\n\"drago\",\n\"dragon\",\n\"dragoon\",\n\"dragsaw\",\n\"drail\",\n\"drain\",\n\"draine\",\n\"drained\",\n\"drainer\",\n\"drake\",\n\"dram\",\n\"drama\",\n\"dramm\",\n\"dramme\",\n\"drammed\",\n\"drammer\",\n\"drang\",\n\"drank\",\n\"drant\",\n\"drape\",\n\"draper\",\n\"drapery\",\n\"drassid\",\n\"drastic\",\n\"drat\",\n\"drate\",\n\"dratted\",\n\"draught\",\n\"dravya\",\n\"draw\",\n\"drawarm\",\n\"drawbar\",\n\"drawboy\",\n\"drawcut\",\n\"drawee\",\n\"drawer\",\n\"drawers\",\n\"drawing\",\n\"drawk\",\n\"drawl\",\n\"drawler\",\n\"drawly\",\n\"drawn\",\n\"drawnet\",\n\"drawoff\",\n\"drawout\",\n\"drawrod\",\n\"dray\",\n\"drayage\",\n\"drayman\",\n\"drazel\",\n\"dread\",\n\"dreader\",\n\"dreadly\",\n\"dream\",\n\"dreamer\",\n\"dreamsy\",\n\"dreamt\",\n\"dreamy\",\n\"drear\",\n\"drearly\",\n\"dreary\",\n\"dredge\",\n\"dredger\",\n\"dree\",\n\"dreep\",\n\"dreepy\",\n\"dreg\",\n\"dreggy\",\n\"dregs\",\n\"drench\",\n\"dreng\",\n\"dress\",\n\"dressed\",\n\"dresser\",\n\"dressy\",\n\"drest\",\n\"drew\",\n\"drewite\",\n\"drias\",\n\"drib\",\n\"dribble\",\n\"driblet\",\n\"driddle\",\n\"dried\",\n\"drier\",\n\"driest\",\n\"drift\",\n\"drifter\",\n\"drifty\",\n\"drill\",\n\"driller\",\n\"drillet\",\n\"dringle\",\n\"drink\",\n\"drinker\",\n\"drinn\",\n\"drip\",\n\"dripper\",\n\"dripple\",\n\"drippy\",\n\"drisk\",\n\"drivage\",\n\"drive\",\n\"drivel\",\n\"driven\",\n\"driver\",\n\"driving\",\n\"drizzle\",\n\"drizzly\",\n\"droddum\",\n\"drogh\",\n\"drogher\",\n\"drogue\",\n\"droit\",\n\"droll\",\n\"drolly\",\n\"drome\",\n\"dromic\",\n\"dromond\",\n\"dromos\",\n\"drona\",\n\"dronage\",\n\"drone\",\n\"droner\",\n\"drongo\",\n\"dronish\",\n\"drony\",\n\"drool\",\n\"droop\",\n\"drooper\",\n\"droopt\",\n\"droopy\",\n\"drop\",\n\"droplet\",\n\"dropman\",\n\"dropout\",\n\"dropper\",\n\"droppy\",\n\"dropsy\",\n\"dropt\",\n\"droshky\",\n\"drosky\",\n\"dross\",\n\"drossel\",\n\"drosser\",\n\"drossy\",\n\"drostdy\",\n\"droud\",\n\"drought\",\n\"drouk\",\n\"drove\",\n\"drover\",\n\"drovy\",\n\"drow\",\n\"drown\",\n\"drowner\",\n\"drowse\",\n\"drowsy\",\n\"drub\",\n\"drubber\",\n\"drubbly\",\n\"drucken\",\n\"drudge\",\n\"drudger\",\n\"druery\",\n\"drug\",\n\"drugger\",\n\"drugget\",\n\"druggy\",\n\"drugman\",\n\"druid\",\n\"druidic\",\n\"druidry\",\n\"druith\",\n\"drum\",\n\"drumble\",\n\"drumlin\",\n\"drumly\",\n\"drummer\",\n\"drummy\",\n\"drung\",\n\"drungar\",\n\"drunk\",\n\"drunken\",\n\"drupal\",\n\"drupe\",\n\"drupel\",\n\"druse\",\n\"drusy\",\n\"druxy\",\n\"dry\",\n\"dryad\",\n\"dryadic\",\n\"dryas\",\n\"drycoal\",\n\"dryfoot\",\n\"drying\",\n\"dryish\",\n\"dryly\",\n\"dryness\",\n\"dryster\",\n\"dryth\",\n\"duad\",\n\"duadic\",\n\"dual\",\n\"duali\",\n\"dualin\",\n\"dualism\",\n\"dualist\",\n\"duality\",\n\"dualize\",\n\"dually\",\n\"duarch\",\n\"duarchy\",\n\"dub\",\n\"dubash\",\n\"dubb\",\n\"dubba\",\n\"dubbah\",\n\"dubber\",\n\"dubbing\",\n\"dubby\",\n\"dubiety\",\n\"dubious\",\n\"dubs\",\n\"ducal\",\n\"ducally\",\n\"ducape\",\n\"ducat\",\n\"ducato\",\n\"ducdame\",\n\"duces\",\n\"duchess\",\n\"duchy\",\n\"duck\",\n\"ducker\",\n\"duckery\",\n\"duckie\",\n\"ducking\",\n\"duckpin\",\n\"duct\",\n\"ducted\",\n\"ductile\",\n\"duction\",\n\"ductor\",\n\"ductule\",\n\"dud\",\n\"dudaim\",\n\"dudder\",\n\"duddery\",\n\"duddies\",\n\"dude\",\n\"dudeen\",\n\"dudgeon\",\n\"dudine\",\n\"dudish\",\n\"dudism\",\n\"dudler\",\n\"dudley\",\n\"dudman\",\n\"due\",\n\"duel\",\n\"dueler\",\n\"dueling\",\n\"duelist\",\n\"duello\",\n\"dueness\",\n\"duenna\",\n\"duer\",\n\"duet\",\n\"duff\",\n\"duffel\",\n\"duffer\",\n\"duffing\",\n\"dufoil\",\n\"dufter\",\n\"duftery\",\n\"dug\",\n\"dugal\",\n\"dugdug\",\n\"duggler\",\n\"dugong\",\n\"dugout\",\n\"dugway\",\n\"duhat\",\n\"duiker\",\n\"duim\",\n\"duit\",\n\"dujan\",\n\"duke\",\n\"dukedom\",\n\"dukely\",\n\"dukery\",\n\"dukhn\",\n\"dukker\",\n\"dulbert\",\n\"dulcet\",\n\"dulcian\",\n\"dulcify\",\n\"dulcose\",\n\"duledge\",\n\"duler\",\n\"dulia\",\n\"dull\",\n\"dullard\",\n\"duller\",\n\"dullery\",\n\"dullify\",\n\"dullish\",\n\"dullity\",\n\"dully\",\n\"dulosis\",\n\"dulotic\",\n\"dulse\",\n\"dult\",\n\"dultie\",\n\"duly\",\n\"dum\",\n\"duma\",\n\"dumaist\",\n\"dumb\",\n\"dumba\",\n\"dumbcow\",\n\"dumbly\",\n\"dumdum\",\n\"dummel\",\n\"dummy\",\n\"dumose\",\n\"dump\",\n\"dumpage\",\n\"dumper\",\n\"dumpily\",\n\"dumping\",\n\"dumpish\",\n\"dumple\",\n\"dumpoke\",\n\"dumpy\",\n\"dumsola\",\n\"dun\",\n\"dunair\",\n\"dunal\",\n\"dunbird\",\n\"dunce\",\n\"duncery\",\n\"dunch\",\n\"duncify\",\n\"duncish\",\n\"dunder\",\n\"dune\",\n\"dunfish\",\n\"dung\",\n\"dungeon\",\n\"dunger\",\n\"dungol\",\n\"dungon\",\n\"dungy\",\n\"dunite\",\n\"dunk\",\n\"dunker\",\n\"dunlin\",\n\"dunnage\",\n\"dunne\",\n\"dunner\",\n\"dunness\",\n\"dunnish\",\n\"dunnite\",\n\"dunnock\",\n\"dunny\",\n\"dunst\",\n\"dunt\",\n\"duntle\",\n\"duny\",\n\"duo\",\n\"duodena\",\n\"duodene\",\n\"duole\",\n\"duopod\",\n\"duopoly\",\n\"duotone\",\n\"duotype\",\n\"dup\",\n\"dupable\",\n\"dupe\",\n\"dupedom\",\n\"duper\",\n\"dupery\",\n\"dupion\",\n\"dupla\",\n\"duple\",\n\"duplet\",\n\"duplex\",\n\"duplify\",\n\"duplone\",\n\"duppy\",\n\"dura\",\n\"durable\",\n\"durably\",\n\"durain\",\n\"dural\",\n\"duramen\",\n\"durance\",\n\"durant\",\n\"durax\",\n\"durbar\",\n\"dure\",\n\"durene\",\n\"durenol\",\n\"duress\",\n\"durgan\",\n\"durian\",\n\"during\",\n\"durity\",\n\"durmast\",\n\"durn\",\n\"duro\",\n\"durra\",\n\"durrie\",\n\"durrin\",\n\"durry\",\n\"durst\",\n\"durwaun\",\n\"duryl\",\n\"dusack\",\n\"duscle\",\n\"dush\",\n\"dusio\",\n\"dusk\",\n\"dusken\",\n\"duskily\",\n\"duskish\",\n\"duskly\",\n\"dusky\",\n\"dust\",\n\"dustbin\",\n\"dustbox\",\n\"dustee\",\n\"duster\",\n\"dustily\",\n\"dusting\",\n\"dustman\",\n\"dustpan\",\n\"dustuck\",\n\"dusty\",\n\"dutch\",\n\"duteous\",\n\"dutied\",\n\"dutiful\",\n\"dutra\",\n\"duty\",\n\"duumvir\",\n\"duvet\",\n\"duvetyn\",\n\"dux\",\n\"duyker\",\n\"dvaita\",\n\"dvandva\",\n\"dwale\",\n\"dwalm\",\n\"dwang\",\n\"dwarf\",\n\"dwarfy\",\n\"dwell\",\n\"dwelled\",\n\"dweller\",\n\"dwelt\",\n\"dwindle\",\n\"dwine\",\n\"dyad\",\n\"dyadic\",\n\"dyarchy\",\n\"dyaster\",\n\"dyce\",\n\"dye\",\n\"dyeable\",\n\"dyeing\",\n\"dyer\",\n\"dyester\",\n\"dyeware\",\n\"dyeweed\",\n\"dyewood\",\n\"dying\",\n\"dyingly\",\n\"dyke\",\n\"dyker\",\n\"dynamic\",\n\"dynamis\",\n\"dynamo\",\n\"dynast\",\n\"dynasty\",\n\"dyne\",\n\"dyphone\",\n\"dyslogy\",\n\"dysnomy\",\n\"dyspnea\",\n\"dystome\",\n\"dysuria\",\n\"dysuric\",\n\"dzeren\",\n\"e\",\n\"ea\",\n\"each\",\n\"eager\",\n\"eagerly\",\n\"eagle\",\n\"eagless\",\n\"eaglet\",\n\"eagre\",\n\"ean\",\n\"ear\",\n\"earache\",\n\"earbob\",\n\"earcap\",\n\"eardrop\",\n\"eardrum\",\n\"eared\",\n\"earful\",\n\"earhole\",\n\"earing\",\n\"earl\",\n\"earlap\",\n\"earldom\",\n\"earless\",\n\"earlet\",\n\"earlike\",\n\"earlish\",\n\"earlock\",\n\"early\",\n\"earmark\",\n\"earn\",\n\"earner\",\n\"earnest\",\n\"earnful\",\n\"earning\",\n\"earpick\",\n\"earplug\",\n\"earring\",\n\"earshot\",\n\"earsore\",\n\"eartab\",\n\"earth\",\n\"earthed\",\n\"earthen\",\n\"earthly\",\n\"earthy\",\n\"earwax\",\n\"earwig\",\n\"earworm\",\n\"earwort\",\n\"ease\",\n\"easeful\",\n\"easel\",\n\"easer\",\n\"easier\",\n\"easiest\",\n\"easily\",\n\"easing\",\n\"east\",\n\"easter\",\n\"eastern\",\n\"easting\",\n\"easy\",\n\"eat\",\n\"eatable\",\n\"eatage\",\n\"eaten\",\n\"eater\",\n\"eatery\",\n\"eating\",\n\"eats\",\n\"eave\",\n\"eaved\",\n\"eaver\",\n\"eaves\",\n\"ebb\",\n\"ebbman\",\n\"eboe\",\n\"ebon\",\n\"ebonist\",\n\"ebonite\",\n\"ebonize\",\n\"ebony\",\n\"ebriate\",\n\"ebriety\",\n\"ebrious\",\n\"ebulus\",\n\"eburine\",\n\"ecad\",\n\"ecanda\",\n\"ecarte\",\n\"ecbatic\",\n\"ecbole\",\n\"ecbolic\",\n\"ecdemic\",\n\"ecderon\",\n\"ecdysis\",\n\"ecesic\",\n\"ecesis\",\n\"eche\",\n\"echea\",\n\"echelon\",\n\"echidna\",\n\"echinal\",\n\"echinid\",\n\"echinus\",\n\"echo\",\n\"echoer\",\n\"echoic\",\n\"echoism\",\n\"echoist\",\n\"echoize\",\n\"ecize\",\n\"ecklein\",\n\"eclair\",\n\"eclat\",\n\"eclegm\",\n\"eclegma\",\n\"eclipse\",\n\"eclogue\",\n\"ecoid\",\n\"ecole\",\n\"ecology\",\n\"economy\",\n\"ecotone\",\n\"ecotype\",\n\"ecphore\",\n\"ecru\",\n\"ecstasy\",\n\"ectad\",\n\"ectal\",\n\"ectally\",\n\"ectasia\",\n\"ectasis\",\n\"ectatic\",\n\"ectene\",\n\"ecthyma\",\n\"ectiris\",\n\"ectopia\",\n\"ectopic\",\n\"ectopy\",\n\"ectozoa\",\n\"ectypal\",\n\"ectype\",\n\"eczema\",\n\"edacity\",\n\"edaphic\",\n\"edaphon\",\n\"edder\",\n\"eddish\",\n\"eddo\",\n\"eddy\",\n\"edea\",\n\"edeagra\",\n\"edeitis\",\n\"edema\",\n\"edemic\",\n\"edenite\",\n\"edental\",\n\"edestan\",\n\"edestin\",\n\"edge\",\n\"edged\",\n\"edgeman\",\n\"edger\",\n\"edging\",\n\"edgrew\",\n\"edgy\",\n\"edh\",\n\"edible\",\n\"edict\",\n\"edictal\",\n\"edicule\",\n\"edifice\",\n\"edifier\",\n\"edify\",\n\"edit\",\n\"edital\",\n\"edition\",\n\"editor\",\n\"educand\",\n\"educate\",\n\"educe\",\n\"educive\",\n\"educt\",\n\"eductor\",\n\"eegrass\",\n\"eel\",\n\"eelboat\",\n\"eelbob\",\n\"eelcake\",\n\"eeler\",\n\"eelery\",\n\"eelfare\",\n\"eelfish\",\n\"eellike\",\n\"eelpot\",\n\"eelpout\",\n\"eelshop\",\n\"eelskin\",\n\"eelware\",\n\"eelworm\",\n\"eely\",\n\"eer\",\n\"eerie\",\n\"eerily\",\n\"effable\",\n\"efface\",\n\"effacer\",\n\"effect\",\n\"effects\",\n\"effendi\",\n\"effete\",\n\"effigy\",\n\"efflate\",\n\"efflux\",\n\"efform\",\n\"effort\",\n\"effulge\",\n\"effund\",\n\"effuse\",\n\"eft\",\n\"eftest\",\n\"egad\",\n\"egality\",\n\"egence\",\n\"egeran\",\n\"egest\",\n\"egesta\",\n\"egg\",\n\"eggcup\",\n\"egger\",\n\"eggfish\",\n\"egghead\",\n\"egghot\",\n\"egging\",\n\"eggler\",\n\"eggless\",\n\"egglike\",\n\"eggnog\",\n\"eggy\",\n\"egilops\",\n\"egipto\",\n\"egma\",\n\"ego\",\n\"egohood\",\n\"egoism\",\n\"egoist\",\n\"egoity\",\n\"egoize\",\n\"egoizer\",\n\"egol\",\n\"egomism\",\n\"egotism\",\n\"egotist\",\n\"egotize\",\n\"egress\",\n\"egret\",\n\"eh\",\n\"eheu\",\n\"ehlite\",\n\"ehuawa\",\n\"eident\",\n\"eider\",\n\"eidetic\",\n\"eidolic\",\n\"eidolon\",\n\"eight\",\n\"eighth\",\n\"eighty\",\n\"eigne\",\n\"eimer\",\n\"einkorn\",\n\"eisodic\",\n\"either\",\n\"eject\",\n\"ejecta\",\n\"ejector\",\n\"ejoo\",\n\"ekaha\",\n\"eke\",\n\"eker\",\n\"ekerite\",\n\"eking\",\n\"ekka\",\n\"ekphore\",\n\"ektene\",\n\"ektenes\",\n\"el\",\n\"elaidic\",\n\"elaidin\",\n\"elain\",\n\"elaine\",\n\"elance\",\n\"eland\",\n\"elanet\",\n\"elapid\",\n\"elapine\",\n\"elapoid\",\n\"elapse\",\n\"elastic\",\n\"elastin\",\n\"elatcha\",\n\"elate\",\n\"elated\",\n\"elater\",\n\"elation\",\n\"elative\",\n\"elator\",\n\"elb\",\n\"elbow\",\n\"elbowed\",\n\"elbower\",\n\"elbowy\",\n\"elcaja\",\n\"elchee\",\n\"eld\",\n\"elder\",\n\"elderly\",\n\"eldest\",\n\"eldin\",\n\"elding\",\n\"eldress\",\n\"elect\",\n\"electee\",\n\"electly\",\n\"elector\",\n\"electro\",\n\"elegant\",\n\"elegiac\",\n\"elegist\",\n\"elegit\",\n\"elegize\",\n\"elegy\",\n\"eleidin\",\n\"element\",\n\"elemi\",\n\"elemin\",\n\"elench\",\n\"elenchi\",\n\"elenge\",\n\"elevate\",\n\"eleven\",\n\"elevon\",\n\"elf\",\n\"elfhood\",\n\"elfic\",\n\"elfin\",\n\"elfish\",\n\"elfkin\",\n\"elfland\",\n\"elflike\",\n\"elflock\",\n\"elfship\",\n\"elfwife\",\n\"elfwort\",\n\"elicit\",\n\"elide\",\n\"elision\",\n\"elisor\",\n\"elite\",\n\"elixir\",\n\"elk\",\n\"elkhorn\",\n\"elkslip\",\n\"elkwood\",\n\"ell\",\n\"ellagic\",\n\"elle\",\n\"elleck\",\n\"ellfish\",\n\"ellipse\",\n\"ellops\",\n\"ellwand\",\n\"elm\",\n\"elmy\",\n\"elocute\",\n\"elod\",\n\"eloge\",\n\"elogium\",\n\"eloign\",\n\"elope\",\n\"eloper\",\n\"elops\",\n\"els\",\n\"else\",\n\"elsehow\",\n\"elsin\",\n\"elt\",\n\"eluate\",\n\"elude\",\n\"eluder\",\n\"elusion\",\n\"elusive\",\n\"elusory\",\n\"elute\",\n\"elution\",\n\"elutor\",\n\"eluvial\",\n\"eluvium\",\n\"elvan\",\n\"elver\",\n\"elves\",\n\"elvet\",\n\"elvish\",\n\"elysia\",\n\"elytral\",\n\"elytrin\",\n\"elytron\",\n\"elytrum\",\n\"em\",\n\"emanant\",\n\"emanate\",\n\"emanium\",\n\"emarcid\",\n\"emball\",\n\"embalm\",\n\"embank\",\n\"embar\",\n\"embargo\",\n\"embark\",\n\"embassy\",\n\"embathe\",\n\"embay\",\n\"embed\",\n\"embelic\",\n\"ember\",\n\"embind\",\n\"embira\",\n\"emblaze\",\n\"emblem\",\n\"emblema\",\n\"emblic\",\n\"embody\",\n\"embog\",\n\"embole\",\n\"embolic\",\n\"embolo\",\n\"embolum\",\n\"embolus\",\n\"emboly\",\n\"embosom\",\n\"emboss\",\n\"embound\",\n\"embow\",\n\"embowed\",\n\"embowel\",\n\"embower\",\n\"embox\",\n\"embrace\",\n\"embrail\",\n\"embroil\",\n\"embrown\",\n\"embryo\",\n\"embryon\",\n\"embuia\",\n\"embus\",\n\"embusk\",\n\"emcee\",\n\"eme\",\n\"emeer\",\n\"emend\",\n\"emender\",\n\"emerald\",\n\"emerge\",\n\"emerize\",\n\"emerse\",\n\"emersed\",\n\"emery\",\n\"emesis\",\n\"emetic\",\n\"emetine\",\n\"emgalla\",\n\"emigree\",\n\"eminent\",\n\"emir\",\n\"emirate\",\n\"emit\",\n\"emitter\",\n\"emma\",\n\"emmenic\",\n\"emmer\",\n\"emmet\",\n\"emodin\",\n\"emoloa\",\n\"emote\",\n\"emotion\",\n\"emotive\",\n\"empall\",\n\"empanel\",\n\"empaper\",\n\"empark\",\n\"empasm\",\n\"empathy\",\n\"emperor\",\n\"empery\",\n\"empire\",\n\"empiric\",\n\"emplace\",\n\"emplane\",\n\"employ\",\n\"emplume\",\n\"emporia\",\n\"empower\",\n\"empress\",\n\"emprise\",\n\"empt\",\n\"emptier\",\n\"emptily\",\n\"emptins\",\n\"emption\",\n\"emptor\",\n\"empty\",\n\"empyema\",\n\"emu\",\n\"emulant\",\n\"emulate\",\n\"emulous\",\n\"emulsin\",\n\"emulsor\",\n\"emyd\",\n\"emydian\",\n\"en\",\n\"enable\",\n\"enabler\",\n\"enact\",\n\"enactor\",\n\"enaena\",\n\"enage\",\n\"enalid\",\n\"enam\",\n\"enamber\",\n\"enamdar\",\n\"enamel\",\n\"enamor\",\n\"enapt\",\n\"enarbor\",\n\"enarch\",\n\"enarm\",\n\"enarme\",\n\"enate\",\n\"enatic\",\n\"enation\",\n\"enbrave\",\n\"encage\",\n\"encake\",\n\"encamp\",\n\"encase\",\n\"encash\",\n\"encauma\",\n\"encave\",\n\"encell\",\n\"enchain\",\n\"enchair\",\n\"enchant\",\n\"enchase\",\n\"enchest\",\n\"encina\",\n\"encinal\",\n\"encist\",\n\"enclasp\",\n\"enclave\",\n\"encloak\",\n\"enclose\",\n\"encloud\",\n\"encoach\",\n\"encode\",\n\"encoil\",\n\"encolor\",\n\"encomia\",\n\"encomic\",\n\"encoop\",\n\"encore\",\n\"encowl\",\n\"encraal\",\n\"encraty\",\n\"encreel\",\n\"encrisp\",\n\"encrown\",\n\"encrust\",\n\"encrypt\",\n\"encup\",\n\"encurl\",\n\"encyst\",\n\"end\",\n\"endable\",\n\"endarch\",\n\"endaze\",\n\"endear\",\n\"ended\",\n\"endemic\",\n\"ender\",\n\"endere\",\n\"enderon\",\n\"endevil\",\n\"endew\",\n\"endgate\",\n\"ending\",\n\"endite\",\n\"endive\",\n\"endless\",\n\"endlong\",\n\"endmost\",\n\"endogen\",\n\"endome\",\n\"endopod\",\n\"endoral\",\n\"endore\",\n\"endorse\",\n\"endoss\",\n\"endotys\",\n\"endow\",\n\"endower\",\n\"endozoa\",\n\"endue\",\n\"endura\",\n\"endure\",\n\"endurer\",\n\"endways\",\n\"endwise\",\n\"endyma\",\n\"endymal\",\n\"endysis\",\n\"enema\",\n\"enemy\",\n\"energic\",\n\"energid\",\n\"energy\",\n\"eneuch\",\n\"eneugh\",\n\"enface\",\n\"enfelon\",\n\"enfeoff\",\n\"enfever\",\n\"enfile\",\n\"enfiled\",\n\"enflesh\",\n\"enfoil\",\n\"enfold\",\n\"enforce\",\n\"enfork\",\n\"enfoul\",\n\"enframe\",\n\"enfree\",\n\"engage\",\n\"engaged\",\n\"engager\",\n\"engaol\",\n\"engarb\",\n\"engaud\",\n\"engaze\",\n\"engem\",\n\"engild\",\n\"engine\",\n\"engird\",\n\"engirt\",\n\"englad\",\n\"englobe\",\n\"engloom\",\n\"englory\",\n\"englut\",\n\"englyn\",\n\"engobe\",\n\"engold\",\n\"engore\",\n\"engorge\",\n\"engrace\",\n\"engraff\",\n\"engraft\",\n\"engrail\",\n\"engrain\",\n\"engram\",\n\"engrasp\",\n\"engrave\",\n\"engreen\",\n\"engross\",\n\"enguard\",\n\"engulf\",\n\"enhalo\",\n\"enhance\",\n\"enhat\",\n\"enhaunt\",\n\"enheart\",\n\"enhedge\",\n\"enhelm\",\n\"enherit\",\n\"enhusk\",\n\"eniac\",\n\"enigma\",\n\"enisle\",\n\"enjail\",\n\"enjamb\",\n\"enjelly\",\n\"enjewel\",\n\"enjoin\",\n\"enjoy\",\n\"enjoyer\",\n\"enkraal\",\n\"enlace\",\n\"enlard\",\n\"enlarge\",\n\"enleaf\",\n\"enlief\",\n\"enlife\",\n\"enlight\",\n\"enlink\",\n\"enlist\",\n\"enliven\",\n\"enlock\",\n\"enlodge\",\n\"enmask\",\n\"enmass\",\n\"enmesh\",\n\"enmist\",\n\"enmity\",\n\"enmoss\",\n\"ennead\",\n\"ennerve\",\n\"enniche\",\n\"ennoble\",\n\"ennoic\",\n\"ennomic\",\n\"ennui\",\n\"enocyte\",\n\"enodal\",\n\"enoil\",\n\"enol\",\n\"enolate\",\n\"enolic\",\n\"enolize\",\n\"enomoty\",\n\"enoplan\",\n\"enorm\",\n\"enough\",\n\"enounce\",\n\"enow\",\n\"enplane\",\n\"enquire\",\n\"enquiry\",\n\"enrace\",\n\"enrage\",\n\"enraged\",\n\"enrange\",\n\"enrank\",\n\"enrapt\",\n\"enray\",\n\"enrib\",\n\"enrich\",\n\"enring\",\n\"enrive\",\n\"enrobe\",\n\"enrober\",\n\"enrol\",\n\"enroll\",\n\"enroot\",\n\"enrough\",\n\"enruin\",\n\"enrut\",\n\"ens\",\n\"ensaint\",\n\"ensand\",\n\"ensate\",\n\"enscene\",\n\"ense\",\n\"enseam\",\n\"enseat\",\n\"enseem\",\n\"enserf\",\n\"ensete\",\n\"enshade\",\n\"enshawl\",\n\"enshell\",\n\"ensign\",\n\"ensile\",\n\"ensky\",\n\"enslave\",\n\"ensmall\",\n\"ensnare\",\n\"ensnarl\",\n\"ensnow\",\n\"ensoul\",\n\"enspell\",\n\"enstamp\",\n\"enstar\",\n\"enstate\",\n\"ensteel\",\n\"enstool\",\n\"enstore\",\n\"ensuant\",\n\"ensue\",\n\"ensuer\",\n\"ensure\",\n\"ensurer\",\n\"ensweep\",\n\"entach\",\n\"entad\",\n\"entail\",\n\"ental\",\n\"entame\",\n\"entasia\",\n\"entasis\",\n\"entelam\",\n\"entente\",\n\"enter\",\n\"enteral\",\n\"enterer\",\n\"enteria\",\n\"enteric\",\n\"enteron\",\n\"entheal\",\n\"enthral\",\n\"enthuse\",\n\"entia\",\n\"entice\",\n\"enticer\",\n\"entify\",\n\"entire\",\n\"entiris\",\n\"entitle\",\n\"entity\",\n\"entoil\",\n\"entomb\",\n\"entomic\",\n\"entone\",\n\"entopic\",\n\"entotic\",\n\"entozoa\",\n\"entrail\",\n\"entrain\",\n\"entrant\",\n\"entrap\",\n\"entreat\",\n\"entree\",\n\"entropy\",\n\"entrust\",\n\"entry\",\n\"entwine\",\n\"entwist\",\n\"enure\",\n\"enurny\",\n\"envapor\",\n\"envault\",\n\"enveil\",\n\"envelop\",\n\"envenom\",\n\"envied\",\n\"envier\",\n\"envious\",\n\"environ\",\n\"envoy\",\n\"envy\",\n\"envying\",\n\"enwiden\",\n\"enwind\",\n\"enwisen\",\n\"enwoman\",\n\"enwomb\",\n\"enwood\",\n\"enwound\",\n\"enwrap\",\n\"enwrite\",\n\"enzone\",\n\"enzooty\",\n\"enzym\",\n\"enzyme\",\n\"enzymic\",\n\"eoan\",\n\"eolith\",\n\"eon\",\n\"eonism\",\n\"eophyte\",\n\"eosate\",\n\"eoside\",\n\"eosin\",\n\"eosinic\",\n\"eozoon\",\n\"epacme\",\n\"epacrid\",\n\"epact\",\n\"epactal\",\n\"epagoge\",\n\"epanody\",\n\"eparch\",\n\"eparchy\",\n\"epaule\",\n\"epaulet\",\n\"epaxial\",\n\"epee\",\n\"epeeist\",\n\"epeiric\",\n\"epeirid\",\n\"epergne\",\n\"epha\",\n\"ephah\",\n\"ephebe\",\n\"ephebic\",\n\"ephebos\",\n\"ephebus\",\n\"ephelis\",\n\"ephetae\",\n\"ephete\",\n\"ephetic\",\n\"ephod\",\n\"ephor\",\n\"ephoral\",\n\"ephoric\",\n\"ephorus\",\n\"ephyra\",\n\"epibole\",\n\"epiboly\",\n\"epic\",\n\"epical\",\n\"epicarp\",\n\"epicede\",\n\"epicele\",\n\"epicene\",\n\"epichil\",\n\"epicism\",\n\"epicist\",\n\"epicly\",\n\"epicure\",\n\"epicyte\",\n\"epidemy\",\n\"epiderm\",\n\"epidote\",\n\"epigeal\",\n\"epigean\",\n\"epigeic\",\n\"epigene\",\n\"epigone\",\n\"epigram\",\n\"epigyne\",\n\"epigyny\",\n\"epihyal\",\n\"epikeia\",\n\"epilate\",\n\"epilobe\",\n\"epimer\",\n\"epimere\",\n\"epimyth\",\n\"epinaos\",\n\"epinine\",\n\"epiotic\",\n\"epipial\",\n\"episode\",\n\"epistle\",\n\"epitaph\",\n\"epitela\",\n\"epithem\",\n\"epithet\",\n\"epitoke\",\n\"epitome\",\n\"epiural\",\n\"epizoa\",\n\"epizoal\",\n\"epizoan\",\n\"epizoic\",\n\"epizoon\",\n\"epoch\",\n\"epocha\",\n\"epochal\",\n\"epode\",\n\"epodic\",\n\"eponym\",\n\"eponymy\",\n\"epopee\",\n\"epopt\",\n\"epoptes\",\n\"epoptic\",\n\"epos\",\n\"epsilon\",\n\"epulary\",\n\"epulis\",\n\"epulo\",\n\"epuloid\",\n\"epural\",\n\"epurate\",\n\"equable\",\n\"equably\",\n\"equal\",\n\"equally\",\n\"equant\",\n\"equate\",\n\"equator\",\n\"equerry\",\n\"equid\",\n\"equine\",\n\"equinia\",\n\"equinox\",\n\"equinus\",\n\"equip\",\n\"equiped\",\n\"equison\",\n\"equites\",\n\"equity\",\n\"equoid\",\n\"er\",\n\"era\",\n\"erade\",\n\"eral\",\n\"eranist\",\n\"erase\",\n\"erased\",\n\"eraser\",\n\"erasion\",\n\"erasure\",\n\"erbia\",\n\"erbium\",\n\"erd\",\n\"erdvark\",\n\"ere\",\n\"erect\",\n\"erecter\",\n\"erectly\",\n\"erector\",\n\"erelong\",\n\"eremic\",\n\"eremite\",\n\"erenach\",\n\"erenow\",\n\"erepsin\",\n\"erept\",\n\"ereptic\",\n\"erethic\",\n\"erg\",\n\"ergal\",\n\"ergasia\",\n\"ergates\",\n\"ergodic\",\n\"ergoism\",\n\"ergon\",\n\"ergot\",\n\"ergoted\",\n\"ergotic\",\n\"ergotin\",\n\"ergusia\",\n\"eria\",\n\"eric\",\n\"ericad\",\n\"erical\",\n\"ericius\",\n\"ericoid\",\n\"erika\",\n\"erikite\",\n\"erineum\",\n\"erinite\",\n\"erinose\",\n\"eristic\",\n\"erizo\",\n\"erlking\",\n\"ermelin\",\n\"ermine\",\n\"ermined\",\n\"erminee\",\n\"ermines\",\n\"erne\",\n\"erode\",\n\"eroded\",\n\"erodent\",\n\"erogeny\",\n\"eros\",\n\"erose\",\n\"erosely\",\n\"erosion\",\n\"erosive\",\n\"eroteme\",\n\"erotic\",\n\"erotica\",\n\"erotism\",\n\"err\",\n\"errable\",\n\"errancy\",\n\"errand\",\n\"errant\",\n\"errata\",\n\"erratic\",\n\"erratum\",\n\"errhine\",\n\"erring\",\n\"errite\",\n\"error\",\n\"ers\",\n\"ersatz\",\n\"erth\",\n\"erthen\",\n\"erthly\",\n\"eruc\",\n\"eruca\",\n\"erucic\",\n\"erucin\",\n\"eruct\",\n\"erudit\",\n\"erudite\",\n\"erugate\",\n\"erupt\",\n\"eryngo\",\n\"es\",\n\"esca\",\n\"escalan\",\n\"escalin\",\n\"escalop\",\n\"escape\",\n\"escapee\",\n\"escaper\",\n\"escarp\",\n\"eschar\",\n\"eschara\",\n\"escheat\",\n\"eschew\",\n\"escoba\",\n\"escolar\",\n\"escort\",\n\"escribe\",\n\"escrol\",\n\"escrow\",\n\"escudo\",\n\"esculin\",\n\"esere\",\n\"eserine\",\n\"esexual\",\n\"eshin\",\n\"esker\",\n\"esne\",\n\"esodic\",\n\"esotery\",\n\"espadon\",\n\"esparto\",\n\"espave\",\n\"espial\",\n\"espier\",\n\"espinal\",\n\"espino\",\n\"esplees\",\n\"espouse\",\n\"espy\",\n\"esquire\",\n\"ess\",\n\"essang\",\n\"essay\",\n\"essayer\",\n\"essed\",\n\"essence\",\n\"essency\",\n\"essling\",\n\"essoin\",\n\"estadal\",\n\"estadio\",\n\"estado\",\n\"estamp\",\n\"estate\",\n\"esteem\",\n\"ester\",\n\"estevin\",\n\"estival\",\n\"estmark\",\n\"estoc\",\n\"estoile\",\n\"estop\",\n\"estrade\",\n\"estray\",\n\"estre\",\n\"estreat\",\n\"estrepe\",\n\"estrin\",\n\"estriol\",\n\"estrone\",\n\"estrous\",\n\"estrual\",\n\"estuary\",\n\"estufa\",\n\"estuous\",\n\"estus\",\n\"eta\",\n\"etacism\",\n\"etacist\",\n\"etalon\",\n\"etamine\",\n\"etch\",\n\"etcher\",\n\"etching\",\n\"eternal\",\n\"etesian\",\n\"ethal\",\n\"ethanal\",\n\"ethane\",\n\"ethanol\",\n\"ethel\",\n\"ethene\",\n\"ethenic\",\n\"ethenol\",\n\"ethenyl\",\n\"ether\",\n\"ethered\",\n\"etheric\",\n\"etherin\",\n\"ethic\",\n\"ethical\",\n\"ethics\",\n\"ethid\",\n\"ethide\",\n\"ethine\",\n\"ethiops\",\n\"ethmoid\",\n\"ethnal\",\n\"ethnic\",\n\"ethnize\",\n\"ethnos\",\n\"ethos\",\n\"ethoxyl\",\n\"ethrog\",\n\"ethyl\",\n\"ethylic\",\n\"ethylin\",\n\"ethyne\",\n\"ethynyl\",\n\"etiolin\",\n\"etna\",\n\"ettle\",\n\"etua\",\n\"etude\",\n\"etui\",\n\"etym\",\n\"etymic\",\n\"etymon\",\n\"etypic\",\n\"eu\",\n\"euaster\",\n\"eucaine\",\n\"euchre\",\n\"euchred\",\n\"euclase\",\n\"eucone\",\n\"euconic\",\n\"eucrasy\",\n\"eucrite\",\n\"euge\",\n\"eugenic\",\n\"eugenol\",\n\"eugeny\",\n\"eulalia\",\n\"eulogia\",\n\"eulogic\",\n\"eulogy\",\n\"eumenid\",\n\"eunicid\",\n\"eunomy\",\n\"eunuch\",\n\"euonym\",\n\"euonymy\",\n\"euouae\",\n\"eupad\",\n\"eupathy\",\n\"eupepsy\",\n\"euphemy\",\n\"euphon\",\n\"euphone\",\n\"euphony\",\n\"euphory\",\n\"euphroe\",\n\"eupione\",\n\"euploid\",\n\"eupnea\",\n\"eureka\",\n\"euripus\",\n\"eurite\",\n\"eurobin\",\n\"euryon\",\n\"eusol\",\n\"eustyle\",\n\"eutaxic\",\n\"eutaxy\",\n\"eutexia\",\n\"eutony\",\n\"evacue\",\n\"evacuee\",\n\"evade\",\n\"evader\",\n\"evalue\",\n\"evangel\",\n\"evanish\",\n\"evase\",\n\"evasion\",\n\"evasive\",\n\"eve\",\n\"evejar\",\n\"evelong\",\n\"even\",\n\"evener\",\n\"evening\",\n\"evenly\",\n\"evens\",\n\"event\",\n\"eveque\",\n\"ever\",\n\"evert\",\n\"evertor\",\n\"everwho\",\n\"every\",\n\"evestar\",\n\"evetide\",\n\"eveweed\",\n\"evict\",\n\"evictor\",\n\"evident\",\n\"evil\",\n\"evilly\",\n\"evince\",\n\"evirate\",\n\"evisite\",\n\"evitate\",\n\"evocate\",\n\"evoe\",\n\"evoke\",\n\"evoker\",\n\"evolute\",\n\"evolve\",\n\"evolver\",\n\"evovae\",\n\"evulse\",\n\"evzone\",\n\"ewder\",\n\"ewe\",\n\"ewer\",\n\"ewerer\",\n\"ewery\",\n\"ewry\",\n\"ex\",\n\"exact\",\n\"exacter\",\n\"exactly\",\n\"exactor\",\n\"exalate\",\n\"exalt\",\n\"exalted\",\n\"exalter\",\n\"exam\",\n\"examen\",\n\"examine\",\n\"example\",\n\"exarate\",\n\"exarch\",\n\"exarchy\",\n\"excamb\",\n\"excave\",\n\"exceed\",\n\"excel\",\n\"except\",\n\"excerpt\",\n\"excess\",\n\"excide\",\n\"exciple\",\n\"excise\",\n\"excisor\",\n\"excite\",\n\"excited\",\n\"exciter\",\n\"excitor\",\n\"exclaim\",\n\"exclave\",\n\"exclude\",\n\"excreta\",\n\"excrete\",\n\"excurse\",\n\"excusal\",\n\"excuse\",\n\"excuser\",\n\"excuss\",\n\"excyst\",\n\"exdie\",\n\"exeat\",\n\"execute\",\n\"exedent\",\n\"exedra\",\n\"exegete\",\n\"exempt\",\n\"exequy\",\n\"exergue\",\n\"exert\",\n\"exes\",\n\"exeunt\",\n\"exflect\",\n\"exhale\",\n\"exhaust\",\n\"exhibit\",\n\"exhort\",\n\"exhume\",\n\"exhumer\",\n\"exigent\",\n\"exile\",\n\"exiler\",\n\"exilian\",\n\"exilic\",\n\"exility\",\n\"exist\",\n\"exister\",\n\"exit\",\n\"exite\",\n\"exition\",\n\"exitus\",\n\"exlex\",\n\"exocarp\",\n\"exocone\",\n\"exode\",\n\"exoderm\",\n\"exodic\",\n\"exodist\",\n\"exodos\",\n\"exodus\",\n\"exody\",\n\"exogamy\",\n\"exogen\",\n\"exogeny\",\n\"exomion\",\n\"exomis\",\n\"exon\",\n\"exoner\",\n\"exopod\",\n\"exordia\",\n\"exormia\",\n\"exosmic\",\n\"exostra\",\n\"exotic\",\n\"exotism\",\n\"expand\",\n\"expanse\",\n\"expect\",\n\"expede\",\n\"expel\",\n\"expend\",\n\"expense\",\n\"expert\",\n\"expiate\",\n\"expire\",\n\"expiree\",\n\"expirer\",\n\"expiry\",\n\"explain\",\n\"explant\",\n\"explode\",\n\"exploit\",\n\"explore\",\n\"expone\",\n\"export\",\n\"exposal\",\n\"expose\",\n\"exposed\",\n\"exposer\",\n\"exposit\",\n\"expound\",\n\"express\",\n\"expugn\",\n\"expulse\",\n\"expunge\",\n\"expurge\",\n\"exradio\",\n\"exscind\",\n\"exsect\",\n\"exsert\",\n\"exship\",\n\"exsurge\",\n\"extant\",\n\"extend\",\n\"extense\",\n\"extent\",\n\"exter\",\n\"extern\",\n\"externe\",\n\"extima\",\n\"extinct\",\n\"extine\",\n\"extol\",\n\"extoll\",\n\"extort\",\n\"extra\",\n\"extract\",\n\"extrait\",\n\"extreme\",\n\"extrude\",\n\"extund\",\n\"exudate\",\n\"exude\",\n\"exult\",\n\"exultet\",\n\"exuviae\",\n\"exuvial\",\n\"ey\",\n\"eyah\",\n\"eyalet\",\n\"eyas\",\n\"eye\",\n\"eyeball\",\n\"eyebalm\",\n\"eyebar\",\n\"eyebeam\",\n\"eyebolt\",\n\"eyebree\",\n\"eyebrow\",\n\"eyecup\",\n\"eyed\",\n\"eyedot\",\n\"eyedrop\",\n\"eyeflap\",\n\"eyeful\",\n\"eyehole\",\n\"eyelash\",\n\"eyeless\",\n\"eyelet\",\n\"eyelid\",\n\"eyelike\",\n\"eyeline\",\n\"eyemark\",\n\"eyen\",\n\"eyepit\",\n\"eyer\",\n\"eyeroot\",\n\"eyeseed\",\n\"eyeshot\",\n\"eyesome\",\n\"eyesore\",\n\"eyespot\",\n\"eyewash\",\n\"eyewear\",\n\"eyewink\",\n\"eyewort\",\n\"eyey\",\n\"eying\",\n\"eyn\",\n\"eyne\",\n\"eyot\",\n\"eyoty\",\n\"eyra\",\n\"eyre\",\n\"eyrie\",\n\"eyrir\",\n\"ezba\",\n\"f\",\n\"fa\",\n\"fabella\",\n\"fabes\",\n\"fable\",\n\"fabled\",\n\"fabler\",\n\"fabliau\",\n\"fabling\",\n\"fabric\",\n\"fabular\",\n\"facadal\",\n\"facade\",\n\"face\",\n\"faced\",\n\"faceman\",\n\"facer\",\n\"facet\",\n\"facete\",\n\"faceted\",\n\"facia\",\n\"facial\",\n\"faciend\",\n\"facient\",\n\"facies\",\n\"facile\",\n\"facing\",\n\"fack\",\n\"fackins\",\n\"facks\",\n\"fact\",\n\"factful\",\n\"faction\",\n\"factish\",\n\"factive\",\n\"factor\",\n\"factory\",\n\"factrix\",\n\"factual\",\n\"factum\",\n\"facture\",\n\"facty\",\n\"facula\",\n\"facular\",\n\"faculty\",\n\"facund\",\n\"facy\",\n\"fad\",\n\"fadable\",\n\"faddish\",\n\"faddism\",\n\"faddist\",\n\"faddle\",\n\"faddy\",\n\"fade\",\n\"faded\",\n\"fadedly\",\n\"faden\",\n\"fader\",\n\"fadge\",\n\"fading\",\n\"fady\",\n\"fae\",\n\"faerie\",\n\"faery\",\n\"faff\",\n\"faffle\",\n\"faffy\",\n\"fag\",\n\"fagald\",\n\"fage\",\n\"fager\",\n\"fagger\",\n\"faggery\",\n\"fagging\",\n\"fagine\",\n\"fagot\",\n\"fagoter\",\n\"fagoty\",\n\"faham\",\n\"fahlerz\",\n\"fahlore\",\n\"faience\",\n\"fail\",\n\"failing\",\n\"faille\",\n\"failure\",\n\"fain\",\n\"fainly\",\n\"fains\",\n\"faint\",\n\"fainter\",\n\"faintly\",\n\"faints\",\n\"fainty\",\n\"faipule\",\n\"fair\",\n\"fairer\",\n\"fairily\",\n\"fairing\",\n\"fairish\",\n\"fairly\",\n\"fairm\",\n\"fairway\",\n\"fairy\",\n\"faith\",\n\"faitour\",\n\"fake\",\n\"faker\",\n\"fakery\",\n\"fakir\",\n\"faky\",\n\"falbala\",\n\"falcade\",\n\"falcate\",\n\"falcer\",\n\"falces\",\n\"falcial\",\n\"falcon\",\n\"falcula\",\n\"faldage\",\n\"faldfee\",\n\"fall\",\n\"fallace\",\n\"fallacy\",\n\"fallage\",\n\"fallen\",\n\"faller\",\n\"falling\",\n\"fallow\",\n\"fallway\",\n\"fally\",\n\"falsary\",\n\"false\",\n\"falsely\",\n\"falsen\",\n\"falser\",\n\"falsie\",\n\"falsify\",\n\"falsism\",\n\"faltche\",\n\"falter\",\n\"falutin\",\n\"falx\",\n\"fam\",\n\"famble\",\n\"fame\",\n\"fameful\",\n\"familia\",\n\"family\",\n\"famine\",\n\"famish\",\n\"famous\",\n\"famulus\",\n\"fan\",\n\"fana\",\n\"fanal\",\n\"fanam\",\n\"fanatic\",\n\"fanback\",\n\"fancied\",\n\"fancier\",\n\"fancify\",\n\"fancy\",\n\"fand\",\n\"fandom\",\n\"fanega\",\n\"fanfare\",\n\"fanfoot\",\n\"fang\",\n\"fanged\",\n\"fangle\",\n\"fangled\",\n\"fanglet\",\n\"fangot\",\n\"fangy\",\n\"fanion\",\n\"fanlike\",\n\"fanman\",\n\"fannel\",\n\"fanner\",\n\"fannier\",\n\"fanning\",\n\"fanon\",\n\"fant\",\n\"fantail\",\n\"fantast\",\n\"fantasy\",\n\"fantod\",\n\"fanweed\",\n\"fanwise\",\n\"fanwork\",\n\"fanwort\",\n\"faon\",\n\"far\",\n\"farad\",\n\"faraday\",\n\"faradic\",\n\"faraway\",\n\"farce\",\n\"farcer\",\n\"farcial\",\n\"farcied\",\n\"farcify\",\n\"farcing\",\n\"farcist\",\n\"farcy\",\n\"farde\",\n\"fardel\",\n\"fardh\",\n\"fardo\",\n\"fare\",\n\"farer\",\n\"farfara\",\n\"farfel\",\n\"fargood\",\n\"farina\",\n\"faring\",\n\"farish\",\n\"farl\",\n\"farleu\",\n\"farm\",\n\"farmage\",\n\"farmer\",\n\"farmery\",\n\"farming\",\n\"farmost\",\n\"farmy\",\n\"farness\",\n\"faro\",\n\"farrago\",\n\"farrand\",\n\"farrier\",\n\"farrow\",\n\"farruca\",\n\"farse\",\n\"farseer\",\n\"farset\",\n\"farther\",\n\"fasces\",\n\"fascet\",\n\"fascia\",\n\"fascial\",\n\"fascine\",\n\"fascis\",\n\"fascism\",\n\"fascist\",\n\"fash\",\n\"fasher\",\n\"fashery\",\n\"fashion\",\n\"fass\",\n\"fast\",\n\"fasten\",\n\"faster\",\n\"fasting\",\n\"fastish\",\n\"fastus\",\n\"fat\",\n\"fatal\",\n\"fatally\",\n\"fatbird\",\n\"fate\",\n\"fated\",\n\"fateful\",\n\"fathead\",\n\"father\",\n\"fathmur\",\n\"fathom\",\n\"fatidic\",\n\"fatigue\",\n\"fatiha\",\n\"fatil\",\n\"fatless\",\n\"fatling\",\n\"fatly\",\n\"fatness\",\n\"fatsia\",\n\"fatten\",\n\"fatter\",\n\"fattily\",\n\"fattish\",\n\"fatty\",\n\"fatuism\",\n\"fatuity\",\n\"fatuoid\",\n\"fatuous\",\n\"fatwood\",\n\"faucal\",\n\"fauces\",\n\"faucet\",\n\"faucial\",\n\"faucre\",\n\"faugh\",\n\"fauld\",\n\"fault\",\n\"faulter\",\n\"faulty\",\n\"faun\",\n\"faunal\",\n\"faunish\",\n\"faunist\",\n\"faunule\",\n\"fause\",\n\"faust\",\n\"fautor\",\n\"fauve\",\n\"favella\",\n\"favilla\",\n\"favism\",\n\"favissa\",\n\"favn\",\n\"favor\",\n\"favored\",\n\"favorer\",\n\"favose\",\n\"favous\",\n\"favus\",\n\"fawn\",\n\"fawner\",\n\"fawnery\",\n\"fawning\",\n\"fawny\",\n\"fay\",\n\"fayles\",\n\"faze\",\n\"fazenda\",\n\"fe\",\n\"feague\",\n\"feak\",\n\"feal\",\n\"fealty\",\n\"fear\",\n\"feared\",\n\"fearer\",\n\"fearful\",\n\"feasor\",\n\"feast\",\n\"feasten\",\n\"feaster\",\n\"feat\",\n\"feather\",\n\"featly\",\n\"featous\",\n\"feature\",\n\"featy\",\n\"feaze\",\n\"febrile\",\n\"fecal\",\n\"feces\",\n\"feck\",\n\"feckful\",\n\"feckly\",\n\"fecula\",\n\"fecund\",\n\"fed\",\n\"feddan\",\n\"federal\",\n\"fee\",\n\"feeable\",\n\"feeble\",\n\"feebly\",\n\"feed\",\n\"feedbin\",\n\"feedbox\",\n\"feeder\",\n\"feeding\",\n\"feedman\",\n\"feedway\",\n\"feedy\",\n\"feel\",\n\"feeler\",\n\"feeless\",\n\"feeling\",\n\"feer\",\n\"feere\",\n\"feering\",\n\"feetage\",\n\"feeze\",\n\"fegary\",\n\"fei\",\n\"feif\",\n\"feigher\",\n\"feign\",\n\"feigned\",\n\"feigner\",\n\"feil\",\n\"feint\",\n\"feis\",\n\"feist\",\n\"feisty\",\n\"felid\",\n\"feline\",\n\"fell\",\n\"fellage\",\n\"fellah\",\n\"fellen\",\n\"feller\",\n\"fellic\",\n\"felling\",\n\"felloe\",\n\"fellow\",\n\"felly\",\n\"feloid\",\n\"felon\",\n\"felonry\",\n\"felony\",\n\"fels\",\n\"felsite\",\n\"felt\",\n\"felted\",\n\"felter\",\n\"felting\",\n\"felty\",\n\"felucca\",\n\"felwort\",\n\"female\",\n\"feme\",\n\"femic\",\n\"feminal\",\n\"feminie\",\n\"feminin\",\n\"femora\",\n\"femoral\",\n\"femur\",\n\"fen\",\n\"fenbank\",\n\"fence\",\n\"fencer\",\n\"fenchyl\",\n\"fencing\",\n\"fend\",\n\"fender\",\n\"fendy\",\n\"fenite\",\n\"fenks\",\n\"fenland\",\n\"fenman\",\n\"fennec\",\n\"fennel\",\n\"fennig\",\n\"fennish\",\n\"fenny\",\n\"fensive\",\n\"fent\",\n\"fenter\",\n\"feod\",\n\"feodal\",\n\"feodary\",\n\"feoff\",\n\"feoffee\",\n\"feoffor\",\n\"feower\",\n\"feral\",\n\"feralin\",\n\"ferash\",\n\"ferdwit\",\n\"ferfet\",\n\"feria\",\n\"ferial\",\n\"feridgi\",\n\"ferie\",\n\"ferine\",\n\"ferity\",\n\"ferk\",\n\"ferling\",\n\"ferly\",\n\"fermail\",\n\"ferme\",\n\"ferment\",\n\"fermery\",\n\"fermila\",\n\"fern\",\n\"ferned\",\n\"fernery\",\n\"ferny\",\n\"feroher\",\n\"ferrado\",\n\"ferrate\",\n\"ferrean\",\n\"ferret\",\n\"ferrety\",\n\"ferri\",\n\"ferric\",\n\"ferrier\",\n\"ferrite\",\n\"ferrous\",\n\"ferrule\",\n\"ferrum\",\n\"ferry\",\n\"fertile\",\n\"feru\",\n\"ferula\",\n\"ferule\",\n\"ferulic\",\n\"fervent\",\n\"fervid\",\n\"fervor\",\n\"fescue\",\n\"fess\",\n\"fessely\",\n\"fest\",\n\"festal\",\n\"fester\",\n\"festine\",\n\"festive\",\n\"festoon\",\n\"festuca\",\n\"fet\",\n\"fetal\",\n\"fetch\",\n\"fetched\",\n\"fetcher\",\n\"fetial\",\n\"fetid\",\n\"fetidly\",\n\"fetish\",\n\"fetlock\",\n\"fetlow\",\n\"fetor\",\n\"fetter\",\n\"fettle\",\n\"fettler\",\n\"fetus\",\n\"feu\",\n\"feuage\",\n\"feuar\",\n\"feucht\",\n\"feud\",\n\"feudal\",\n\"feudee\",\n\"feudist\",\n\"feued\",\n\"feuille\",\n\"fever\",\n\"feveret\",\n\"few\",\n\"fewness\",\n\"fewsome\",\n\"fewter\",\n\"fey\",\n\"feyness\",\n\"fez\",\n\"fezzed\",\n\"fezzy\",\n\"fi\",\n\"fiacre\",\n\"fiance\",\n\"fiancee\",\n\"fiar\",\n\"fiard\",\n\"fiasco\",\n\"fiat\",\n\"fib\",\n\"fibber\",\n\"fibbery\",\n\"fibdom\",\n\"fiber\",\n\"fibered\",\n\"fibril\",\n\"fibrin\",\n\"fibrine\",\n\"fibroid\",\n\"fibroin\",\n\"fibroma\",\n\"fibrose\",\n\"fibrous\",\n\"fibry\",\n\"fibster\",\n\"fibula\",\n\"fibulae\",\n\"fibular\",\n\"ficary\",\n\"fice\",\n\"ficelle\",\n\"fiche\",\n\"fichu\",\n\"fickle\",\n\"fickly\",\n\"fico\",\n\"ficoid\",\n\"fictile\",\n\"fiction\",\n\"fictive\",\n\"fid\",\n\"fidalgo\",\n\"fidate\",\n\"fiddle\",\n\"fiddler\",\n\"fiddley\",\n\"fide\",\n\"fideism\",\n\"fideist\",\n\"fidfad\",\n\"fidge\",\n\"fidget\",\n\"fidgety\",\n\"fiducia\",\n\"fie\",\n\"fiefdom\",\n\"field\",\n\"fielded\",\n\"fielder\",\n\"fieldy\",\n\"fiend\",\n\"fiendly\",\n\"fient\",\n\"fierce\",\n\"fiercen\",\n\"fierily\",\n\"fiery\",\n\"fiesta\",\n\"fife\",\n\"fifer\",\n\"fifie\",\n\"fifish\",\n\"fifo\",\n\"fifteen\",\n\"fifth\",\n\"fifthly\",\n\"fifty\",\n\"fig\",\n\"figaro\",\n\"figbird\",\n\"figent\",\n\"figged\",\n\"figgery\",\n\"figging\",\n\"figgle\",\n\"figgy\",\n\"fight\",\n\"fighter\",\n\"figless\",\n\"figlike\",\n\"figment\",\n\"figural\",\n\"figure\",\n\"figured\",\n\"figurer\",\n\"figury\",\n\"figworm\",\n\"figwort\",\n\"fike\",\n\"fikie\",\n\"filace\",\n\"filacer\",\n\"filao\",\n\"filar\",\n\"filaria\",\n\"filasse\",\n\"filate\",\n\"filator\",\n\"filbert\",\n\"filch\",\n\"filcher\",\n\"file\",\n\"filemot\",\n\"filer\",\n\"filet\",\n\"filial\",\n\"filiate\",\n\"filibeg\",\n\"filical\",\n\"filicic\",\n\"filicin\",\n\"filiety\",\n\"filing\",\n\"filings\",\n\"filippo\",\n\"filite\",\n\"fill\",\n\"filled\",\n\"filler\",\n\"fillet\",\n\"filleul\",\n\"filling\",\n\"fillip\",\n\"fillock\",\n\"filly\",\n\"film\",\n\"filmdom\",\n\"filmet\",\n\"filmic\",\n\"filmily\",\n\"filmish\",\n\"filmist\",\n\"filmize\",\n\"filmy\",\n\"filo\",\n\"filose\",\n\"fils\",\n\"filter\",\n\"filth\",\n\"filthy\",\n\"fimble\",\n\"fimbria\",\n\"fin\",\n\"finable\",\n\"finagle\",\n\"final\",\n\"finale\",\n\"finally\",\n\"finance\",\n\"finback\",\n\"finch\",\n\"finched\",\n\"find\",\n\"findal\",\n\"finder\",\n\"finding\",\n\"findjan\",\n\"fine\",\n\"fineish\",\n\"finely\",\n\"finer\",\n\"finery\",\n\"finesse\",\n\"finetop\",\n\"finfish\",\n\"finfoot\",\n\"fingent\",\n\"finger\",\n\"fingery\",\n\"finial\",\n\"finical\",\n\"finick\",\n\"finific\",\n\"finify\",\n\"finikin\",\n\"fining\",\n\"finis\",\n\"finish\",\n\"finite\",\n\"finity\",\n\"finjan\",\n\"fink\",\n\"finkel\",\n\"finland\",\n\"finless\",\n\"finlet\",\n\"finlike\",\n\"finnac\",\n\"finned\",\n\"finner\",\n\"finnip\",\n\"finny\",\n\"fiord\",\n\"fiorded\",\n\"fiorin\",\n\"fiorite\",\n\"fip\",\n\"fipenny\",\n\"fipple\",\n\"fique\",\n\"fir\",\n\"firca\",\n\"fire\",\n\"firearm\",\n\"firebox\",\n\"fireboy\",\n\"firebug\",\n\"fired\",\n\"firedog\",\n\"firefly\",\n\"firelit\",\n\"fireman\",\n\"firer\",\n\"firetop\",\n\"firing\",\n\"firk\",\n\"firker\",\n\"firkin\",\n\"firlot\",\n\"firm\",\n\"firman\",\n\"firmer\",\n\"firmly\",\n\"firn\",\n\"firring\",\n\"firry\",\n\"first\",\n\"firstly\",\n\"firth\",\n\"fisc\",\n\"fiscal\",\n\"fise\",\n\"fisetin\",\n\"fish\",\n\"fishbed\",\n\"fished\",\n\"fisher\",\n\"fishery\",\n\"fishet\",\n\"fisheye\",\n\"fishful\",\n\"fishgig\",\n\"fishify\",\n\"fishily\",\n\"fishing\",\n\"fishlet\",\n\"fishman\",\n\"fishpot\",\n\"fishway\",\n\"fishy\",\n\"fisnoga\",\n\"fissate\",\n\"fissile\",\n\"fission\",\n\"fissive\",\n\"fissure\",\n\"fissury\",\n\"fist\",\n\"fisted\",\n\"fister\",\n\"fistful\",\n\"fistic\",\n\"fistify\",\n\"fisting\",\n\"fistuca\",\n\"fistula\",\n\"fistule\",\n\"fisty\",\n\"fit\",\n\"fitch\",\n\"fitched\",\n\"fitchee\",\n\"fitcher\",\n\"fitchet\",\n\"fitchew\",\n\"fitful\",\n\"fitly\",\n\"fitment\",\n\"fitness\",\n\"fitout\",\n\"fitroot\",\n\"fittage\",\n\"fitted\",\n\"fitten\",\n\"fitter\",\n\"fitters\",\n\"fittily\",\n\"fitting\",\n\"fitty\",\n\"fitweed\",\n\"five\",\n\"fivebar\",\n\"fiver\",\n\"fives\",\n\"fix\",\n\"fixable\",\n\"fixage\",\n\"fixate\",\n\"fixatif\",\n\"fixator\",\n\"fixed\",\n\"fixedly\",\n\"fixer\",\n\"fixing\",\n\"fixity\",\n\"fixture\",\n\"fixure\",\n\"fizgig\",\n\"fizz\",\n\"fizzer\",\n\"fizzle\",\n\"fizzy\",\n\"fjeld\",\n\"flabby\",\n\"flabrum\",\n\"flaccid\",\n\"flack\",\n\"flacked\",\n\"flacker\",\n\"flacket\",\n\"flaff\",\n\"flaffer\",\n\"flag\",\n\"flagger\",\n\"flaggy\",\n\"flaglet\",\n\"flagman\",\n\"flagon\",\n\"flail\",\n\"flair\",\n\"flaith\",\n\"flak\",\n\"flakage\",\n\"flake\",\n\"flaker\",\n\"flakily\",\n\"flaky\",\n\"flam\",\n\"flamant\",\n\"flamb\",\n\"flame\",\n\"flamed\",\n\"flamen\",\n\"flamer\",\n\"flamfew\",\n\"flaming\",\n\"flamy\",\n\"flan\",\n\"flanch\",\n\"flandan\",\n\"flane\",\n\"flange\",\n\"flanger\",\n\"flank\",\n\"flanked\",\n\"flanker\",\n\"flanky\",\n\"flannel\",\n\"flanque\",\n\"flap\",\n\"flapper\",\n\"flare\",\n\"flaring\",\n\"flary\",\n\"flaser\",\n\"flash\",\n\"flasher\",\n\"flashet\",\n\"flashly\",\n\"flashy\",\n\"flask\",\n\"flasker\",\n\"flasket\",\n\"flasque\",\n\"flat\",\n\"flatcap\",\n\"flatcar\",\n\"flatdom\",\n\"flated\",\n\"flathat\",\n\"flatlet\",\n\"flatly\",\n\"flatman\",\n\"flatten\",\n\"flatter\",\n\"flattie\",\n\"flattop\",\n\"flatus\",\n\"flatway\",\n\"flaught\",\n\"flaunt\",\n\"flaunty\",\n\"flavedo\",\n\"flavic\",\n\"flavid\",\n\"flavin\",\n\"flavine\",\n\"flavo\",\n\"flavone\",\n\"flavor\",\n\"flavory\",\n\"flavour\",\n\"flaw\",\n\"flawed\",\n\"flawful\",\n\"flawn\",\n\"flawy\",\n\"flax\",\n\"flaxen\",\n\"flaxman\",\n\"flaxy\",\n\"flay\",\n\"flayer\",\n\"flea\",\n\"fleam\",\n\"fleay\",\n\"flebile\",\n\"fleche\",\n\"fleck\",\n\"flecken\",\n\"flecker\",\n\"flecky\",\n\"flector\",\n\"fled\",\n\"fledge\",\n\"fledgy\",\n\"flee\",\n\"fleece\",\n\"fleeced\",\n\"fleecer\",\n\"fleech\",\n\"fleecy\",\n\"fleer\",\n\"fleerer\",\n\"fleet\",\n\"fleeter\",\n\"fleetly\",\n\"flemish\",\n\"flench\",\n\"flense\",\n\"flenser\",\n\"flerry\",\n\"flesh\",\n\"fleshed\",\n\"fleshen\",\n\"flesher\",\n\"fleshly\",\n\"fleshy\",\n\"flet\",\n\"fletch\",\n\"flether\",\n\"fleuret\",\n\"fleury\",\n\"flew\",\n\"flewed\",\n\"flewit\",\n\"flews\",\n\"flex\",\n\"flexed\",\n\"flexile\",\n\"flexion\",\n\"flexor\",\n\"flexure\",\n\"fley\",\n\"flick\",\n\"flicker\",\n\"flicky\",\n\"flidder\",\n\"flier\",\n\"fligger\",\n\"flight\",\n\"flighty\",\n\"flimmer\",\n\"flimp\",\n\"flimsy\",\n\"flinch\",\n\"flinder\",\n\"fling\",\n\"flinger\",\n\"flingy\",\n\"flint\",\n\"flinter\",\n\"flinty\",\n\"flioma\",\n\"flip\",\n\"flipe\",\n\"flipper\",\n\"flirt\",\n\"flirter\",\n\"flirty\",\n\"flisk\",\n\"flisky\",\n\"flit\",\n\"flitch\",\n\"flite\",\n\"fliting\",\n\"flitter\",\n\"flivver\",\n\"flix\",\n\"float\",\n\"floater\",\n\"floaty\",\n\"flob\",\n\"flobby\",\n\"floc\",\n\"floccus\",\n\"flock\",\n\"flocker\",\n\"flocky\",\n\"flocoon\",\n\"flodge\",\n\"floe\",\n\"floey\",\n\"flog\",\n\"flogger\",\n\"flokite\",\n\"flong\",\n\"flood\",\n\"flooded\",\n\"flooder\",\n\"floody\",\n\"floor\",\n\"floorer\",\n\"floozy\",\n\"flop\",\n\"flopper\",\n\"floppy\",\n\"flora\",\n\"floral\",\n\"floran\",\n\"florate\",\n\"floreal\",\n\"florent\",\n\"flores\",\n\"floret\",\n\"florid\",\n\"florin\",\n\"florist\",\n\"floroon\",\n\"florula\",\n\"flory\",\n\"flosh\",\n\"floss\",\n\"flosser\",\n\"flossy\",\n\"flot\",\n\"flota\",\n\"flotage\",\n\"flotant\",\n\"flotsam\",\n\"flounce\",\n\"flour\",\n\"floury\",\n\"flouse\",\n\"flout\",\n\"flouter\",\n\"flow\",\n\"flowage\",\n\"flower\",\n\"flowery\",\n\"flowing\",\n\"flown\",\n\"flowoff\",\n\"flu\",\n\"fluate\",\n\"fluavil\",\n\"flub\",\n\"flubdub\",\n\"flucan\",\n\"flue\",\n\"flued\",\n\"flueman\",\n\"fluency\",\n\"fluent\",\n\"fluer\",\n\"fluey\",\n\"fluff\",\n\"fluffer\",\n\"fluffy\",\n\"fluible\",\n\"fluid\",\n\"fluidal\",\n\"fluidic\",\n\"fluidly\",\n\"fluke\",\n\"fluked\",\n\"flukily\",\n\"fluking\",\n\"fluky\",\n\"flume\",\n\"flummer\",\n\"flummox\",\n\"flump\",\n\"flung\",\n\"flunk\",\n\"flunker\",\n\"flunky\",\n\"fluor\",\n\"fluoran\",\n\"fluoric\",\n\"fluoryl\",\n\"flurn\",\n\"flurr\",\n\"flurry\",\n\"flush\",\n\"flusher\",\n\"flushy\",\n\"flusk\",\n\"flusker\",\n\"fluster\",\n\"flute\",\n\"fluted\",\n\"fluter\",\n\"flutina\",\n\"fluting\",\n\"flutist\",\n\"flutter\",\n\"fluty\",\n\"fluvial\",\n\"flux\",\n\"fluxer\",\n\"fluxile\",\n\"fluxion\",\n\"fly\",\n\"flyable\",\n\"flyaway\",\n\"flyback\",\n\"flyball\",\n\"flybane\",\n\"flybelt\",\n\"flyblow\",\n\"flyboat\",\n\"flyboy\",\n\"flyer\",\n\"flyflap\",\n\"flying\",\n\"flyleaf\",\n\"flyless\",\n\"flyman\",\n\"flyness\",\n\"flype\",\n\"flytail\",\n\"flytier\",\n\"flytrap\",\n\"flyway\",\n\"flywort\",\n\"foal\",\n\"foaly\",\n\"foam\",\n\"foambow\",\n\"foamer\",\n\"foamily\",\n\"foaming\",\n\"foamy\",\n\"fob\",\n\"focal\",\n\"focally\",\n\"foci\",\n\"focoids\",\n\"focsle\",\n\"focus\",\n\"focuser\",\n\"fod\",\n\"fodda\",\n\"fodder\",\n\"foder\",\n\"fodge\",\n\"fodgel\",\n\"fodient\",\n\"foe\",\n\"foehn\",\n\"foeish\",\n\"foeless\",\n\"foelike\",\n\"foeman\",\n\"foeship\",\n\"fog\",\n\"fogbow\",\n\"fogdog\",\n\"fogdom\",\n\"fogey\",\n\"foggage\",\n\"fogged\",\n\"fogger\",\n\"foggily\",\n\"foggish\",\n\"foggy\",\n\"foghorn\",\n\"fogle\",\n\"fogless\",\n\"fogman\",\n\"fogo\",\n\"fogon\",\n\"fogou\",\n\"fogram\",\n\"fogus\",\n\"fogy\",\n\"fogydom\",\n\"fogyish\",\n\"fogyism\",\n\"fohat\",\n\"foible\",\n\"foil\",\n\"foiler\",\n\"foiling\",\n\"foining\",\n\"foison\",\n\"foist\",\n\"foister\",\n\"foisty\",\n\"foiter\",\n\"fold\",\n\"foldage\",\n\"folded\",\n\"folden\",\n\"folder\",\n\"folding\",\n\"foldure\",\n\"foldy\",\n\"fole\",\n\"folia\",\n\"foliage\",\n\"folial\",\n\"foliar\",\n\"foliary\",\n\"foliate\",\n\"folie\",\n\"folio\",\n\"foliole\",\n\"foliose\",\n\"foliot\",\n\"folious\",\n\"folium\",\n\"folk\",\n\"folkmot\",\n\"folksy\",\n\"folkway\",\n\"folky\",\n\"folles\",\n\"follis\",\n\"follow\",\n\"folly\",\n\"foment\",\n\"fomes\",\n\"fomites\",\n\"fondak\",\n\"fondant\",\n\"fondish\",\n\"fondle\",\n\"fondler\",\n\"fondly\",\n\"fondu\",\n\"fondue\",\n\"fonduk\",\n\"fonly\",\n\"fonnish\",\n\"fono\",\n\"fons\",\n\"font\",\n\"fontal\",\n\"fonted\",\n\"fontful\",\n\"fontlet\",\n\"foo\",\n\"food\",\n\"fooder\",\n\"foodful\",\n\"foody\",\n\"fool\",\n\"fooldom\",\n\"foolery\",\n\"fooless\",\n\"fooling\",\n\"foolish\",\n\"fooner\",\n\"fooster\",\n\"foot\",\n\"footage\",\n\"footboy\",\n\"footed\",\n\"footer\",\n\"footful\",\n\"foothot\",\n\"footing\",\n\"footle\",\n\"footler\",\n\"footman\",\n\"footpad\",\n\"foots\",\n\"footway\",\n\"footy\",\n\"foozle\",\n\"foozler\",\n\"fop\",\n\"fopling\",\n\"foppery\",\n\"foppish\",\n\"foppy\",\n\"fopship\",\n\"for\",\n\"fora\",\n\"forage\",\n\"forager\",\n\"foramen\",\n\"forane\",\n\"foray\",\n\"forayer\",\n\"forb\",\n\"forbade\",\n\"forbar\",\n\"forbear\",\n\"forbid\",\n\"forbit\",\n\"forbled\",\n\"forblow\",\n\"forbore\",\n\"forbow\",\n\"forby\",\n\"force\",\n\"forced\",\n\"forceps\",\n\"forcer\",\n\"forche\",\n\"forcing\",\n\"ford\",\n\"fordays\",\n\"fording\",\n\"fordo\",\n\"fordone\",\n\"fordy\",\n\"fore\",\n\"foreact\",\n\"forearm\",\n\"forebay\",\n\"forecar\",\n\"foreday\",\n\"forefin\",\n\"forefit\",\n\"forego\",\n\"foreign\",\n\"forel\",\n\"forelay\",\n\"foreleg\",\n\"foreman\",\n\"forepad\",\n\"forepaw\",\n\"foreran\",\n\"forerib\",\n\"forerun\",\n\"foresay\",\n\"foresee\",\n\"foreset\",\n\"foresin\",\n\"forest\",\n\"foresty\",\n\"foretop\",\n\"foreuse\",\n\"forever\",\n\"forevow\",\n\"forfar\",\n\"forfare\",\n\"forfars\",\n\"forfeit\",\n\"forfend\",\n\"forge\",\n\"forged\",\n\"forger\",\n\"forgery\",\n\"forget\",\n\"forgie\",\n\"forging\",\n\"forgive\",\n\"forgo\",\n\"forgoer\",\n\"forgot\",\n\"forgrow\",\n\"forhoo\",\n\"forhooy\",\n\"forhow\",\n\"forint\",\n\"fork\",\n\"forked\",\n\"forker\",\n\"forkful\",\n\"forkman\",\n\"forky\",\n\"forleft\",\n\"forlet\",\n\"forlorn\",\n\"form\",\n\"formal\",\n\"formant\",\n\"format\",\n\"formate\",\n\"forme\",\n\"formed\",\n\"formee\",\n\"formel\",\n\"formene\",\n\"former\",\n\"formful\",\n\"formic\",\n\"formin\",\n\"forming\",\n\"formose\",\n\"formula\",\n\"formule\",\n\"formy\",\n\"formyl\",\n\"fornent\",\n\"fornix\",\n\"forpet\",\n\"forpine\",\n\"forpit\",\n\"forrad\",\n\"forrard\",\n\"forride\",\n\"forrit\",\n\"forrue\",\n\"forsake\",\n\"forset\",\n\"forslow\",\n\"fort\",\n\"forte\",\n\"forth\",\n\"forthgo\",\n\"forthy\",\n\"forties\",\n\"fortify\",\n\"fortin\",\n\"fortis\",\n\"fortlet\",\n\"fortune\",\n\"forty\",\n\"forum\",\n\"forward\",\n\"forwean\",\n\"forwent\",\n\"fosh\",\n\"fosie\",\n\"fossa\",\n\"fossage\",\n\"fossane\",\n\"fosse\",\n\"fossed\",\n\"fossick\",\n\"fossil\",\n\"fossor\",\n\"fossula\",\n\"fossule\",\n\"fostell\",\n\"foster\",\n\"fot\",\n\"fotch\",\n\"fother\",\n\"fotmal\",\n\"fotui\",\n\"fou\",\n\"foud\",\n\"fouette\",\n\"fougade\",\n\"fought\",\n\"foughty\",\n\"foujdar\",\n\"foul\",\n\"foulage\",\n\"foulard\",\n\"fouler\",\n\"fouling\",\n\"foulish\",\n\"foully\",\n\"foumart\",\n\"foun\",\n\"found\",\n\"founder\",\n\"foundry\",\n\"fount\",\n\"four\",\n\"fourble\",\n\"fourche\",\n\"fourer\",\n\"fourre\",\n\"fourth\",\n\"foussa\",\n\"foute\",\n\"fouter\",\n\"fouth\",\n\"fovea\",\n\"foveal\",\n\"foveate\",\n\"foveola\",\n\"foveole\",\n\"fow\",\n\"fowk\",\n\"fowl\",\n\"fowler\",\n\"fowlery\",\n\"fowling\",\n\"fox\",\n\"foxbane\",\n\"foxchop\",\n\"foxer\",\n\"foxery\",\n\"foxfeet\",\n\"foxfish\",\n\"foxhole\",\n\"foxily\",\n\"foxing\",\n\"foxish\",\n\"foxlike\",\n\"foxship\",\n\"foxskin\",\n\"foxtail\",\n\"foxwood\",\n\"foxy\",\n\"foy\",\n\"foyaite\",\n\"foyboat\",\n\"foyer\",\n\"fozy\",\n\"fra\",\n\"frab\",\n\"frabbit\",\n\"frabous\",\n\"fracas\",\n\"frache\",\n\"frack\",\n\"fracted\",\n\"frae\",\n\"fraghan\",\n\"fragile\",\n\"fraid\",\n\"fraik\",\n\"frail\",\n\"frailly\",\n\"frailty\",\n\"fraise\",\n\"fraiser\",\n\"frame\",\n\"framea\",\n\"framed\",\n\"framer\",\n\"framing\",\n\"frammit\",\n\"franc\",\n\"franco\",\n\"frank\",\n\"franker\",\n\"frankly\",\n\"frantic\",\n\"franzy\",\n\"frap\",\n\"frappe\",\n\"frasco\",\n\"frase\",\n\"frasier\",\n\"frass\",\n\"frat\",\n\"fratch\",\n\"fratchy\",\n\"frater\",\n\"fratery\",\n\"fratry\",\n\"fraud\",\n\"fraught\",\n\"frawn\",\n\"fraxin\",\n\"fray\",\n\"frayed\",\n\"fraying\",\n\"frayn\",\n\"fraze\",\n\"frazer\",\n\"frazil\",\n\"frazzle\",\n\"freak\",\n\"freaky\",\n\"fream\",\n\"freath\",\n\"freck\",\n\"frecken\",\n\"frecket\",\n\"freckle\",\n\"freckly\",\n\"free\",\n\"freed\",\n\"freedom\",\n\"freeing\",\n\"freeish\",\n\"freely\",\n\"freeman\",\n\"freer\",\n\"freet\",\n\"freety\",\n\"freeway\",\n\"freeze\",\n\"freezer\",\n\"freight\",\n\"freir\",\n\"freit\",\n\"freity\",\n\"fremd\",\n\"fremdly\",\n\"frenal\",\n\"frenate\",\n\"frenum\",\n\"frenzy\",\n\"fresco\",\n\"fresh\",\n\"freshen\",\n\"freshet\",\n\"freshly\",\n\"fresnel\",\n\"fresno\",\n\"fret\",\n\"fretful\",\n\"frett\",\n\"frette\",\n\"fretted\",\n\"fretter\",\n\"fretty\",\n\"fretum\",\n\"friable\",\n\"friand\",\n\"friar\",\n\"friarly\",\n\"friary\",\n\"frib\",\n\"fribble\",\n\"fribby\",\n\"fried\",\n\"friend\",\n\"frier\",\n\"frieze\",\n\"friezer\",\n\"friezy\",\n\"frig\",\n\"frigate\",\n\"friggle\",\n\"fright\",\n\"frighty\",\n\"frigid\",\n\"frijol\",\n\"frike\",\n\"frill\",\n\"frilled\",\n\"friller\",\n\"frilly\",\n\"frim\",\n\"fringe\",\n\"fringed\",\n\"fringy\",\n\"frisca\",\n\"frisk\",\n\"frisker\",\n\"frisket\",\n\"frisky\",\n\"frison\",\n\"frist\",\n\"frisure\",\n\"frit\",\n\"frith\",\n\"fritt\",\n\"fritter\",\n\"frivol\",\n\"frixion\",\n\"friz\",\n\"frize\",\n\"frizer\",\n\"frizz\",\n\"frizzer\",\n\"frizzle\",\n\"frizzly\",\n\"frizzy\",\n\"fro\",\n\"frock\",\n\"froe\",\n\"frog\",\n\"frogbit\",\n\"frogeye\",\n\"frogged\",\n\"froggy\",\n\"frogleg\",\n\"froglet\",\n\"frogman\",\n\"froise\",\n\"frolic\",\n\"from\",\n\"frond\",\n\"fronded\",\n\"front\",\n\"frontad\",\n\"frontal\",\n\"fronted\",\n\"fronter\",\n\"froom\",\n\"frore\",\n\"frory\",\n\"frosh\",\n\"frost\",\n\"frosted\",\n\"froster\",\n\"frosty\",\n\"frot\",\n\"froth\",\n\"frother\",\n\"frothy\",\n\"frotton\",\n\"frough\",\n\"froughy\",\n\"frounce\",\n\"frow\",\n\"froward\",\n\"frower\",\n\"frowl\",\n\"frown\",\n\"frowner\",\n\"frowny\",\n\"frowst\",\n\"frowsty\",\n\"frowy\",\n\"frowze\",\n\"frowzly\",\n\"frowzy\",\n\"froze\",\n\"frozen\",\n\"fructed\",\n\"frugal\",\n\"fruggan\",\n\"fruit\",\n\"fruited\",\n\"fruiter\",\n\"fruity\",\n\"frump\",\n\"frumple\",\n\"frumpy\",\n\"frush\",\n\"frustum\",\n\"frutify\",\n\"fry\",\n\"fryer\",\n\"fu\",\n\"fub\",\n\"fubby\",\n\"fubsy\",\n\"fucate\",\n\"fuchsin\",\n\"fuci\",\n\"fucoid\",\n\"fucosan\",\n\"fucose\",\n\"fucous\",\n\"fucus\",\n\"fud\",\n\"fuddle\",\n\"fuddler\",\n\"fuder\",\n\"fudge\",\n\"fudger\",\n\"fudgy\",\n\"fuel\",\n\"fueler\",\n\"fuerte\",\n\"fuff\",\n\"fuffy\",\n\"fugal\",\n\"fugally\",\n\"fuggy\",\n\"fugient\",\n\"fugle\",\n\"fugler\",\n\"fugu\",\n\"fugue\",\n\"fuguist\",\n\"fuidhir\",\n\"fuji\",\n\"fulcral\",\n\"fulcrum\",\n\"fulfill\",\n\"fulgent\",\n\"fulgid\",\n\"fulgide\",\n\"fulgor\",\n\"fulham\",\n\"fulk\",\n\"full\",\n\"fullam\",\n\"fuller\",\n\"fullery\",\n\"fulling\",\n\"fullish\",\n\"fullom\",\n\"fully\",\n\"fulmar\",\n\"fulmine\",\n\"fulsome\",\n\"fulth\",\n\"fulvene\",\n\"fulvid\",\n\"fulvous\",\n\"fulwa\",\n\"fulyie\",\n\"fulzie\",\n\"fum\",\n\"fumado\",\n\"fumage\",\n\"fumaric\",\n\"fumaryl\",\n\"fumble\",\n\"fumbler\",\n\"fume\",\n\"fumer\",\n\"fumet\",\n\"fumette\",\n\"fumily\",\n\"fuming\",\n\"fumose\",\n\"fumous\",\n\"fumy\",\n\"fun\",\n\"fund\",\n\"fundal\",\n\"funded\",\n\"funder\",\n\"fundi\",\n\"fundic\",\n\"funds\",\n\"fundus\",\n\"funeral\",\n\"funest\",\n\"fungal\",\n\"fungate\",\n\"fungi\",\n\"fungian\",\n\"fungic\",\n\"fungin\",\n\"fungo\",\n\"fungoid\",\n\"fungose\",\n\"fungous\",\n\"fungus\",\n\"fungusy\",\n\"funicle\",\n\"funis\",\n\"funk\",\n\"funker\",\n\"funky\",\n\"funnel\",\n\"funnily\",\n\"funny\",\n\"funori\",\n\"funt\",\n\"fur\",\n\"fural\",\n\"furan\",\n\"furazan\",\n\"furbish\",\n\"furca\",\n\"furcal\",\n\"furcate\",\n\"furcula\",\n\"furdel\",\n\"furfur\",\n\"furiant\",\n\"furied\",\n\"furify\",\n\"furil\",\n\"furilic\",\n\"furiosa\",\n\"furioso\",\n\"furious\",\n\"furison\",\n\"furl\",\n\"furler\",\n\"furless\",\n\"furlong\",\n\"furnace\",\n\"furnage\",\n\"furner\",\n\"furnish\",\n\"furoic\",\n\"furoid\",\n\"furoin\",\n\"furole\",\n\"furor\",\n\"furore\",\n\"furphy\",\n\"furred\",\n\"furrier\",\n\"furrily\",\n\"furring\",\n\"furrow\",\n\"furrowy\",\n\"furry\",\n\"further\",\n\"furtive\",\n\"fury\",\n\"furyl\",\n\"furze\",\n\"furzed\",\n\"furzery\",\n\"furzy\",\n\"fusain\",\n\"fusate\",\n\"fusc\",\n\"fuscin\",\n\"fuscous\",\n\"fuse\",\n\"fused\",\n\"fusee\",\n\"fusht\",\n\"fusible\",\n\"fusibly\",\n\"fusil\",\n\"fusilly\",\n\"fusion\",\n\"fusoid\",\n\"fuss\",\n\"fusser\",\n\"fussify\",\n\"fussily\",\n\"fussock\",\n\"fussy\",\n\"fust\",\n\"fustee\",\n\"fustet\",\n\"fustian\",\n\"fustic\",\n\"fustily\",\n\"fustin\",\n\"fustle\",\n\"fusty\",\n\"fusuma\",\n\"fusure\",\n\"fut\",\n\"futchel\",\n\"fute\",\n\"futhorc\",\n\"futile\",\n\"futtock\",\n\"futural\",\n\"future\",\n\"futuric\",\n\"futwa\",\n\"fuye\",\n\"fuze\",\n\"fuzz\",\n\"fuzzily\",\n\"fuzzy\",\n\"fyke\",\n\"fylfot\",\n\"fyrd\",\n\"g\",\n\"ga\",\n\"gab\",\n\"gabbard\",\n\"gabber\",\n\"gabble\",\n\"gabbler\",\n\"gabbro\",\n\"gabby\",\n\"gabelle\",\n\"gabgab\",\n\"gabi\",\n\"gabion\",\n\"gable\",\n\"gablet\",\n\"gablock\",\n\"gaby\",\n\"gad\",\n\"gadbee\",\n\"gadbush\",\n\"gadded\",\n\"gadder\",\n\"gaddi\",\n\"gadding\",\n\"gaddish\",\n\"gade\",\n\"gadfly\",\n\"gadge\",\n\"gadger\",\n\"gadget\",\n\"gadid\",\n\"gadling\",\n\"gadman\",\n\"gadoid\",\n\"gadroon\",\n\"gadsman\",\n\"gaduin\",\n\"gadwall\",\n\"gaen\",\n\"gaet\",\n\"gaff\",\n\"gaffe\",\n\"gaffer\",\n\"gaffle\",\n\"gag\",\n\"gagate\",\n\"gage\",\n\"gagee\",\n\"gageite\",\n\"gager\",\n\"gagger\",\n\"gaggery\",\n\"gaggle\",\n\"gaggler\",\n\"gagman\",\n\"gagor\",\n\"gagroot\",\n\"gahnite\",\n\"gaiassa\",\n\"gaiety\",\n\"gaily\",\n\"gain\",\n\"gainage\",\n\"gaine\",\n\"gainer\",\n\"gainful\",\n\"gaining\",\n\"gainly\",\n\"gains\",\n\"gainsay\",\n\"gainset\",\n\"gainst\",\n\"gair\",\n\"gait\",\n\"gaited\",\n\"gaiter\",\n\"gaiting\",\n\"gaize\",\n\"gaj\",\n\"gal\",\n\"gala\",\n\"galah\",\n\"galanas\",\n\"galanga\",\n\"galant\",\n\"galany\",\n\"galatea\",\n\"galaxy\",\n\"galban\",\n\"gale\",\n\"galea\",\n\"galeage\",\n\"galeate\",\n\"galee\",\n\"galeeny\",\n\"galeid\",\n\"galena\",\n\"galenic\",\n\"galeoid\",\n\"galera\",\n\"galerum\",\n\"galerus\",\n\"galet\",\n\"galey\",\n\"galgal\",\n\"gali\",\n\"galilee\",\n\"galiot\",\n\"galipot\",\n\"gall\",\n\"galla\",\n\"gallah\",\n\"gallant\",\n\"gallate\",\n\"galled\",\n\"gallein\",\n\"galleon\",\n\"galler\",\n\"gallery\",\n\"gallet\",\n\"galley\",\n\"gallfly\",\n\"gallic\",\n\"galline\",\n\"galling\",\n\"gallium\",\n\"gallnut\",\n\"gallon\",\n\"galloon\",\n\"gallop\",\n\"gallous\",\n\"gallows\",\n\"gally\",\n\"galoot\",\n\"galop\",\n\"galore\",\n\"galosh\",\n\"galp\",\n\"galt\",\n\"galumph\",\n\"galuth\",\n\"galyac\",\n\"galyak\",\n\"gam\",\n\"gamahe\",\n\"gamasid\",\n\"gamb\",\n\"gamba\",\n\"gambade\",\n\"gambado\",\n\"gambang\",\n\"gambeer\",\n\"gambet\",\n\"gambia\",\n\"gambier\",\n\"gambist\",\n\"gambit\",\n\"gamble\",\n\"gambler\",\n\"gamboge\",\n\"gambol\",\n\"gambrel\",\n\"game\",\n\"gamebag\",\n\"gameful\",\n\"gamely\",\n\"gamene\",\n\"gametal\",\n\"gamete\",\n\"gametic\",\n\"gamic\",\n\"gamily\",\n\"gamin\",\n\"gaming\",\n\"gamma\",\n\"gammer\",\n\"gammick\",\n\"gammock\",\n\"gammon\",\n\"gammy\",\n\"gamont\",\n\"gamori\",\n\"gamp\",\n\"gamut\",\n\"gamy\",\n\"gan\",\n\"ganam\",\n\"ganch\",\n\"gander\",\n\"gandul\",\n\"gandum\",\n\"gane\",\n\"ganef\",\n\"gang\",\n\"ganga\",\n\"gangan\",\n\"gangava\",\n\"gangdom\",\n\"gange\",\n\"ganger\",\n\"ganging\",\n\"gangism\",\n\"ganglia\",\n\"gangly\",\n\"gangman\",\n\"gangrel\",\n\"gangue\",\n\"gangway\",\n\"ganja\",\n\"ganner\",\n\"gannet\",\n\"ganoid\",\n\"ganoin\",\n\"ganosis\",\n\"gansel\",\n\"gansey\",\n\"gansy\",\n\"gant\",\n\"ganta\",\n\"gantang\",\n\"gantlet\",\n\"ganton\",\n\"gantry\",\n\"gantsl\",\n\"ganza\",\n\"ganzie\",\n\"gaol\",\n\"gaoler\",\n\"gap\",\n\"gapa\",\n\"gape\",\n\"gaper\",\n\"gapes\",\n\"gaping\",\n\"gapo\",\n\"gappy\",\n\"gapy\",\n\"gar\",\n\"gara\",\n\"garad\",\n\"garage\",\n\"garance\",\n\"garava\",\n\"garawi\",\n\"garb\",\n\"garbage\",\n\"garbel\",\n\"garbell\",\n\"garbill\",\n\"garble\",\n\"garbler\",\n\"garboil\",\n\"garbure\",\n\"garce\",\n\"gardant\",\n\"gardeen\",\n\"garden\",\n\"gardeny\",\n\"gardy\",\n\"gare\",\n\"gareh\",\n\"garetta\",\n\"garfish\",\n\"garget\",\n\"gargety\",\n\"gargle\",\n\"gargol\",\n\"garial\",\n\"gariba\",\n\"garish\",\n\"garland\",\n\"garle\",\n\"garlic\",\n\"garment\",\n\"garn\",\n\"garnel\",\n\"garner\",\n\"garnet\",\n\"garnets\",\n\"garnett\",\n\"garnetz\",\n\"garnice\",\n\"garniec\",\n\"garnish\",\n\"garoo\",\n\"garrafa\",\n\"garran\",\n\"garret\",\n\"garrot\",\n\"garrote\",\n\"garrupa\",\n\"garse\",\n\"garsil\",\n\"garston\",\n\"garten\",\n\"garter\",\n\"garth\",\n\"garum\",\n\"garvey\",\n\"garvock\",\n\"gas\",\n\"gasbag\",\n\"gaseity\",\n\"gaseous\",\n\"gash\",\n\"gashes\",\n\"gashful\",\n\"gashly\",\n\"gashy\",\n\"gasify\",\n\"gasket\",\n\"gaskin\",\n\"gasking\",\n\"gaskins\",\n\"gasless\",\n\"gaslit\",\n\"gaslock\",\n\"gasman\",\n\"gasp\",\n\"gasper\",\n\"gasping\",\n\"gaspy\",\n\"gasser\",\n\"gassing\",\n\"gassy\",\n\"gast\",\n\"gaster\",\n\"gastral\",\n\"gastric\",\n\"gastrin\",\n\"gat\",\n\"gata\",\n\"gatch\",\n\"gate\",\n\"gateado\",\n\"gateage\",\n\"gated\",\n\"gateman\",\n\"gater\",\n\"gateway\",\n\"gather\",\n\"gating\",\n\"gator\",\n\"gatter\",\n\"gau\",\n\"gaub\",\n\"gauby\",\n\"gauche\",\n\"gaud\",\n\"gaudery\",\n\"gaudful\",\n\"gaudily\",\n\"gaudy\",\n\"gaufer\",\n\"gauffer\",\n\"gauffre\",\n\"gaufre\",\n\"gauge\",\n\"gauger\",\n\"gauging\",\n\"gaulin\",\n\"gault\",\n\"gaulter\",\n\"gaum\",\n\"gaumish\",\n\"gaumy\",\n\"gaun\",\n\"gaunt\",\n\"gaunted\",\n\"gauntly\",\n\"gauntry\",\n\"gaunty\",\n\"gaup\",\n\"gaupus\",\n\"gaur\",\n\"gaus\",\n\"gauss\",\n\"gauster\",\n\"gaut\",\n\"gauze\",\n\"gauzily\",\n\"gauzy\",\n\"gavall\",\n\"gave\",\n\"gavel\",\n\"gaveler\",\n\"gavial\",\n\"gavotte\",\n\"gavyuti\",\n\"gaw\",\n\"gawby\",\n\"gawcie\",\n\"gawk\",\n\"gawkily\",\n\"gawkish\",\n\"gawky\",\n\"gawm\",\n\"gawn\",\n\"gawney\",\n\"gawsie\",\n\"gay\",\n\"gayal\",\n\"gayatri\",\n\"gaybine\",\n\"gaycat\",\n\"gayish\",\n\"gayment\",\n\"gayness\",\n\"gaysome\",\n\"gayyou\",\n\"gaz\",\n\"gazabo\",\n\"gaze\",\n\"gazebo\",\n\"gazee\",\n\"gazel\",\n\"gazelle\",\n\"gazer\",\n\"gazette\",\n\"gazi\",\n\"gazing\",\n\"gazon\",\n\"gazy\",\n\"ge\",\n\"geal\",\n\"gean\",\n\"gear\",\n\"gearbox\",\n\"geared\",\n\"gearing\",\n\"gearman\",\n\"gearset\",\n\"gease\",\n\"geason\",\n\"geat\",\n\"gebang\",\n\"gebanga\",\n\"gebbie\",\n\"gebur\",\n\"geck\",\n\"gecko\",\n\"geckoid\",\n\"ged\",\n\"gedackt\",\n\"gedder\",\n\"gedeckt\",\n\"gedrite\",\n\"gee\",\n\"geebong\",\n\"geebung\",\n\"geejee\",\n\"geek\",\n\"geelbec\",\n\"geerah\",\n\"geest\",\n\"geet\",\n\"geezer\",\n\"gegg\",\n\"geggee\",\n\"gegger\",\n\"geggery\",\n\"gein\",\n\"geira\",\n\"geisha\",\n\"geison\",\n\"geitjie\",\n\"gel\",\n\"gelable\",\n\"gelada\",\n\"gelatin\",\n\"geld\",\n\"geldant\",\n\"gelder\",\n\"gelding\",\n\"gelid\",\n\"gelidly\",\n\"gelilah\",\n\"gell\",\n\"gelly\",\n\"gelong\",\n\"gelose\",\n\"gelosin\",\n\"gelt\",\n\"gem\",\n\"gemauve\",\n\"gemel\",\n\"gemeled\",\n\"gemless\",\n\"gemlike\",\n\"gemma\",\n\"gemmae\",\n\"gemmate\",\n\"gemmer\",\n\"gemmily\",\n\"gemmoid\",\n\"gemmula\",\n\"gemmule\",\n\"gemmy\",\n\"gemot\",\n\"gemsbok\",\n\"gemul\",\n\"gemuti\",\n\"gemwork\",\n\"gen\",\n\"gena\",\n\"genal\",\n\"genapp\",\n\"genarch\",\n\"gender\",\n\"gene\",\n\"genear\",\n\"geneat\",\n\"geneki\",\n\"genep\",\n\"genera\",\n\"general\",\n\"generic\",\n\"genesic\",\n\"genesis\",\n\"genet\",\n\"genetic\",\n\"geneva\",\n\"genial\",\n\"genian\",\n\"genic\",\n\"genie\",\n\"genii\",\n\"genin\",\n\"genion\",\n\"genip\",\n\"genipa\",\n\"genipap\",\n\"genista\",\n\"genital\",\n\"genitor\",\n\"genius\",\n\"genizah\",\n\"genoese\",\n\"genom\",\n\"genome\",\n\"genomic\",\n\"genos\",\n\"genre\",\n\"genro\",\n\"gens\",\n\"genson\",\n\"gent\",\n\"genteel\",\n\"gentes\",\n\"gentian\",\n\"gentile\",\n\"gentle\",\n\"gently\",\n\"gentman\",\n\"gentry\",\n\"genty\",\n\"genu\",\n\"genua\",\n\"genual\",\n\"genuine\",\n\"genus\",\n\"genys\",\n\"geo\",\n\"geobios\",\n\"geodal\",\n\"geode\",\n\"geodesy\",\n\"geodete\",\n\"geodic\",\n\"geodist\",\n\"geoduck\",\n\"geoform\",\n\"geogeny\",\n\"geogony\",\n\"geoid\",\n\"geoidal\",\n\"geology\",\n\"geomaly\",\n\"geomant\",\n\"geomyid\",\n\"geonoma\",\n\"geopony\",\n\"georama\",\n\"georgic\",\n\"geosid\",\n\"geoside\",\n\"geotaxy\",\n\"geotic\",\n\"geoty\",\n\"ger\",\n\"gerah\",\n\"geranic\",\n\"geranyl\",\n\"gerate\",\n\"gerated\",\n\"geratic\",\n\"geraty\",\n\"gerb\",\n\"gerbe\",\n\"gerbil\",\n\"gercrow\",\n\"gerefa\",\n\"gerenda\",\n\"gerent\",\n\"gerenuk\",\n\"gerim\",\n\"gerip\",\n\"germ\",\n\"germal\",\n\"german\",\n\"germane\",\n\"germen\",\n\"germin\",\n\"germina\",\n\"germing\",\n\"germon\",\n\"germule\",\n\"germy\",\n\"gernitz\",\n\"geront\",\n\"geronto\",\n\"gers\",\n\"gersum\",\n\"gerund\",\n\"gerusia\",\n\"gervao\",\n\"gesith\",\n\"gesning\",\n\"gesso\",\n\"gest\",\n\"gestant\",\n\"gestate\",\n\"geste\",\n\"gested\",\n\"gesten\",\n\"gestic\",\n\"gestion\",\n\"gesture\",\n\"get\",\n\"geta\",\n\"getah\",\n\"getaway\",\n\"gether\",\n\"getling\",\n\"getter\",\n\"getting\",\n\"getup\",\n\"geum\",\n\"gewgaw\",\n\"gewgawy\",\n\"gey\",\n\"geyan\",\n\"geyser\",\n\"gez\",\n\"ghafir\",\n\"ghaist\",\n\"ghalva\",\n\"gharial\",\n\"gharnao\",\n\"gharry\",\n\"ghastly\",\n\"ghat\",\n\"ghatti\",\n\"ghatwal\",\n\"ghazi\",\n\"ghazism\",\n\"ghebeta\",\n\"ghee\",\n\"gheleem\",\n\"gherkin\",\n\"ghetti\",\n\"ghetto\",\n\"ghizite\",\n\"ghoom\",\n\"ghost\",\n\"ghoster\",\n\"ghostly\",\n\"ghosty\",\n\"ghoul\",\n\"ghrush\",\n\"ghurry\",\n\"giant\",\n\"giantly\",\n\"giantry\",\n\"giardia\",\n\"giarra\",\n\"giarre\",\n\"gib\",\n\"gibaro\",\n\"gibbals\",\n\"gibbed\",\n\"gibber\",\n\"gibbet\",\n\"gibbles\",\n\"gibbon\",\n\"gibbose\",\n\"gibbous\",\n\"gibbus\",\n\"gibby\",\n\"gibe\",\n\"gibel\",\n\"giber\",\n\"gibing\",\n\"gibleh\",\n\"giblet\",\n\"giblets\",\n\"gibus\",\n\"gid\",\n\"giddap\",\n\"giddea\",\n\"giddify\",\n\"giddily\",\n\"giddy\",\n\"gidgee\",\n\"gie\",\n\"gied\",\n\"gien\",\n\"gif\",\n\"gift\",\n\"gifted\",\n\"giftie\",\n\"gig\",\n\"gigback\",\n\"gigeria\",\n\"gigful\",\n\"gigger\",\n\"giggish\",\n\"giggit\",\n\"giggle\",\n\"giggler\",\n\"giggly\",\n\"giglet\",\n\"giglot\",\n\"gigman\",\n\"gignate\",\n\"gigolo\",\n\"gigot\",\n\"gigsman\",\n\"gigster\",\n\"gigtree\",\n\"gigunu\",\n\"gilbert\",\n\"gild\",\n\"gilded\",\n\"gilden\",\n\"gilder\",\n\"gilding\",\n\"gilguy\",\n\"gilia\",\n\"gilim\",\n\"gill\",\n\"gilled\",\n\"giller\",\n\"gillie\",\n\"gilling\",\n\"gilly\",\n\"gilo\",\n\"gilpy\",\n\"gilse\",\n\"gilt\",\n\"giltcup\",\n\"gim\",\n\"gimbal\",\n\"gimble\",\n\"gimel\",\n\"gimlet\",\n\"gimlety\",\n\"gimmal\",\n\"gimmer\",\n\"gimmick\",\n\"gimp\",\n\"gimped\",\n\"gimper\",\n\"gimping\",\n\"gin\",\n\"ging\",\n\"ginger\",\n\"gingery\",\n\"gingham\",\n\"gingili\",\n\"gingiva\",\n\"gink\",\n\"ginkgo\",\n\"ginned\",\n\"ginner\",\n\"ginners\",\n\"ginnery\",\n\"ginney\",\n\"ginning\",\n\"ginnle\",\n\"ginny\",\n\"ginseng\",\n\"ginward\",\n\"gio\",\n\"gip\",\n\"gipon\",\n\"gipper\",\n\"gipser\",\n\"gipsire\",\n\"giraffe\",\n\"girasol\",\n\"girba\",\n\"gird\",\n\"girder\",\n\"girding\",\n\"girdle\",\n\"girdler\",\n\"girl\",\n\"girleen\",\n\"girlery\",\n\"girlie\",\n\"girling\",\n\"girlish\",\n\"girlism\",\n\"girly\",\n\"girn\",\n\"girny\",\n\"giro\",\n\"girr\",\n\"girse\",\n\"girsh\",\n\"girsle\",\n\"girt\",\n\"girth\",\n\"gisarme\",\n\"gish\",\n\"gisla\",\n\"gisler\",\n\"gist\",\n\"git\",\n\"gitalin\",\n\"gith\",\n\"gitonin\",\n\"gitoxin\",\n\"gittern\",\n\"gittith\",\n\"give\",\n\"given\",\n\"giver\",\n\"givey\",\n\"giving\",\n\"gizz\",\n\"gizzard\",\n\"gizzen\",\n\"gizzern\",\n\"glace\",\n\"glaceed\",\n\"glacial\",\n\"glacier\",\n\"glacis\",\n\"glack\",\n\"glad\",\n\"gladden\",\n\"gladdon\",\n\"gladdy\",\n\"glade\",\n\"gladeye\",\n\"gladful\",\n\"gladify\",\n\"gladii\",\n\"gladius\",\n\"gladly\",\n\"glady\",\n\"glaga\",\n\"glaieul\",\n\"glaik\",\n\"glaiket\",\n\"glair\",\n\"glairy\",\n\"glaive\",\n\"glaived\",\n\"glaked\",\n\"glaky\",\n\"glam\",\n\"glamour\",\n\"glance\",\n\"glancer\",\n\"gland\",\n\"glandes\",\n\"glans\",\n\"glar\",\n\"glare\",\n\"glarily\",\n\"glaring\",\n\"glarry\",\n\"glary\",\n\"glashan\",\n\"glass\",\n\"glassen\",\n\"glasser\",\n\"glasses\",\n\"glassie\",\n\"glassy\",\n\"glaucin\",\n\"glaum\",\n\"glaur\",\n\"glaury\",\n\"glaver\",\n\"glaze\",\n\"glazed\",\n\"glazen\",\n\"glazer\",\n\"glazier\",\n\"glazily\",\n\"glazing\",\n\"glazy\",\n\"gleam\",\n\"gleamy\",\n\"glean\",\n\"gleaner\",\n\"gleary\",\n\"gleba\",\n\"glebal\",\n\"glebe\",\n\"glebous\",\n\"glede\",\n\"gledy\",\n\"glee\",\n\"gleed\",\n\"gleeful\",\n\"gleek\",\n\"gleeman\",\n\"gleet\",\n\"gleety\",\n\"gleg\",\n\"glegly\",\n\"glen\",\n\"glenoid\",\n\"glent\",\n\"gleyde\",\n\"glia\",\n\"gliadin\",\n\"glial\",\n\"glib\",\n\"glibly\",\n\"glidder\",\n\"glide\",\n\"glider\",\n\"gliding\",\n\"gliff\",\n\"glime\",\n\"glimmer\",\n\"glimpse\",\n\"glink\",\n\"glint\",\n\"glioma\",\n\"gliosa\",\n\"gliosis\",\n\"glirine\",\n\"glisk\",\n\"glisky\",\n\"glisten\",\n\"glister\",\n\"glitter\",\n\"gloam\",\n\"gloat\",\n\"gloater\",\n\"global\",\n\"globate\",\n\"globe\",\n\"globed\",\n\"globin\",\n\"globoid\",\n\"globose\",\n\"globous\",\n\"globule\",\n\"globy\",\n\"glochid\",\n\"glochis\",\n\"gloea\",\n\"gloeal\",\n\"glom\",\n\"glome\",\n\"glommox\",\n\"glomus\",\n\"glonoin\",\n\"gloom\",\n\"gloomth\",\n\"gloomy\",\n\"glop\",\n\"gloppen\",\n\"glor\",\n\"glore\",\n\"glorify\",\n\"glory\",\n\"gloss\",\n\"glossa\",\n\"glossal\",\n\"glossed\",\n\"glosser\",\n\"glossic\",\n\"glossy\",\n\"glost\",\n\"glottal\",\n\"glottic\",\n\"glottid\",\n\"glottis\",\n\"glout\",\n\"glove\",\n\"glover\",\n\"glovey\",\n\"gloving\",\n\"glow\",\n\"glower\",\n\"glowfly\",\n\"glowing\",\n\"gloy\",\n\"gloze\",\n\"glozing\",\n\"glub\",\n\"glucase\",\n\"glucid\",\n\"glucide\",\n\"glucina\",\n\"glucine\",\n\"gluck\",\n\"glucose\",\n\"glue\",\n\"glued\",\n\"gluepot\",\n\"gluer\",\n\"gluey\",\n\"glug\",\n\"gluish\",\n\"glum\",\n\"gluma\",\n\"glumal\",\n\"glume\",\n\"glumly\",\n\"glummy\",\n\"glumose\",\n\"glump\",\n\"glumpy\",\n\"glunch\",\n\"glusid\",\n\"gluside\",\n\"glut\",\n\"glutch\",\n\"gluteal\",\n\"gluten\",\n\"gluteus\",\n\"glutin\",\n\"glutoid\",\n\"glutose\",\n\"glutter\",\n\"glutton\",\n\"glycid\",\n\"glycide\",\n\"glycine\",\n\"glycol\",\n\"glycose\",\n\"glycyl\",\n\"glyoxal\",\n\"glyoxim\",\n\"glyoxyl\",\n\"glyph\",\n\"glyphic\",\n\"glyptic\",\n\"glyster\",\n\"gnabble\",\n\"gnar\",\n\"gnarl\",\n\"gnarled\",\n\"gnarly\",\n\"gnash\",\n\"gnat\",\n\"gnathal\",\n\"gnathic\",\n\"gnatter\",\n\"gnatty\",\n\"gnaw\",\n\"gnawer\",\n\"gnawing\",\n\"gnawn\",\n\"gneiss\",\n\"gneissy\",\n\"gnome\",\n\"gnomed\",\n\"gnomic\",\n\"gnomide\",\n\"gnomish\",\n\"gnomist\",\n\"gnomon\",\n\"gnosis\",\n\"gnostic\",\n\"gnu\",\n\"go\",\n\"goa\",\n\"goad\",\n\"goaf\",\n\"goal\",\n\"goalage\",\n\"goalee\",\n\"goalie\",\n\"goanna\",\n\"goat\",\n\"goatee\",\n\"goateed\",\n\"goatish\",\n\"goatly\",\n\"goaty\",\n\"goave\",\n\"gob\",\n\"goback\",\n\"goban\",\n\"gobang\",\n\"gobbe\",\n\"gobber\",\n\"gobbet\",\n\"gobbin\",\n\"gobbing\",\n\"gobble\",\n\"gobbler\",\n\"gobby\",\n\"gobelin\",\n\"gobi\",\n\"gobiid\",\n\"gobioid\",\n\"goblet\",\n\"goblin\",\n\"gobline\",\n\"gobo\",\n\"gobony\",\n\"goburra\",\n\"goby\",\n\"gocart\",\n\"god\",\n\"goddard\",\n\"godded\",\n\"goddess\",\n\"goddize\",\n\"gode\",\n\"godet\",\n\"godhead\",\n\"godhood\",\n\"godkin\",\n\"godless\",\n\"godlet\",\n\"godlike\",\n\"godlily\",\n\"godling\",\n\"godly\",\n\"godown\",\n\"godpapa\",\n\"godsend\",\n\"godship\",\n\"godson\",\n\"godwit\",\n\"goeduck\",\n\"goel\",\n\"goelism\",\n\"goer\",\n\"goes\",\n\"goetia\",\n\"goetic\",\n\"goety\",\n\"goff\",\n\"goffer\",\n\"goffle\",\n\"gog\",\n\"gogga\",\n\"goggan\",\n\"goggle\",\n\"goggled\",\n\"goggler\",\n\"goggly\",\n\"goglet\",\n\"gogo\",\n\"goi\",\n\"going\",\n\"goitcho\",\n\"goiter\",\n\"goitral\",\n\"gol\",\n\"gola\",\n\"golach\",\n\"goladar\",\n\"gold\",\n\"goldbug\",\n\"goldcup\",\n\"golden\",\n\"golder\",\n\"goldie\",\n\"goldin\",\n\"goldish\",\n\"goldtit\",\n\"goldy\",\n\"golee\",\n\"golem\",\n\"golf\",\n\"golfdom\",\n\"golfer\",\n\"goli\",\n\"goliard\",\n\"goliath\",\n\"golland\",\n\"gollar\",\n\"golly\",\n\"goloe\",\n\"golpe\",\n\"gomari\",\n\"gomart\",\n\"gomavel\",\n\"gombay\",\n\"gombeen\",\n\"gomer\",\n\"gomeral\",\n\"gomlah\",\n\"gomuti\",\n\"gon\",\n\"gonad\",\n\"gonadal\",\n\"gonadic\",\n\"gonagra\",\n\"gonakie\",\n\"gonal\",\n\"gonapod\",\n\"gondang\",\n\"gondite\",\n\"gondola\",\n\"gone\",\n\"goner\",\n\"gong\",\n\"gongman\",\n\"gonia\",\n\"goniac\",\n\"gonial\",\n\"goniale\",\n\"gonid\",\n\"gonidia\",\n\"gonidic\",\n\"gonimic\",\n\"gonion\",\n\"gonitis\",\n\"gonium\",\n\"gonne\",\n\"gony\",\n\"gonys\",\n\"goo\",\n\"goober\",\n\"good\",\n\"gooding\",\n\"goodish\",\n\"goodly\",\n\"goodman\",\n\"goods\",\n\"goody\",\n\"goof\",\n\"goofer\",\n\"goofily\",\n\"goofy\",\n\"googly\",\n\"googol\",\n\"googul\",\n\"gook\",\n\"gool\",\n\"goolah\",\n\"gools\",\n\"gooma\",\n\"goon\",\n\"goondie\",\n\"goonie\",\n\"goose\",\n\"goosery\",\n\"goosish\",\n\"goosy\",\n\"gopher\",\n\"gopura\",\n\"gor\",\n\"gora\",\n\"goracco\",\n\"goral\",\n\"goran\",\n\"gorb\",\n\"gorbal\",\n\"gorbet\",\n\"gorble\",\n\"gorce\",\n\"gorcock\",\n\"gorcrow\",\n\"gore\",\n\"gorer\",\n\"gorevan\",\n\"gorfly\",\n\"gorge\",\n\"gorged\",\n\"gorger\",\n\"gorget\",\n\"gorglin\",\n\"gorhen\",\n\"goric\",\n\"gorilla\",\n\"gorily\",\n\"goring\",\n\"gorlin\",\n\"gorlois\",\n\"gormaw\",\n\"gormed\",\n\"gorra\",\n\"gorraf\",\n\"gorry\",\n\"gorse\",\n\"gorsedd\",\n\"gorsy\",\n\"gory\",\n\"gos\",\n\"gosain\",\n\"goschen\",\n\"gosh\",\n\"goshawk\",\n\"goslet\",\n\"gosling\",\n\"gosmore\",\n\"gospel\",\n\"gosport\",\n\"gossan\",\n\"gossard\",\n\"gossip\",\n\"gossipy\",\n\"gossoon\",\n\"gossy\",\n\"got\",\n\"gotch\",\n\"gote\",\n\"gothite\",\n\"gotra\",\n\"gotraja\",\n\"gotten\",\n\"gouaree\",\n\"gouge\",\n\"gouger\",\n\"goujon\",\n\"goulash\",\n\"goumi\",\n\"goup\",\n\"gourami\",\n\"gourd\",\n\"gourde\",\n\"gourdy\",\n\"gourmet\",\n\"gousty\",\n\"gout\",\n\"goutify\",\n\"goutily\",\n\"goutish\",\n\"goutte\",\n\"gouty\",\n\"gove\",\n\"govern\",\n\"gowan\",\n\"gowdnie\",\n\"gowf\",\n\"gowfer\",\n\"gowk\",\n\"gowked\",\n\"gowkit\",\n\"gowl\",\n\"gown\",\n\"gownlet\",\n\"gowpen\",\n\"goy\",\n\"goyim\",\n\"goyin\",\n\"goyle\",\n\"gozell\",\n\"gozzard\",\n\"gra\",\n\"grab\",\n\"grabber\",\n\"grabble\",\n\"graben\",\n\"grace\",\n\"gracer\",\n\"gracile\",\n\"grackle\",\n\"grad\",\n\"gradal\",\n\"gradate\",\n\"graddan\",\n\"grade\",\n\"graded\",\n\"gradely\",\n\"grader\",\n\"gradin\",\n\"gradine\",\n\"grading\",\n\"gradual\",\n\"gradus\",\n\"graff\",\n\"graffer\",\n\"graft\",\n\"grafted\",\n\"grafter\",\n\"graham\",\n\"grail\",\n\"grailer\",\n\"grain\",\n\"grained\",\n\"grainer\",\n\"grainy\",\n\"graip\",\n\"graisse\",\n\"graith\",\n\"grallic\",\n\"gram\",\n\"grama\",\n\"grame\",\n\"grammar\",\n\"gramme\",\n\"gramp\",\n\"grampa\",\n\"grampus\",\n\"granada\",\n\"granage\",\n\"granary\",\n\"granate\",\n\"granch\",\n\"grand\",\n\"grandam\",\n\"grandee\",\n\"grandly\",\n\"grandma\",\n\"grandpa\",\n\"grane\",\n\"grange\",\n\"granger\",\n\"granite\",\n\"grank\",\n\"grannom\",\n\"granny\",\n\"grano\",\n\"granose\",\n\"grant\",\n\"grantee\",\n\"granter\",\n\"grantor\",\n\"granula\",\n\"granule\",\n\"granza\",\n\"grape\",\n\"graped\",\n\"grapery\",\n\"graph\",\n\"graphic\",\n\"graphy\",\n\"graping\",\n\"grapnel\",\n\"grappa\",\n\"grapple\",\n\"grapy\",\n\"grasp\",\n\"grasper\",\n\"grass\",\n\"grassed\",\n\"grasser\",\n\"grasset\",\n\"grassy\",\n\"grat\",\n\"grate\",\n\"grater\",\n\"grather\",\n\"gratify\",\n\"grating\",\n\"gratis\",\n\"gratten\",\n\"graupel\",\n\"grave\",\n\"graved\",\n\"gravel\",\n\"gravely\",\n\"graven\",\n\"graver\",\n\"gravic\",\n\"gravid\",\n\"graving\",\n\"gravity\",\n\"gravure\",\n\"gravy\",\n\"grawls\",\n\"gray\",\n\"grayfly\",\n\"grayish\",\n\"graylag\",\n\"grayly\",\n\"graze\",\n\"grazer\",\n\"grazier\",\n\"grazing\",\n\"grease\",\n\"greaser\",\n\"greasy\",\n\"great\",\n\"greaten\",\n\"greater\",\n\"greatly\",\n\"greave\",\n\"greaved\",\n\"greaves\",\n\"grebe\",\n\"grece\",\n\"gree\",\n\"greed\",\n\"greedy\",\n\"green\",\n\"greener\",\n\"greeney\",\n\"greenly\",\n\"greenth\",\n\"greenuk\",\n\"greeny\",\n\"greet\",\n\"greeter\",\n\"gregal\",\n\"gregale\",\n\"grege\",\n\"greggle\",\n\"grego\",\n\"greige\",\n\"grein\",\n\"greisen\",\n\"gremial\",\n\"gremlin\",\n\"grenade\",\n\"greund\",\n\"grew\",\n\"grey\",\n\"greyly\",\n\"gribble\",\n\"grice\",\n\"grid\",\n\"griddle\",\n\"gride\",\n\"griece\",\n\"grieced\",\n\"grief\",\n\"grieve\",\n\"grieved\",\n\"griever\",\n\"griff\",\n\"griffe\",\n\"griffin\",\n\"griffon\",\n\"grift\",\n\"grifter\",\n\"grig\",\n\"grignet\",\n\"grigri\",\n\"grike\",\n\"grill\",\n\"grille\",\n\"grilled\",\n\"griller\",\n\"grilse\",\n\"grim\",\n\"grimace\",\n\"grime\",\n\"grimful\",\n\"grimily\",\n\"grimly\",\n\"grimme\",\n\"grimp\",\n\"grimy\",\n\"grin\",\n\"grinch\",\n\"grind\",\n\"grinder\",\n\"grindle\",\n\"gringo\",\n\"grinner\",\n\"grinny\",\n\"grip\",\n\"gripe\",\n\"griper\",\n\"griping\",\n\"gripman\",\n\"grippal\",\n\"grippe\",\n\"gripper\",\n\"gripple\",\n\"grippy\",\n\"gripy\",\n\"gris\",\n\"grisard\",\n\"griskin\",\n\"grisly\",\n\"grison\",\n\"grist\",\n\"grister\",\n\"gristle\",\n\"gristly\",\n\"gristy\",\n\"grit\",\n\"grith\",\n\"grits\",\n\"gritten\",\n\"gritter\",\n\"grittle\",\n\"gritty\",\n\"grivet\",\n\"grivna\",\n\"grizzle\",\n\"grizzly\",\n\"groan\",\n\"groaner\",\n\"groat\",\n\"groats\",\n\"grobian\",\n\"grocer\",\n\"grocery\",\n\"groff\",\n\"grog\",\n\"groggy\",\n\"grogram\",\n\"groin\",\n\"groined\",\n\"grommet\",\n\"groom\",\n\"groomer\",\n\"groomy\",\n\"groop\",\n\"groose\",\n\"groot\",\n\"grooty\",\n\"groove\",\n\"groover\",\n\"groovy\",\n\"grope\",\n\"groper\",\n\"groping\",\n\"gropple\",\n\"gros\",\n\"groser\",\n\"groset\",\n\"gross\",\n\"grossen\",\n\"grosser\",\n\"grossly\",\n\"grosso\",\n\"grosz\",\n\"groszy\",\n\"grot\",\n\"grotto\",\n\"grouch\",\n\"grouchy\",\n\"grouf\",\n\"grough\",\n\"ground\",\n\"grounds\",\n\"groundy\",\n\"group\",\n\"grouped\",\n\"grouper\",\n\"grouse\",\n\"grouser\",\n\"grousy\",\n\"grout\",\n\"grouter\",\n\"grouts\",\n\"grouty\",\n\"grouze\",\n\"grove\",\n\"groved\",\n\"grovel\",\n\"grovy\",\n\"grow\",\n\"growan\",\n\"growed\",\n\"grower\",\n\"growing\",\n\"growl\",\n\"growler\",\n\"growly\",\n\"grown\",\n\"grownup\",\n\"growse\",\n\"growth\",\n\"growthy\",\n\"grozart\",\n\"grozet\",\n\"grr\",\n\"grub\",\n\"grubbed\",\n\"grubber\",\n\"grubby\",\n\"grubs\",\n\"grudge\",\n\"grudger\",\n\"grue\",\n\"gruel\",\n\"grueler\",\n\"gruelly\",\n\"gruff\",\n\"gruffly\",\n\"gruffs\",\n\"gruffy\",\n\"grufted\",\n\"grugru\",\n\"gruine\",\n\"grum\",\n\"grumble\",\n\"grumbly\",\n\"grume\",\n\"grumly\",\n\"grummel\",\n\"grummet\",\n\"grumose\",\n\"grumous\",\n\"grump\",\n\"grumph\",\n\"grumphy\",\n\"grumpy\",\n\"grun\",\n\"grundy\",\n\"grunion\",\n\"grunt\",\n\"grunter\",\n\"gruntle\",\n\"grush\",\n\"grushie\",\n\"gruss\",\n\"grutch\",\n\"grutten\",\n\"gryde\",\n\"grylli\",\n\"gryllid\",\n\"gryllos\",\n\"gryllus\",\n\"grysbok\",\n\"guaba\",\n\"guacimo\",\n\"guacin\",\n\"guaco\",\n\"guaiac\",\n\"guaiol\",\n\"guaka\",\n\"guama\",\n\"guan\",\n\"guana\",\n\"guanaco\",\n\"guanase\",\n\"guanay\",\n\"guango\",\n\"guanine\",\n\"guanize\",\n\"guano\",\n\"guanyl\",\n\"guao\",\n\"guapena\",\n\"guar\",\n\"guara\",\n\"guarabu\",\n\"guarana\",\n\"guarani\",\n\"guard\",\n\"guarded\",\n\"guarder\",\n\"guardo\",\n\"guariba\",\n\"guarri\",\n\"guasa\",\n\"guava\",\n\"guavina\",\n\"guayaba\",\n\"guayabi\",\n\"guayabo\",\n\"guayule\",\n\"guaza\",\n\"gubbo\",\n\"gucki\",\n\"gud\",\n\"gudame\",\n\"guddle\",\n\"gude\",\n\"gudge\",\n\"gudgeon\",\n\"gudget\",\n\"gudok\",\n\"gue\",\n\"guebucu\",\n\"guemal\",\n\"guenepe\",\n\"guenon\",\n\"guepard\",\n\"guerdon\",\n\"guereza\",\n\"guess\",\n\"guesser\",\n\"guest\",\n\"guesten\",\n\"guester\",\n\"gufa\",\n\"guff\",\n\"guffaw\",\n\"guffer\",\n\"guffin\",\n\"guffy\",\n\"gugal\",\n\"guggle\",\n\"gugglet\",\n\"guglet\",\n\"guglia\",\n\"guglio\",\n\"gugu\",\n\"guhr\",\n\"guib\",\n\"guiba\",\n\"guidage\",\n\"guide\",\n\"guider\",\n\"guidman\",\n\"guidon\",\n\"guige\",\n\"guignol\",\n\"guijo\",\n\"guild\",\n\"guilder\",\n\"guildic\",\n\"guildry\",\n\"guile\",\n\"guilery\",\n\"guilt\",\n\"guilty\",\n\"guily\",\n\"guimpe\",\n\"guinea\",\n\"guipure\",\n\"guisard\",\n\"guise\",\n\"guiser\",\n\"guising\",\n\"guitar\",\n\"gul\",\n\"gula\",\n\"gulae\",\n\"gulaman\",\n\"gular\",\n\"gularis\",\n\"gulch\",\n\"gulden\",\n\"gule\",\n\"gules\",\n\"gulf\",\n\"gulfy\",\n\"gulgul\",\n\"gulix\",\n\"gull\",\n\"gullery\",\n\"gullet\",\n\"gullion\",\n\"gullish\",\n\"gully\",\n\"gulonic\",\n\"gulose\",\n\"gulp\",\n\"gulper\",\n\"gulpin\",\n\"gulping\",\n\"gulpy\",\n\"gulsach\",\n\"gum\",\n\"gumbo\",\n\"gumboil\",\n\"gumby\",\n\"gumdrop\",\n\"gumihan\",\n\"gumless\",\n\"gumlike\",\n\"gumly\",\n\"gumma\",\n\"gummage\",\n\"gummata\",\n\"gummed\",\n\"gummer\",\n\"gumming\",\n\"gummite\",\n\"gummose\",\n\"gummous\",\n\"gummy\",\n\"gump\",\n\"gumpus\",\n\"gumshoe\",\n\"gumweed\",\n\"gumwood\",\n\"gun\",\n\"guna\",\n\"gunate\",\n\"gunboat\",\n\"gundi\",\n\"gundy\",\n\"gunebo\",\n\"gunfire\",\n\"gunge\",\n\"gunite\",\n\"gunj\",\n\"gunk\",\n\"gunl\",\n\"gunless\",\n\"gunlock\",\n\"gunman\",\n\"gunnage\",\n\"gunne\",\n\"gunnel\",\n\"gunner\",\n\"gunnery\",\n\"gunnies\",\n\"gunning\",\n\"gunnung\",\n\"gunny\",\n\"gunong\",\n\"gunplay\",\n\"gunrack\",\n\"gunsel\",\n\"gunshop\",\n\"gunshot\",\n\"gunsman\",\n\"gunster\",\n\"gunter\",\n\"gunwale\",\n\"gunyah\",\n\"gunyang\",\n\"gunyeh\",\n\"gup\",\n\"guppy\",\n\"gur\",\n\"gurdle\",\n\"gurge\",\n\"gurgeon\",\n\"gurges\",\n\"gurgle\",\n\"gurglet\",\n\"gurgly\",\n\"gurjun\",\n\"gurk\",\n\"gurl\",\n\"gurly\",\n\"gurnard\",\n\"gurnet\",\n\"gurniad\",\n\"gurr\",\n\"gurrah\",\n\"gurry\",\n\"gurt\",\n\"guru\",\n\"gush\",\n\"gusher\",\n\"gushet\",\n\"gushily\",\n\"gushing\",\n\"gushy\",\n\"gusla\",\n\"gusle\",\n\"guss\",\n\"gusset\",\n\"gussie\",\n\"gust\",\n\"gustful\",\n\"gustily\",\n\"gusto\",\n\"gusty\",\n\"gut\",\n\"gutless\",\n\"gutlike\",\n\"gutling\",\n\"gutt\",\n\"gutta\",\n\"guttate\",\n\"gutte\",\n\"gutter\",\n\"guttery\",\n\"gutti\",\n\"guttide\",\n\"guttie\",\n\"guttle\",\n\"guttler\",\n\"guttula\",\n\"guttule\",\n\"guttus\",\n\"gutty\",\n\"gutweed\",\n\"gutwise\",\n\"gutwort\",\n\"guy\",\n\"guydom\",\n\"guyer\",\n\"guz\",\n\"guze\",\n\"guzzle\",\n\"guzzler\",\n\"gwag\",\n\"gweduc\",\n\"gweed\",\n\"gweeon\",\n\"gwely\",\n\"gwine\",\n\"gwyniad\",\n\"gyle\",\n\"gym\",\n\"gymel\",\n\"gymnast\",\n\"gymnic\",\n\"gymnics\",\n\"gymnite\",\n\"gymnure\",\n\"gympie\",\n\"gyn\",\n\"gyne\",\n\"gynecic\",\n\"gynic\",\n\"gynics\",\n\"gyp\",\n\"gype\",\n\"gypper\",\n\"gyps\",\n\"gypsine\",\n\"gypsite\",\n\"gypsous\",\n\"gypster\",\n\"gypsum\",\n\"gypsy\",\n\"gypsyfy\",\n\"gypsyry\",\n\"gyral\",\n\"gyrally\",\n\"gyrant\",\n\"gyrate\",\n\"gyrator\",\n\"gyre\",\n\"gyrene\",\n\"gyri\",\n\"gyric\",\n\"gyrinid\",\n\"gyro\",\n\"gyrocar\",\n\"gyroma\",\n\"gyron\",\n\"gyronny\",\n\"gyrose\",\n\"gyrous\",\n\"gyrus\",\n\"gyte\",\n\"gytling\",\n\"gyve\",\n\"h\",\n\"ha\",\n\"haab\",\n\"haaf\",\n\"habble\",\n\"habeas\",\n\"habena\",\n\"habenal\",\n\"habenar\",\n\"habile\",\n\"habille\",\n\"habit\",\n\"habitan\",\n\"habitat\",\n\"habited\",\n\"habitue\",\n\"habitus\",\n\"habnab\",\n\"haboob\",\n\"habu\",\n\"habutai\",\n\"hache\",\n\"hachure\",\n\"hack\",\n\"hackbut\",\n\"hacked\",\n\"hackee\",\n\"hacker\",\n\"hackery\",\n\"hackin\",\n\"hacking\",\n\"hackle\",\n\"hackler\",\n\"hacklog\",\n\"hackly\",\n\"hackman\",\n\"hackney\",\n\"hacksaw\",\n\"hacky\",\n\"had\",\n\"hadbot\",\n\"hadden\",\n\"haddie\",\n\"haddo\",\n\"haddock\",\n\"hade\",\n\"hading\",\n\"hadj\",\n\"hadji\",\n\"hadland\",\n\"hadrome\",\n\"haec\",\n\"haem\",\n\"haemony\",\n\"haet\",\n\"haff\",\n\"haffet\",\n\"haffle\",\n\"hafiz\",\n\"hafnium\",\n\"hafnyl\",\n\"haft\",\n\"hafter\",\n\"hag\",\n\"hagboat\",\n\"hagborn\",\n\"hagbush\",\n\"hagdon\",\n\"hageen\",\n\"hagfish\",\n\"haggada\",\n\"haggard\",\n\"hagged\",\n\"hagger\",\n\"haggis\",\n\"haggish\",\n\"haggle\",\n\"haggler\",\n\"haggly\",\n\"haggy\",\n\"hagi\",\n\"hagia\",\n\"haglet\",\n\"haglike\",\n\"haglin\",\n\"hagride\",\n\"hagrope\",\n\"hagseed\",\n\"hagship\",\n\"hagweed\",\n\"hagworm\",\n\"hah\",\n\"haik\",\n\"haikai\",\n\"haikal\",\n\"haikwan\",\n\"hail\",\n\"hailer\",\n\"hailse\",\n\"haily\",\n\"hain\",\n\"haine\",\n\"hair\",\n\"haircut\",\n\"hairdo\",\n\"haire\",\n\"haired\",\n\"hairen\",\n\"hairif\",\n\"hairlet\",\n\"hairpin\",\n\"hairup\",\n\"hairy\",\n\"haje\",\n\"hajib\",\n\"hajilij\",\n\"hak\",\n\"hakam\",\n\"hakdar\",\n\"hake\",\n\"hakeem\",\n\"hakim\",\n\"hako\",\n\"haku\",\n\"hala\",\n\"halakah\",\n\"halakic\",\n\"halal\",\n\"halberd\",\n\"halbert\",\n\"halch\",\n\"halcyon\",\n\"hale\",\n\"halebi\",\n\"haler\",\n\"halerz\",\n\"half\",\n\"halfer\",\n\"halfman\",\n\"halfway\",\n\"halibiu\",\n\"halibut\",\n\"halide\",\n\"halidom\",\n\"halite\",\n\"halitus\",\n\"hall\",\n\"hallage\",\n\"hallah\",\n\"hallan\",\n\"hallel\",\n\"hallex\",\n\"halling\",\n\"hallman\",\n\"halloo\",\n\"hallow\",\n\"hallux\",\n\"hallway\",\n\"halma\",\n\"halo\",\n\"halogen\",\n\"haloid\",\n\"hals\",\n\"halse\",\n\"halsen\",\n\"halt\",\n\"halter\",\n\"halting\",\n\"halurgy\",\n\"halutz\",\n\"halvans\",\n\"halve\",\n\"halved\",\n\"halver\",\n\"halves\",\n\"halyard\",\n\"ham\",\n\"hamal\",\n\"hamald\",\n\"hamate\",\n\"hamated\",\n\"hamatum\",\n\"hamble\",\n\"hame\",\n\"hameil\",\n\"hamel\",\n\"hamfat\",\n\"hami\",\n\"hamlah\",\n\"hamlet\",\n\"hammada\",\n\"hammam\",\n\"hammer\",\n\"hammock\",\n\"hammy\",\n\"hamose\",\n\"hamous\",\n\"hamper\",\n\"hamsa\",\n\"hamster\",\n\"hamular\",\n\"hamule\",\n\"hamulus\",\n\"hamus\",\n\"hamza\",\n\"han\",\n\"hanaper\",\n\"hanbury\",\n\"hance\",\n\"hanced\",\n\"hanch\",\n\"hand\",\n\"handbag\",\n\"handbow\",\n\"handcar\",\n\"handed\",\n\"hander\",\n\"handful\",\n\"handgun\",\n\"handily\",\n\"handle\",\n\"handled\",\n\"handler\",\n\"handout\",\n\"handsaw\",\n\"handsel\",\n\"handset\",\n\"handy\",\n\"hangar\",\n\"hangby\",\n\"hangdog\",\n\"hange\",\n\"hangee\",\n\"hanger\",\n\"hangie\",\n\"hanging\",\n\"hangle\",\n\"hangman\",\n\"hangout\",\n\"hangul\",\n\"hanif\",\n\"hank\",\n\"hanker\",\n\"hankie\",\n\"hankle\",\n\"hanky\",\n\"hanna\",\n\"hansa\",\n\"hanse\",\n\"hansel\",\n\"hansom\",\n\"hant\",\n\"hantle\",\n\"hao\",\n\"haole\",\n\"haoma\",\n\"haori\",\n\"hap\",\n\"hapless\",\n\"haplite\",\n\"haploid\",\n\"haploma\",\n\"haplont\",\n\"haply\",\n\"happen\",\n\"happier\",\n\"happify\",\n\"happily\",\n\"happing\",\n\"happy\",\n\"hapten\",\n\"haptene\",\n\"haptere\",\n\"haptic\",\n\"haptics\",\n\"hapu\",\n\"hapuku\",\n\"harass\",\n\"haratch\",\n\"harbi\",\n\"harbor\",\n\"hard\",\n\"harden\",\n\"harder\",\n\"hardily\",\n\"hardim\",\n\"hardish\",\n\"hardly\",\n\"hardock\",\n\"hardpan\",\n\"hardy\",\n\"hare\",\n\"harebur\",\n\"harelip\",\n\"harem\",\n\"harfang\",\n\"haricot\",\n\"harish\",\n\"hark\",\n\"harka\",\n\"harl\",\n\"harling\",\n\"harlock\",\n\"harlot\",\n\"harm\",\n\"harmal\",\n\"harmala\",\n\"harman\",\n\"harmel\",\n\"harmer\",\n\"harmful\",\n\"harmine\",\n\"harmony\",\n\"harmost\",\n\"harn\",\n\"harness\",\n\"harnpan\",\n\"harp\",\n\"harpago\",\n\"harper\",\n\"harpier\",\n\"harpist\",\n\"harpoon\",\n\"harpula\",\n\"harr\",\n\"harrier\",\n\"harrow\",\n\"harry\",\n\"harsh\",\n\"harshen\",\n\"harshly\",\n\"hart\",\n\"hartal\",\n\"hartin\",\n\"hartite\",\n\"harvest\",\n\"hasan\",\n\"hash\",\n\"hashab\",\n\"hasher\",\n\"hashish\",\n\"hashy\",\n\"hask\",\n\"hasky\",\n\"haslet\",\n\"haslock\",\n\"hasp\",\n\"hassar\",\n\"hassel\",\n\"hassle\",\n\"hassock\",\n\"hasta\",\n\"hastate\",\n\"hastati\",\n\"haste\",\n\"hasten\",\n\"haster\",\n\"hastily\",\n\"hastish\",\n\"hastler\",\n\"hasty\",\n\"hat\",\n\"hatable\",\n\"hatband\",\n\"hatbox\",\n\"hatbrim\",\n\"hatch\",\n\"hatchel\",\n\"hatcher\",\n\"hatchet\",\n\"hate\",\n\"hateful\",\n\"hater\",\n\"hatful\",\n\"hath\",\n\"hathi\",\n\"hatless\",\n\"hatlike\",\n\"hatpin\",\n\"hatrack\",\n\"hatrail\",\n\"hatred\",\n\"hatress\",\n\"hatt\",\n\"hatted\",\n\"hatter\",\n\"hattery\",\n\"hatting\",\n\"hattock\",\n\"hatty\",\n\"hau\",\n\"hauberk\",\n\"haugh\",\n\"haught\",\n\"haughty\",\n\"haul\",\n\"haulage\",\n\"hauld\",\n\"hauler\",\n\"haulier\",\n\"haulm\",\n\"haulmy\",\n\"haunch\",\n\"haunchy\",\n\"haunt\",\n\"haunter\",\n\"haunty\",\n\"hause\",\n\"hausen\",\n\"hausse\",\n\"hautboy\",\n\"hauteur\",\n\"havage\",\n\"have\",\n\"haveage\",\n\"havel\",\n\"haven\",\n\"havener\",\n\"havenet\",\n\"havent\",\n\"haver\",\n\"haverel\",\n\"haverer\",\n\"havers\",\n\"havier\",\n\"havoc\",\n\"haw\",\n\"hawbuck\",\n\"hawer\",\n\"hawk\",\n\"hawkbit\",\n\"hawked\",\n\"hawker\",\n\"hawkery\",\n\"hawkie\",\n\"hawking\",\n\"hawkish\",\n\"hawknut\",\n\"hawky\",\n\"hawm\",\n\"hawok\",\n\"hawse\",\n\"hawser\",\n\"hay\",\n\"haya\",\n\"hayband\",\n\"haybird\",\n\"haybote\",\n\"haycap\",\n\"haycart\",\n\"haycock\",\n\"hayey\",\n\"hayfork\",\n\"haylift\",\n\"hayloft\",\n\"haymow\",\n\"hayrack\",\n\"hayrake\",\n\"hayrick\",\n\"hayseed\",\n\"haysel\",\n\"haysuck\",\n\"haytime\",\n\"hayward\",\n\"hayweed\",\n\"haywire\",\n\"hayz\",\n\"hazard\",\n\"haze\",\n\"hazel\",\n\"hazeled\",\n\"hazelly\",\n\"hazen\",\n\"hazer\",\n\"hazily\",\n\"hazing\",\n\"hazle\",\n\"hazy\",\n\"hazzan\",\n\"he\",\n\"head\",\n\"headcap\",\n\"headed\",\n\"header\",\n\"headful\",\n\"headily\",\n\"heading\",\n\"headman\",\n\"headset\",\n\"headway\",\n\"heady\",\n\"heaf\",\n\"heal\",\n\"heald\",\n\"healder\",\n\"healer\",\n\"healful\",\n\"healing\",\n\"health\",\n\"healthy\",\n\"heap\",\n\"heaper\",\n\"heaps\",\n\"heapy\",\n\"hear\",\n\"hearer\",\n\"hearing\",\n\"hearken\",\n\"hearsay\",\n\"hearse\",\n\"hearst\",\n\"heart\",\n\"hearted\",\n\"hearten\",\n\"hearth\",\n\"heartly\",\n\"hearts\",\n\"hearty\",\n\"heat\",\n\"heater\",\n\"heatful\",\n\"heath\",\n\"heathen\",\n\"heather\",\n\"heathy\",\n\"heating\",\n\"heaume\",\n\"heaumer\",\n\"heave\",\n\"heaven\",\n\"heavens\",\n\"heaver\",\n\"heavies\",\n\"heavily\",\n\"heaving\",\n\"heavity\",\n\"heavy\",\n\"hebamic\",\n\"hebenon\",\n\"hebete\",\n\"hebetic\",\n\"hech\",\n\"heck\",\n\"heckle\",\n\"heckler\",\n\"hectare\",\n\"hecte\",\n\"hectic\",\n\"hector\",\n\"heddle\",\n\"heddler\",\n\"hedebo\",\n\"heder\",\n\"hederic\",\n\"hederin\",\n\"hedge\",\n\"hedger\",\n\"hedging\",\n\"hedgy\",\n\"hedonic\",\n\"heed\",\n\"heeder\",\n\"heedful\",\n\"heedily\",\n\"heedy\",\n\"heehaw\",\n\"heel\",\n\"heelcap\",\n\"heeled\",\n\"heeler\",\n\"heeltap\",\n\"heer\",\n\"heeze\",\n\"heezie\",\n\"heezy\",\n\"heft\",\n\"hefter\",\n\"heftily\",\n\"hefty\",\n\"hegari\",\n\"hegemon\",\n\"hegira\",\n\"hegumen\",\n\"hei\",\n\"heiau\",\n\"heifer\",\n\"heigh\",\n\"height\",\n\"heii\",\n\"heimin\",\n\"heinous\",\n\"heir\",\n\"heirdom\",\n\"heiress\",\n\"heitiki\",\n\"hekteus\",\n\"helbeh\",\n\"helcoid\",\n\"helder\",\n\"hele\",\n\"helenin\",\n\"heliast\",\n\"helical\",\n\"heliced\",\n\"helices\",\n\"helicin\",\n\"helicon\",\n\"helide\",\n\"heling\",\n\"helio\",\n\"helioid\",\n\"helium\",\n\"helix\",\n\"hell\",\n\"hellbox\",\n\"hellcat\",\n\"helldog\",\n\"heller\",\n\"helleri\",\n\"hellhag\",\n\"hellier\",\n\"hellion\",\n\"hellish\",\n\"hello\",\n\"helluo\",\n\"helly\",\n\"helm\",\n\"helmage\",\n\"helmed\",\n\"helmet\",\n\"helodes\",\n\"heloe\",\n\"heloma\",\n\"helonin\",\n\"helosis\",\n\"helotry\",\n\"help\",\n\"helper\",\n\"helpful\",\n\"helping\",\n\"helply\",\n\"helve\",\n\"helvell\",\n\"helver\",\n\"helvite\",\n\"hem\",\n\"hemad\",\n\"hemal\",\n\"hemapod\",\n\"hemase\",\n\"hematal\",\n\"hematic\",\n\"hematid\",\n\"hematin\",\n\"heme\",\n\"hemen\",\n\"hemera\",\n\"hemiamb\",\n\"hemic\",\n\"hemin\",\n\"hemina\",\n\"hemine\",\n\"heminee\",\n\"hemiope\",\n\"hemipic\",\n\"heml\",\n\"hemlock\",\n\"hemmel\",\n\"hemmer\",\n\"hemocry\",\n\"hemoid\",\n\"hemol\",\n\"hemopod\",\n\"hemp\",\n\"hempen\",\n\"hempy\",\n\"hen\",\n\"henad\",\n\"henbane\",\n\"henbill\",\n\"henbit\",\n\"hence\",\n\"hencoop\",\n\"hencote\",\n\"hend\",\n\"hendly\",\n\"henfish\",\n\"henism\",\n\"henlike\",\n\"henna\",\n\"hennery\",\n\"hennin\",\n\"hennish\",\n\"henny\",\n\"henotic\",\n\"henpeck\",\n\"henpen\",\n\"henry\",\n\"hent\",\n\"henter\",\n\"henware\",\n\"henwife\",\n\"henwise\",\n\"henyard\",\n\"hep\",\n\"hepar\",\n\"heparin\",\n\"hepatic\",\n\"hepcat\",\n\"heppen\",\n\"hepper\",\n\"heptace\",\n\"heptad\",\n\"heptal\",\n\"heptane\",\n\"heptene\",\n\"heptine\",\n\"heptite\",\n\"heptoic\",\n\"heptose\",\n\"heptyl\",\n\"heptyne\",\n\"her\",\n\"herald\",\n\"herb\",\n\"herbage\",\n\"herbal\",\n\"herbane\",\n\"herbary\",\n\"herbish\",\n\"herbist\",\n\"herblet\",\n\"herbman\",\n\"herbose\",\n\"herbous\",\n\"herby\",\n\"herd\",\n\"herdboy\",\n\"herder\",\n\"herdic\",\n\"herding\",\n\"here\",\n\"hereat\",\n\"hereby\",\n\"herein\",\n\"herem\",\n\"hereof\",\n\"hereon\",\n\"heresy\",\n\"heretic\",\n\"hereto\",\n\"herile\",\n\"heriot\",\n\"heritor\",\n\"herl\",\n\"herling\",\n\"herma\",\n\"hermaic\",\n\"hermit\",\n\"hern\",\n\"hernani\",\n\"hernant\",\n\"herne\",\n\"hernia\",\n\"hernial\",\n\"hero\",\n\"heroess\",\n\"heroic\",\n\"heroid\",\n\"heroify\",\n\"heroin\",\n\"heroine\",\n\"heroism\",\n\"heroize\",\n\"heron\",\n\"heroner\",\n\"heronry\",\n\"herpes\",\n\"herring\",\n\"hers\",\n\"herse\",\n\"hersed\",\n\"herself\",\n\"hership\",\n\"hersir\",\n\"hertz\",\n\"hessite\",\n\"hest\",\n\"hestern\",\n\"het\",\n\"hetaera\",\n\"hetaery\",\n\"heteric\",\n\"hetero\",\n\"hething\",\n\"hetman\",\n\"hetter\",\n\"heuau\",\n\"heugh\",\n\"heumite\",\n\"hevi\",\n\"hew\",\n\"hewable\",\n\"hewel\",\n\"hewer\",\n\"hewhall\",\n\"hewn\",\n\"hewt\",\n\"hex\",\n\"hexa\",\n\"hexace\",\n\"hexacid\",\n\"hexact\",\n\"hexad\",\n\"hexadic\",\n\"hexagon\",\n\"hexagyn\",\n\"hexane\",\n\"hexaped\",\n\"hexapla\",\n\"hexapod\",\n\"hexarch\",\n\"hexene\",\n\"hexer\",\n\"hexerei\",\n\"hexeris\",\n\"hexine\",\n\"hexis\",\n\"hexitol\",\n\"hexode\",\n\"hexogen\",\n\"hexoic\",\n\"hexone\",\n\"hexonic\",\n\"hexosan\",\n\"hexose\",\n\"hexyl\",\n\"hexylic\",\n\"hexyne\",\n\"hey\",\n\"heyday\",\n\"hi\",\n\"hia\",\n\"hiant\",\n\"hiatal\",\n\"hiate\",\n\"hiation\",\n\"hiatus\",\n\"hibbin\",\n\"hic\",\n\"hicatee\",\n\"hiccup\",\n\"hick\",\n\"hickey\",\n\"hickory\",\n\"hidable\",\n\"hidage\",\n\"hidalgo\",\n\"hidated\",\n\"hidden\",\n\"hide\",\n\"hided\",\n\"hideous\",\n\"hider\",\n\"hidling\",\n\"hie\",\n\"hieder\",\n\"hield\",\n\"hiemal\",\n\"hieron\",\n\"hieros\",\n\"higdon\",\n\"higgle\",\n\"higgler\",\n\"high\",\n\"highboy\",\n\"higher\",\n\"highest\",\n\"highish\",\n\"highly\",\n\"highman\",\n\"hight\",\n\"hightop\",\n\"highway\",\n\"higuero\",\n\"hijack\",\n\"hike\",\n\"hiker\",\n\"hilch\",\n\"hilding\",\n\"hill\",\n\"hiller\",\n\"hillet\",\n\"hillman\",\n\"hillock\",\n\"hilltop\",\n\"hilly\",\n\"hilsa\",\n\"hilt\",\n\"hilum\",\n\"hilus\",\n\"him\",\n\"himp\",\n\"himself\",\n\"himward\",\n\"hin\",\n\"hinau\",\n\"hinch\",\n\"hind\",\n\"hinder\",\n\"hing\",\n\"hinge\",\n\"hinger\",\n\"hingle\",\n\"hinney\",\n\"hinny\",\n\"hinoid\",\n\"hinoki\",\n\"hint\",\n\"hinter\",\n\"hiodont\",\n\"hip\",\n\"hipbone\",\n\"hipe\",\n\"hiper\",\n\"hiphalt\",\n\"hipless\",\n\"hipmold\",\n\"hipped\",\n\"hippen\",\n\"hippian\",\n\"hippic\",\n\"hipping\",\n\"hippish\",\n\"hipple\",\n\"hippo\",\n\"hippoid\",\n\"hippus\",\n\"hippy\",\n\"hipshot\",\n\"hipwort\",\n\"hirable\",\n\"hircine\",\n\"hire\",\n\"hired\",\n\"hireman\",\n\"hirer\",\n\"hirmos\",\n\"hiro\",\n\"hirple\",\n\"hirse\",\n\"hirsel\",\n\"hirsle\",\n\"hirsute\",\n\"his\",\n\"hish\",\n\"hisn\",\n\"hispid\",\n\"hiss\",\n\"hisser\",\n\"hissing\",\n\"hist\",\n\"histie\",\n\"histoid\",\n\"histon\",\n\"histone\",\n\"history\",\n\"histrio\",\n\"hit\",\n\"hitch\",\n\"hitcher\",\n\"hitchy\",\n\"hithe\",\n\"hither\",\n\"hitless\",\n\"hitter\",\n\"hive\",\n\"hiver\",\n\"hives\",\n\"hizz\",\n\"ho\",\n\"hoar\",\n\"hoard\",\n\"hoarder\",\n\"hoarily\",\n\"hoarish\",\n\"hoarse\",\n\"hoarsen\",\n\"hoary\",\n\"hoast\",\n\"hoatzin\",\n\"hoax\",\n\"hoaxee\",\n\"hoaxer\",\n\"hob\",\n\"hobber\",\n\"hobbet\",\n\"hobbil\",\n\"hobble\",\n\"hobbler\",\n\"hobbly\",\n\"hobby\",\n\"hoblike\",\n\"hobnail\",\n\"hobnob\",\n\"hobo\",\n\"hoboism\",\n\"hocco\",\n\"hock\",\n\"hocker\",\n\"hocket\",\n\"hockey\",\n\"hocky\",\n\"hocus\",\n\"hod\",\n\"hodden\",\n\"hodder\",\n\"hoddle\",\n\"hoddy\",\n\"hodful\",\n\"hodman\",\n\"hoe\",\n\"hoecake\",\n\"hoedown\",\n\"hoeful\",\n\"hoer\",\n\"hog\",\n\"hoga\",\n\"hogan\",\n\"hogback\",\n\"hogbush\",\n\"hogfish\",\n\"hogged\",\n\"hogger\",\n\"hoggery\",\n\"hogget\",\n\"hoggie\",\n\"hoggin\",\n\"hoggish\",\n\"hoggism\",\n\"hoggy\",\n\"hogherd\",\n\"hoghide\",\n\"hoghood\",\n\"hoglike\",\n\"hogling\",\n\"hogmace\",\n\"hognose\",\n\"hognut\",\n\"hogpen\",\n\"hogship\",\n\"hogskin\",\n\"hogsty\",\n\"hogward\",\n\"hogwash\",\n\"hogweed\",\n\"hogwort\",\n\"hogyard\",\n\"hoi\",\n\"hoick\",\n\"hoin\",\n\"hoise\",\n\"hoist\",\n\"hoister\",\n\"hoit\",\n\"hoju\",\n\"hokey\",\n\"hokum\",\n\"holard\",\n\"holcad\",\n\"hold\",\n\"holdall\",\n\"holden\",\n\"holder\",\n\"holding\",\n\"holdout\",\n\"holdup\",\n\"hole\",\n\"holeman\",\n\"holer\",\n\"holey\",\n\"holia\",\n\"holiday\",\n\"holily\",\n\"holing\",\n\"holism\",\n\"holl\",\n\"holla\",\n\"holler\",\n\"hollin\",\n\"hollo\",\n\"hollock\",\n\"hollong\",\n\"hollow\",\n\"holly\",\n\"holm\",\n\"holmia\",\n\"holmic\",\n\"holmium\",\n\"holmos\",\n\"holour\",\n\"holster\",\n\"holt\",\n\"holy\",\n\"holyday\",\n\"homage\",\n\"homager\",\n\"home\",\n\"homelet\",\n\"homely\",\n\"homelyn\",\n\"homeoid\",\n\"homer\",\n\"homey\",\n\"homily\",\n\"hominal\",\n\"hominid\",\n\"hominy\",\n\"homish\",\n\"homo\",\n\"homodox\",\n\"homogen\",\n\"homonym\",\n\"homrai\",\n\"homy\",\n\"honda\",\n\"hondo\",\n\"hone\",\n\"honest\",\n\"honesty\",\n\"honey\",\n\"honeyed\",\n\"hong\",\n\"honied\",\n\"honily\",\n\"honk\",\n\"honker\",\n\"honor\",\n\"honoree\",\n\"honorer\",\n\"hontish\",\n\"hontous\",\n\"hooch\",\n\"hood\",\n\"hoodcap\",\n\"hooded\",\n\"hoodful\",\n\"hoodie\",\n\"hoodlum\",\n\"hoodman\",\n\"hoodoo\",\n\"hoodshy\",\n\"hooey\",\n\"hoof\",\n\"hoofed\",\n\"hoofer\",\n\"hoofish\",\n\"hooflet\",\n\"hoofrot\",\n\"hoofs\",\n\"hoofy\",\n\"hook\",\n\"hookah\",\n\"hooked\",\n\"hooker\",\n\"hookers\",\n\"hookish\",\n\"hooklet\",\n\"hookman\",\n\"hooktip\",\n\"hookum\",\n\"hookup\",\n\"hooky\",\n\"hoolock\",\n\"hooly\",\n\"hoon\",\n\"hoop\",\n\"hooped\",\n\"hooper\",\n\"hooping\",\n\"hoopla\",\n\"hoople\",\n\"hoopman\",\n\"hoopoe\",\n\"hoose\",\n\"hoosh\",\n\"hoot\",\n\"hootay\",\n\"hooter\",\n\"hoove\",\n\"hooven\",\n\"hoovey\",\n\"hop\",\n\"hopbine\",\n\"hopbush\",\n\"hope\",\n\"hoped\",\n\"hopeful\",\n\"hopeite\",\n\"hoper\",\n\"hopi\",\n\"hoplite\",\n\"hopoff\",\n\"hopped\",\n\"hopper\",\n\"hoppers\",\n\"hoppet\",\n\"hoppity\",\n\"hopple\",\n\"hoppy\",\n\"hoptoad\",\n\"hopvine\",\n\"hopyard\",\n\"hora\",\n\"horal\",\n\"horary\",\n\"hordary\",\n\"horde\",\n\"hordein\",\n\"horizon\",\n\"horme\",\n\"hormic\",\n\"hormigo\",\n\"hormion\",\n\"hormist\",\n\"hormone\",\n\"hormos\",\n\"horn\",\n\"horned\",\n\"horner\",\n\"hornet\",\n\"hornety\",\n\"hornful\",\n\"hornify\",\n\"hornily\",\n\"horning\",\n\"hornish\",\n\"hornist\",\n\"hornito\",\n\"hornlet\",\n\"horntip\",\n\"horny\",\n\"horrent\",\n\"horreum\",\n\"horrid\",\n\"horrify\",\n\"horror\",\n\"horse\",\n\"horser\",\n\"horsify\",\n\"horsily\",\n\"horsing\",\n\"horst\",\n\"horsy\",\n\"hortite\",\n\"hory\",\n\"hosanna\",\n\"hose\",\n\"hosed\",\n\"hosel\",\n\"hoseman\",\n\"hosier\",\n\"hosiery\",\n\"hospice\",\n\"host\",\n\"hostage\",\n\"hostel\",\n\"hoster\",\n\"hostess\",\n\"hostie\",\n\"hostile\",\n\"hosting\",\n\"hostler\",\n\"hostly\",\n\"hostry\",\n\"hot\",\n\"hotbed\",\n\"hotbox\",\n\"hotch\",\n\"hotel\",\n\"hotfoot\",\n\"hothead\",\n\"hoti\",\n\"hotly\",\n\"hotness\",\n\"hotspur\",\n\"hotter\",\n\"hottery\",\n\"hottish\",\n\"houbara\",\n\"hough\",\n\"hougher\",\n\"hounce\",\n\"hound\",\n\"hounder\",\n\"houndy\",\n\"hour\",\n\"hourful\",\n\"houri\",\n\"hourly\",\n\"housage\",\n\"housal\",\n\"house\",\n\"housel\",\n\"houser\",\n\"housing\",\n\"housty\",\n\"housy\",\n\"houtou\",\n\"houvari\",\n\"hove\",\n\"hovel\",\n\"hoveler\",\n\"hoven\",\n\"hover\",\n\"hoverer\",\n\"hoverly\",\n\"how\",\n\"howadji\",\n\"howbeit\",\n\"howdah\",\n\"howder\",\n\"howdie\",\n\"howdy\",\n\"howe\",\n\"howel\",\n\"however\",\n\"howff\",\n\"howish\",\n\"howk\",\n\"howkit\",\n\"howl\",\n\"howler\",\n\"howlet\",\n\"howling\",\n\"howlite\",\n\"howso\",\n\"hox\",\n\"hoy\",\n\"hoyden\",\n\"hoyle\",\n\"hoyman\",\n\"huaca\",\n\"huaco\",\n\"huarizo\",\n\"hub\",\n\"hubb\",\n\"hubba\",\n\"hubber\",\n\"hubble\",\n\"hubbly\",\n\"hubbub\",\n\"hubby\",\n\"hubshi\",\n\"huchen\",\n\"hucho\",\n\"huck\",\n\"huckle\",\n\"hud\",\n\"huddle\",\n\"huddler\",\n\"huddock\",\n\"huddup\",\n\"hue\",\n\"hued\",\n\"hueful\",\n\"hueless\",\n\"huer\",\n\"huff\",\n\"huffier\",\n\"huffily\",\n\"huffish\",\n\"huffle\",\n\"huffler\",\n\"huffy\",\n\"hug\",\n\"huge\",\n\"hugely\",\n\"hugeous\",\n\"hugger\",\n\"hugging\",\n\"huggle\",\n\"hugsome\",\n\"huh\",\n\"huia\",\n\"huipil\",\n\"huitain\",\n\"huke\",\n\"hula\",\n\"huldee\",\n\"hulk\",\n\"hulkage\",\n\"hulking\",\n\"hulky\",\n\"hull\",\n\"huller\",\n\"hullock\",\n\"hulloo\",\n\"hulsite\",\n\"hulster\",\n\"hulu\",\n\"hulver\",\n\"hum\",\n\"human\",\n\"humane\",\n\"humanly\",\n\"humate\",\n\"humble\",\n\"humbler\",\n\"humblie\",\n\"humbly\",\n\"humbo\",\n\"humbug\",\n\"humbuzz\",\n\"humdrum\",\n\"humect\",\n\"humeral\",\n\"humeri\",\n\"humerus\",\n\"humet\",\n\"humetty\",\n\"humhum\",\n\"humic\",\n\"humid\",\n\"humidly\",\n\"humidor\",\n\"humific\",\n\"humify\",\n\"humin\",\n\"humite\",\n\"humlie\",\n\"hummel\",\n\"hummer\",\n\"hummie\",\n\"humming\",\n\"hummock\",\n\"humor\",\n\"humoral\",\n\"humous\",\n\"hump\",\n\"humped\",\n\"humph\",\n\"humpty\",\n\"humpy\",\n\"humus\",\n\"hunch\",\n\"hunchet\",\n\"hunchy\",\n\"hundi\",\n\"hundred\",\n\"hung\",\n\"hunger\",\n\"hungry\",\n\"hunh\",\n\"hunk\",\n\"hunker\",\n\"hunkers\",\n\"hunkies\",\n\"hunks\",\n\"hunky\",\n\"hunt\",\n\"hunting\",\n\"hup\",\n\"hura\",\n\"hurdies\",\n\"hurdis\",\n\"hurdle\",\n\"hurdler\",\n\"hurds\",\n\"hure\",\n\"hureek\",\n\"hurgila\",\n\"hurkle\",\n\"hurl\",\n\"hurled\",\n\"hurler\",\n\"hurley\",\n\"hurling\",\n\"hurlock\",\n\"hurly\",\n\"huron\",\n\"hurr\",\n\"hurrah\",\n\"hurried\",\n\"hurrier\",\n\"hurrock\",\n\"hurroo\",\n\"hurry\",\n\"hurst\",\n\"hurt\",\n\"hurted\",\n\"hurter\",\n\"hurtful\",\n\"hurting\",\n\"hurtle\",\n\"hurty\",\n\"husband\",\n\"huse\",\n\"hush\",\n\"hushaby\",\n\"husheen\",\n\"hushel\",\n\"husher\",\n\"hushful\",\n\"hushing\",\n\"hushion\",\n\"husho\",\n\"husk\",\n\"husked\",\n\"husker\",\n\"huskily\",\n\"husking\",\n\"husky\",\n\"huso\",\n\"huspil\",\n\"huss\",\n\"hussar\",\n\"hussy\",\n\"husting\",\n\"hustle\",\n\"hustler\",\n\"hut\",\n\"hutch\",\n\"hutcher\",\n\"hutchet\",\n\"huthold\",\n\"hutia\",\n\"hutlet\",\n\"hutment\",\n\"huvelyk\",\n\"huzoor\",\n\"huzz\",\n\"huzza\",\n\"huzzard\",\n\"hyaena\",\n\"hyaline\",\n\"hyalite\",\n\"hyaloid\",\n\"hybosis\",\n\"hybrid\",\n\"hydatid\",\n\"hydnoid\",\n\"hydrant\",\n\"hydrate\",\n\"hydrazo\",\n\"hydria\",\n\"hydric\",\n\"hydride\",\n\"hydro\",\n\"hydroa\",\n\"hydroid\",\n\"hydrol\",\n\"hydrome\",\n\"hydrone\",\n\"hydrops\",\n\"hydrous\",\n\"hydroxy\",\n\"hydrula\",\n\"hyena\",\n\"hyenic\",\n\"hyenine\",\n\"hyenoid\",\n\"hyetal\",\n\"hygeist\",\n\"hygiene\",\n\"hygric\",\n\"hygrine\",\n\"hygroma\",\n\"hying\",\n\"hyke\",\n\"hyle\",\n\"hyleg\",\n\"hylic\",\n\"hylism\",\n\"hylist\",\n\"hyloid\",\n\"hymen\",\n\"hymenal\",\n\"hymenic\",\n\"hymn\",\n\"hymnal\",\n\"hymnary\",\n\"hymner\",\n\"hymnic\",\n\"hymnist\",\n\"hymnode\",\n\"hymnody\",\n\"hynde\",\n\"hyne\",\n\"hyoid\",\n\"hyoidal\",\n\"hyoidan\",\n\"hyoides\",\n\"hyp\",\n\"hypate\",\n\"hypaton\",\n\"hyper\",\n\"hypha\",\n\"hyphal\",\n\"hyphema\",\n\"hyphen\",\n\"hypho\",\n\"hypnody\",\n\"hypnoid\",\n\"hypnone\",\n\"hypo\",\n\"hypogee\",\n\"hypoid\",\n\"hyponym\",\n\"hypopus\",\n\"hyporit\",\n\"hyppish\",\n\"hypural\",\n\"hyraces\",\n\"hyracid\",\n\"hyrax\",\n\"hyson\",\n\"hyssop\",\n\"i\",\n\"iamb\",\n\"iambi\",\n\"iambic\",\n\"iambist\",\n\"iambize\",\n\"iambus\",\n\"iao\",\n\"iatric\",\n\"iba\",\n\"iberite\",\n\"ibex\",\n\"ibices\",\n\"ibid\",\n\"ibidine\",\n\"ibis\",\n\"ibolium\",\n\"ibota\",\n\"icaco\",\n\"ice\",\n\"iceberg\",\n\"iceboat\",\n\"icebone\",\n\"icebox\",\n\"icecap\",\n\"iced\",\n\"icefall\",\n\"icefish\",\n\"iceland\",\n\"iceleaf\",\n\"iceless\",\n\"icelike\",\n\"iceman\",\n\"iceroot\",\n\"icework\",\n\"ich\",\n\"ichnite\",\n\"icho\",\n\"ichor\",\n\"ichthus\",\n\"ichu\",\n\"icica\",\n\"icicle\",\n\"icicled\",\n\"icily\",\n\"iciness\",\n\"icing\",\n\"icon\",\n\"iconic\",\n\"iconism\",\n\"icosian\",\n\"icotype\",\n\"icteric\",\n\"icterus\",\n\"ictic\",\n\"ictuate\",\n\"ictus\",\n\"icy\",\n\"id\",\n\"idalia\",\n\"idant\",\n\"iddat\",\n\"ide\",\n\"idea\",\n\"ideaed\",\n\"ideaful\",\n\"ideal\",\n\"ideally\",\n\"ideate\",\n\"ideist\",\n\"identic\",\n\"ides\",\n\"idgah\",\n\"idiasm\",\n\"idic\",\n\"idiocy\",\n\"idiom\",\n\"idiot\",\n\"idiotcy\",\n\"idiotic\",\n\"idiotry\",\n\"idite\",\n\"iditol\",\n\"idle\",\n\"idleful\",\n\"idleman\",\n\"idler\",\n\"idleset\",\n\"idlety\",\n\"idlish\",\n\"idly\",\n\"idol\",\n\"idola\",\n\"idolify\",\n\"idolism\",\n\"idolist\",\n\"idolize\",\n\"idolous\",\n\"idolum\",\n\"idoneal\",\n\"idorgan\",\n\"idose\",\n\"idryl\",\n\"idyl\",\n\"idyler\",\n\"idylism\",\n\"idylist\",\n\"idylize\",\n\"idyllic\",\n\"ie\",\n\"if\",\n\"ife\",\n\"iffy\",\n\"igloo\",\n\"ignatia\",\n\"ignavia\",\n\"igneous\",\n\"ignify\",\n\"ignite\",\n\"igniter\",\n\"ignitor\",\n\"ignoble\",\n\"ignobly\",\n\"ignore\",\n\"ignorer\",\n\"ignote\",\n\"iguana\",\n\"iguanid\",\n\"ihi\",\n\"ihleite\",\n\"ihram\",\n\"iiwi\",\n\"ijma\",\n\"ijolite\",\n\"ikat\",\n\"ikey\",\n\"ikona\",\n\"ikra\",\n\"ileac\",\n\"ileitis\",\n\"ileon\",\n\"ilesite\",\n\"ileum\",\n\"ileus\",\n\"ilex\",\n\"ilia\",\n\"iliac\",\n\"iliacus\",\n\"iliahi\",\n\"ilial\",\n\"iliau\",\n\"ilicic\",\n\"ilicin\",\n\"ilima\",\n\"ilium\",\n\"ilk\",\n\"ilka\",\n\"ilkane\",\n\"ill\",\n\"illapse\",\n\"illeck\",\n\"illegal\",\n\"illeism\",\n\"illeist\",\n\"illess\",\n\"illfare\",\n\"illicit\",\n\"illish\",\n\"illium\",\n\"illness\",\n\"illocal\",\n\"illogic\",\n\"illoyal\",\n\"illth\",\n\"illude\",\n\"illuder\",\n\"illume\",\n\"illumer\",\n\"illupi\",\n\"illure\",\n\"illusor\",\n\"illy\",\n\"ilot\",\n\"ilvaite\",\n\"image\",\n\"imager\",\n\"imagery\",\n\"imagine\",\n\"imagism\",\n\"imagist\",\n\"imago\",\n\"imam\",\n\"imamah\",\n\"imamate\",\n\"imamic\",\n\"imaret\",\n\"imban\",\n\"imband\",\n\"imbarge\",\n\"imbark\",\n\"imbarn\",\n\"imbased\",\n\"imbat\",\n\"imbauba\",\n\"imbe\",\n\"imbed\",\n\"imber\",\n\"imbibe\",\n\"imbiber\",\n\"imbondo\",\n\"imbosom\",\n\"imbower\",\n\"imbrex\",\n\"imbrue\",\n\"imbrute\",\n\"imbue\",\n\"imburse\",\n\"imi\",\n\"imide\",\n\"imidic\",\n\"imine\",\n\"imino\",\n\"imitant\",\n\"imitate\",\n\"immane\",\n\"immask\",\n\"immense\",\n\"immerd\",\n\"immerge\",\n\"immerit\",\n\"immerse\",\n\"immew\",\n\"immi\",\n\"immit\",\n\"immix\",\n\"immoral\",\n\"immound\",\n\"immund\",\n\"immune\",\n\"immure\",\n\"immute\",\n\"imonium\",\n\"imp\",\n\"impack\",\n\"impact\",\n\"impages\",\n\"impaint\",\n\"impair\",\n\"impala\",\n\"impale\",\n\"impaler\",\n\"impall\",\n\"impalm\",\n\"impalsy\",\n\"impane\",\n\"impanel\",\n\"impar\",\n\"impark\",\n\"imparl\",\n\"impart\",\n\"impasse\",\n\"impaste\",\n\"impasto\",\n\"impave\",\n\"impavid\",\n\"impawn\",\n\"impeach\",\n\"impearl\",\n\"impede\",\n\"impeder\",\n\"impel\",\n\"impen\",\n\"impend\",\n\"impent\",\n\"imperia\",\n\"imperil\",\n\"impest\",\n\"impetre\",\n\"impetus\",\n\"imphee\",\n\"impi\",\n\"impiety\",\n\"impinge\",\n\"impious\",\n\"impish\",\n\"implant\",\n\"implate\",\n\"implead\",\n\"implete\",\n\"implex\",\n\"implial\",\n\"impling\",\n\"implode\",\n\"implore\",\n\"implume\",\n\"imply\",\n\"impofo\",\n\"impone\",\n\"impoor\",\n\"import\",\n\"imposal\",\n\"impose\",\n\"imposer\",\n\"impost\",\n\"impot\",\n\"impound\",\n\"impreg\",\n\"impregn\",\n\"impresa\",\n\"imprese\",\n\"impress\",\n\"imprest\",\n\"imprime\",\n\"imprint\",\n\"improof\",\n\"improve\",\n\"impship\",\n\"impubic\",\n\"impugn\",\n\"impulse\",\n\"impure\",\n\"impute\",\n\"imputer\",\n\"impy\",\n\"imshi\",\n\"imsonic\",\n\"imu\",\n\"in\",\n\"inachid\",\n\"inadept\",\n\"inagile\",\n\"inaja\",\n\"inane\",\n\"inanely\",\n\"inanga\",\n\"inanity\",\n\"inapt\",\n\"inaptly\",\n\"inarch\",\n\"inarm\",\n\"inaugur\",\n\"inaxon\",\n\"inbe\",\n\"inbeing\",\n\"inbent\",\n\"inbirth\",\n\"inblow\",\n\"inblown\",\n\"inboard\",\n\"inbond\",\n\"inborn\",\n\"inbound\",\n\"inbread\",\n\"inbreak\",\n\"inbred\",\n\"inbreed\",\n\"inbring\",\n\"inbuilt\",\n\"inburnt\",\n\"inburst\",\n\"inby\",\n\"incarn\",\n\"incase\",\n\"incast\",\n\"incense\",\n\"incept\",\n\"incest\",\n\"inch\",\n\"inched\",\n\"inchpin\",\n\"incide\",\n\"incisal\",\n\"incise\",\n\"incisor\",\n\"incite\",\n\"inciter\",\n\"incivic\",\n\"incline\",\n\"inclip\",\n\"inclose\",\n\"include\",\n\"inclusa\",\n\"incluse\",\n\"incog\",\n\"income\",\n\"incomer\",\n\"inconnu\",\n\"incrash\",\n\"increep\",\n\"increst\",\n\"incross\",\n\"incrust\",\n\"incubi\",\n\"incubus\",\n\"incudal\",\n\"incudes\",\n\"incult\",\n\"incur\",\n\"incurse\",\n\"incurve\",\n\"incus\",\n\"incuse\",\n\"incut\",\n\"indaba\",\n\"indan\",\n\"indane\",\n\"indart\",\n\"indazin\",\n\"indazol\",\n\"inde\",\n\"indebt\",\n\"indeed\",\n\"indeedy\",\n\"indene\",\n\"indent\",\n\"index\",\n\"indexed\",\n\"indexer\",\n\"indic\",\n\"indican\",\n\"indices\",\n\"indicia\",\n\"indict\",\n\"indign\",\n\"indigo\",\n\"indite\",\n\"inditer\",\n\"indium\",\n\"indogen\",\n\"indole\",\n\"indoles\",\n\"indolyl\",\n\"indoor\",\n\"indoors\",\n\"indorse\",\n\"indoxyl\",\n\"indraft\",\n\"indrawn\",\n\"indri\",\n\"induce\",\n\"induced\",\n\"inducer\",\n\"induct\",\n\"indue\",\n\"indulge\",\n\"indult\",\n\"indulto\",\n\"induna\",\n\"indwell\",\n\"indy\",\n\"indyl\",\n\"indylic\",\n\"inearth\",\n\"inept\",\n\"ineptly\",\n\"inequal\",\n\"inerm\",\n\"inert\",\n\"inertia\",\n\"inertly\",\n\"inesite\",\n\"ineunt\",\n\"inexact\",\n\"inexist\",\n\"inface\",\n\"infall\",\n\"infame\",\n\"infamy\",\n\"infancy\",\n\"infand\",\n\"infang\",\n\"infant\",\n\"infanta\",\n\"infante\",\n\"infarct\",\n\"infare\",\n\"infaust\",\n\"infect\",\n\"infeed\",\n\"infeft\",\n\"infelt\",\n\"infer\",\n\"infern\",\n\"inferno\",\n\"infest\",\n\"infidel\",\n\"infield\",\n\"infill\",\n\"infilm\",\n\"infirm\",\n\"infit\",\n\"infix\",\n\"inflame\",\n\"inflate\",\n\"inflect\",\n\"inflex\",\n\"inflict\",\n\"inflood\",\n\"inflow\",\n\"influx\",\n\"infold\",\n\"inform\",\n\"infra\",\n\"infract\",\n\"infula\",\n\"infuse\",\n\"infuser\",\n\"ing\",\n\"ingate\",\n\"ingenit\",\n\"ingenue\",\n\"ingest\",\n\"ingesta\",\n\"ingiver\",\n\"ingle\",\n\"inglobe\",\n\"ingoing\",\n\"ingot\",\n\"ingraft\",\n\"ingrain\",\n\"ingrate\",\n\"ingress\",\n\"ingross\",\n\"ingrow\",\n\"ingrown\",\n\"inguen\",\n\"ingulf\",\n\"inhabit\",\n\"inhale\",\n\"inhaler\",\n\"inhaul\",\n\"inhaust\",\n\"inhere\",\n\"inherit\",\n\"inhiate\",\n\"inhibit\",\n\"inhuman\",\n\"inhume\",\n\"inhumer\",\n\"inial\",\n\"iniome\",\n\"inion\",\n\"initial\",\n\"initis\",\n\"initive\",\n\"inject\",\n\"injelly\",\n\"injunct\",\n\"injure\",\n\"injured\",\n\"injurer\",\n\"injury\",\n\"ink\",\n\"inkbush\",\n\"inken\",\n\"inker\",\n\"inket\",\n\"inkfish\",\n\"inkhorn\",\n\"inkish\",\n\"inkle\",\n\"inkless\",\n\"inklike\",\n\"inkling\",\n\"inknot\",\n\"inkosi\",\n\"inkpot\",\n\"inkroot\",\n\"inks\",\n\"inkshed\",\n\"inkweed\",\n\"inkwell\",\n\"inkwood\",\n\"inky\",\n\"inlaid\",\n\"inlaik\",\n\"inlake\",\n\"inland\",\n\"inlaut\",\n\"inlaw\",\n\"inlawry\",\n\"inlay\",\n\"inlayer\",\n\"inleak\",\n\"inlet\",\n\"inlier\",\n\"inlook\",\n\"inly\",\n\"inlying\",\n\"inmate\",\n\"inmeats\",\n\"inmost\",\n\"inn\",\n\"innate\",\n\"inneity\",\n\"inner\",\n\"innerly\",\n\"innerve\",\n\"inness\",\n\"innest\",\n\"innet\",\n\"inning\",\n\"innless\",\n\"innyard\",\n\"inocyte\",\n\"inogen\",\n\"inoglia\",\n\"inolith\",\n\"inoma\",\n\"inone\",\n\"inopine\",\n\"inorb\",\n\"inosic\",\n\"inosin\",\n\"inosite\",\n\"inower\",\n\"inphase\",\n\"inport\",\n\"inpour\",\n\"inpush\",\n\"input\",\n\"inquest\",\n\"inquiet\",\n\"inquire\",\n\"inquiry\",\n\"inring\",\n\"inro\",\n\"inroad\",\n\"inroll\",\n\"inrub\",\n\"inrun\",\n\"inrush\",\n\"insack\",\n\"insane\",\n\"insculp\",\n\"insea\",\n\"inseam\",\n\"insect\",\n\"insee\",\n\"inseer\",\n\"insense\",\n\"insert\",\n\"inset\",\n\"inshave\",\n\"inshell\",\n\"inship\",\n\"inshoe\",\n\"inshoot\",\n\"inshore\",\n\"inside\",\n\"insider\",\n\"insight\",\n\"insigne\",\n\"insipid\",\n\"insist\",\n\"insnare\",\n\"insofar\",\n\"insole\",\n\"insolid\",\n\"insooth\",\n\"insorb\",\n\"insoul\",\n\"inspan\",\n\"inspeak\",\n\"inspect\",\n\"inspire\",\n\"inspoke\",\n\"install\",\n\"instant\",\n\"instar\",\n\"instate\",\n\"instead\",\n\"insteam\",\n\"insteep\",\n\"instep\",\n\"instill\",\n\"insula\",\n\"insular\",\n\"insulin\",\n\"insulse\",\n\"insult\",\n\"insunk\",\n\"insure\",\n\"insured\",\n\"insurer\",\n\"insurge\",\n\"inswamp\",\n\"inswell\",\n\"inswept\",\n\"inswing\",\n\"intact\",\n\"intake\",\n\"intaker\",\n\"integer\",\n\"inteind\",\n\"intend\",\n\"intense\",\n\"intent\",\n\"inter\",\n\"interim\",\n\"intern\",\n\"intext\",\n\"inthrow\",\n\"intil\",\n\"intima\",\n\"intimal\",\n\"intine\",\n\"into\",\n\"intoed\",\n\"intone\",\n\"intoner\",\n\"intort\",\n\"intown\",\n\"intrada\",\n\"intrait\",\n\"intrant\",\n\"intreat\",\n\"intrine\",\n\"introit\",\n\"intrude\",\n\"intruse\",\n\"intrust\",\n\"intube\",\n\"intue\",\n\"intuent\",\n\"intuit\",\n\"inturn\",\n\"intwist\",\n\"inula\",\n\"inulase\",\n\"inulin\",\n\"inuloid\",\n\"inunct\",\n\"inure\",\n\"inured\",\n\"inurn\",\n\"inutile\",\n\"invade\",\n\"invader\",\n\"invalid\",\n\"inveigh\",\n\"inveil\",\n\"invein\",\n\"invent\",\n\"inverse\",\n\"invert\",\n\"invest\",\n\"invigor\",\n\"invised\",\n\"invital\",\n\"invite\",\n\"invitee\",\n\"inviter\",\n\"invivid\",\n\"invoice\",\n\"invoke\",\n\"invoker\",\n\"involve\",\n\"inwale\",\n\"inwall\",\n\"inward\",\n\"inwards\",\n\"inweave\",\n\"inweed\",\n\"inwick\",\n\"inwind\",\n\"inwit\",\n\"inwith\",\n\"inwood\",\n\"inwork\",\n\"inworn\",\n\"inwound\",\n\"inwoven\",\n\"inwrap\",\n\"inwrit\",\n\"inyoite\",\n\"inyoke\",\n\"io\",\n\"iodate\",\n\"iodic\",\n\"iodide\",\n\"iodine\",\n\"iodism\",\n\"iodite\",\n\"iodize\",\n\"iodizer\",\n\"iodo\",\n\"iodol\",\n\"iodoso\",\n\"iodous\",\n\"iodoxy\",\n\"iolite\",\n\"ion\",\n\"ionic\",\n\"ionium\",\n\"ionize\",\n\"ionizer\",\n\"ionogen\",\n\"ionone\",\n\"iota\",\n\"iotize\",\n\"ipecac\",\n\"ipid\",\n\"ipil\",\n\"ipomea\",\n\"ipseand\",\n\"ipseity\",\n\"iracund\",\n\"irade\",\n\"irate\",\n\"irately\",\n\"ire\",\n\"ireful\",\n\"ireless\",\n\"irene\",\n\"irenic\",\n\"irenics\",\n\"irian\",\n\"irid\",\n\"iridal\",\n\"iridate\",\n\"irides\",\n\"iridial\",\n\"iridian\",\n\"iridic\",\n\"iridin\",\n\"iridine\",\n\"iridite\",\n\"iridium\",\n\"iridize\",\n\"iris\",\n\"irised\",\n\"irisin\",\n\"iritic\",\n\"iritis\",\n\"irk\",\n\"irksome\",\n\"irok\",\n\"iroko\",\n\"iron\",\n\"irone\",\n\"ironer\",\n\"ironice\",\n\"ironish\",\n\"ironism\",\n\"ironist\",\n\"ironize\",\n\"ironly\",\n\"ironman\",\n\"irony\",\n\"irrisor\",\n\"irrupt\",\n\"is\",\n\"isagoge\",\n\"isagon\",\n\"isamine\",\n\"isatate\",\n\"isatic\",\n\"isatide\",\n\"isatin\",\n\"isazoxy\",\n\"isba\",\n\"ischiac\",\n\"ischial\",\n\"ischium\",\n\"ischury\",\n\"iserine\",\n\"iserite\",\n\"isidium\",\n\"isidoid\",\n\"island\",\n\"islandy\",\n\"islay\",\n\"isle\",\n\"islet\",\n\"isleted\",\n\"islot\",\n\"ism\",\n\"ismal\",\n\"ismatic\",\n\"ismdom\",\n\"ismy\",\n\"iso\",\n\"isoamyl\",\n\"isobar\",\n\"isobare\",\n\"isobase\",\n\"isobath\",\n\"isochor\",\n\"isocola\",\n\"isocrat\",\n\"isodont\",\n\"isoflor\",\n\"isogamy\",\n\"isogen\",\n\"isogeny\",\n\"isogon\",\n\"isogram\",\n\"isohel\",\n\"isohyet\",\n\"isolate\",\n\"isology\",\n\"isomer\",\n\"isomere\",\n\"isomery\",\n\"isoneph\",\n\"isonomy\",\n\"isonym\",\n\"isonymy\",\n\"isopag\",\n\"isopod\",\n\"isopoly\",\n\"isoptic\",\n\"isopyre\",\n\"isotac\",\n\"isotely\",\n\"isotome\",\n\"isotony\",\n\"isotope\",\n\"isotopy\",\n\"isotron\",\n\"isotype\",\n\"isoxime\",\n\"issei\",\n\"issite\",\n\"issuant\",\n\"issue\",\n\"issuer\",\n\"issuing\",\n\"ist\",\n\"isthmi\",\n\"isthmic\",\n\"isthmus\",\n\"istle\",\n\"istoke\",\n\"isuret\",\n\"isuroid\",\n\"it\",\n\"itacism\",\n\"itacist\",\n\"italics\",\n\"italite\",\n\"itch\",\n\"itching\",\n\"itchy\",\n\"itcze\",\n\"item\",\n\"iteming\",\n\"itemize\",\n\"itemy\",\n\"iter\",\n\"iterant\",\n\"iterate\",\n\"ither\",\n\"itmo\",\n\"itoubou\",\n\"its\",\n\"itself\",\n\"iturite\",\n\"itzebu\",\n\"iva\",\n\"ivied\",\n\"ivin\",\n\"ivoried\",\n\"ivorine\",\n\"ivorist\",\n\"ivory\",\n\"ivy\",\n\"ivylike\",\n\"ivyweed\",\n\"ivywood\",\n\"ivywort\",\n\"iwa\",\n\"iwaiwa\",\n\"iwis\",\n\"ixodian\",\n\"ixodic\",\n\"ixodid\",\n\"iyo\",\n\"izar\",\n\"izard\",\n\"izle\",\n\"izote\",\n\"iztle\",\n\"izzard\",\n\"j\",\n\"jab\",\n\"jabbed\",\n\"jabber\",\n\"jabbing\",\n\"jabble\",\n\"jabers\",\n\"jabia\",\n\"jabiru\",\n\"jabot\",\n\"jabul\",\n\"jacal\",\n\"jacamar\",\n\"jacami\",\n\"jacamin\",\n\"jacana\",\n\"jacare\",\n\"jacate\",\n\"jacchus\",\n\"jacent\",\n\"jacinth\",\n\"jack\",\n\"jackal\",\n\"jackass\",\n\"jackbox\",\n\"jackboy\",\n\"jackdaw\",\n\"jackeen\",\n\"jacker\",\n\"jacket\",\n\"jackety\",\n\"jackleg\",\n\"jackman\",\n\"jacko\",\n\"jackrod\",\n\"jacksaw\",\n\"jacktan\",\n\"jacobus\",\n\"jacoby\",\n\"jaconet\",\n\"jactant\",\n\"jacu\",\n\"jacuaru\",\n\"jadder\",\n\"jade\",\n\"jaded\",\n\"jadedly\",\n\"jadeite\",\n\"jadery\",\n\"jadish\",\n\"jady\",\n\"jaeger\",\n\"jag\",\n\"jagat\",\n\"jager\",\n\"jagged\",\n\"jagger\",\n\"jaggery\",\n\"jaggy\",\n\"jagir\",\n\"jagla\",\n\"jagless\",\n\"jagong\",\n\"jagrata\",\n\"jagua\",\n\"jaguar\",\n\"jail\",\n\"jailage\",\n\"jaildom\",\n\"jailer\",\n\"jailish\",\n\"jajman\",\n\"jake\",\n\"jakes\",\n\"jako\",\n\"jalap\",\n\"jalapa\",\n\"jalapin\",\n\"jalkar\",\n\"jalopy\",\n\"jalouse\",\n\"jam\",\n\"jama\",\n\"jaman\",\n\"jamb\",\n\"jambeau\",\n\"jambo\",\n\"jambone\",\n\"jambool\",\n\"jambosa\",\n\"jamdani\",\n\"jami\",\n\"jamlike\",\n\"jammer\",\n\"jammy\",\n\"jampan\",\n\"jampani\",\n\"jamwood\",\n\"janapa\",\n\"janapan\",\n\"jane\",\n\"jangada\",\n\"jangkar\",\n\"jangle\",\n\"jangler\",\n\"jangly\",\n\"janitor\",\n\"jank\",\n\"janker\",\n\"jann\",\n\"jannock\",\n\"jantu\",\n\"janua\",\n\"jaob\",\n\"jap\",\n\"japan\",\n\"jape\",\n\"japer\",\n\"japery\",\n\"japing\",\n\"japish\",\n\"jaquima\",\n\"jar\",\n\"jara\",\n\"jaragua\",\n\"jarbird\",\n\"jarble\",\n\"jarbot\",\n\"jarfly\",\n\"jarful\",\n\"jarg\",\n\"jargon\",\n\"jarkman\",\n\"jarl\",\n\"jarldom\",\n\"jarless\",\n\"jarnut\",\n\"jarool\",\n\"jarra\",\n\"jarrah\",\n\"jarring\",\n\"jarry\",\n\"jarvey\",\n\"jasey\",\n\"jaseyed\",\n\"jasmine\",\n\"jasmone\",\n\"jasper\",\n\"jaspery\",\n\"jaspis\",\n\"jaspoid\",\n\"jass\",\n\"jassid\",\n\"jassoid\",\n\"jatha\",\n\"jati\",\n\"jato\",\n\"jaudie\",\n\"jauk\",\n\"jaun\",\n\"jaunce\",\n\"jaunder\",\n\"jaunt\",\n\"jauntie\",\n\"jaunty\",\n\"jaup\",\n\"javali\",\n\"javelin\",\n\"javer\",\n\"jaw\",\n\"jawab\",\n\"jawbone\",\n\"jawed\",\n\"jawfall\",\n\"jawfish\",\n\"jawfoot\",\n\"jawless\",\n\"jawy\",\n\"jay\",\n\"jayhawk\",\n\"jaypie\",\n\"jaywalk\",\n\"jazz\",\n\"jazzer\",\n\"jazzily\",\n\"jazzy\",\n\"jealous\",\n\"jean\",\n\"jeans\",\n\"jecoral\",\n\"jecorin\",\n\"jed\",\n\"jedcock\",\n\"jedding\",\n\"jeddock\",\n\"jeel\",\n\"jeep\",\n\"jeer\",\n\"jeerer\",\n\"jeering\",\n\"jeery\",\n\"jeff\",\n\"jehu\",\n\"jehup\",\n\"jejunal\",\n\"jejune\",\n\"jejunum\",\n\"jelab\",\n\"jelick\",\n\"jell\",\n\"jellica\",\n\"jellico\",\n\"jellied\",\n\"jellify\",\n\"jellily\",\n\"jelloid\",\n\"jelly\",\n\"jemadar\",\n\"jemmily\",\n\"jemmy\",\n\"jenkin\",\n\"jenna\",\n\"jennet\",\n\"jennier\",\n\"jenny\",\n\"jeofail\",\n\"jeopard\",\n\"jerboa\",\n\"jereed\",\n\"jerez\",\n\"jerib\",\n\"jerk\",\n\"jerker\",\n\"jerkily\",\n\"jerkin\",\n\"jerkish\",\n\"jerky\",\n\"jerl\",\n\"jerm\",\n\"jerque\",\n\"jerquer\",\n\"jerry\",\n\"jersey\",\n\"jert\",\n\"jervia\",\n\"jervina\",\n\"jervine\",\n\"jess\",\n\"jessamy\",\n\"jessant\",\n\"jessed\",\n\"jessur\",\n\"jest\",\n\"jestee\",\n\"jester\",\n\"jestful\",\n\"jesting\",\n\"jet\",\n\"jetbead\",\n\"jete\",\n\"jetsam\",\n\"jettage\",\n\"jetted\",\n\"jetter\",\n\"jettied\",\n\"jetton\",\n\"jetty\",\n\"jetware\",\n\"jewbird\",\n\"jewbush\",\n\"jewel\",\n\"jeweler\",\n\"jewelry\",\n\"jewely\",\n\"jewfish\",\n\"jezail\",\n\"jeziah\",\n\"jharal\",\n\"jheel\",\n\"jhool\",\n\"jhow\",\n\"jib\",\n\"jibbah\",\n\"jibber\",\n\"jibby\",\n\"jibe\",\n\"jibhead\",\n\"jibi\",\n\"jibman\",\n\"jiboa\",\n\"jibstay\",\n\"jicama\",\n\"jicara\",\n\"jiff\",\n\"jiffle\",\n\"jiffy\",\n\"jig\",\n\"jigger\",\n\"jiggers\",\n\"jigget\",\n\"jiggety\",\n\"jiggish\",\n\"jiggle\",\n\"jiggly\",\n\"jiggy\",\n\"jiglike\",\n\"jigman\",\n\"jihad\",\n\"jikungu\",\n\"jillet\",\n\"jilt\",\n\"jiltee\",\n\"jilter\",\n\"jiltish\",\n\"jimbang\",\n\"jimjam\",\n\"jimmy\",\n\"jimp\",\n\"jimply\",\n\"jina\",\n\"jing\",\n\"jingal\",\n\"jingle\",\n\"jingled\",\n\"jingler\",\n\"jinglet\",\n\"jingly\",\n\"jingo\",\n\"jinja\",\n\"jinjili\",\n\"jink\",\n\"jinker\",\n\"jinket\",\n\"jinkle\",\n\"jinks\",\n\"jinn\",\n\"jinni\",\n\"jinny\",\n\"jinriki\",\n\"jinx\",\n\"jipper\",\n\"jiqui\",\n\"jirble\",\n\"jirga\",\n\"jiti\",\n\"jitneur\",\n\"jitney\",\n\"jitro\",\n\"jitter\",\n\"jitters\",\n\"jittery\",\n\"jiva\",\n\"jive\",\n\"jixie\",\n\"jo\",\n\"job\",\n\"jobade\",\n\"jobarbe\",\n\"jobber\",\n\"jobbery\",\n\"jobbet\",\n\"jobbing\",\n\"jobbish\",\n\"jobble\",\n\"jobless\",\n\"jobman\",\n\"jobo\",\n\"joch\",\n\"jock\",\n\"jocker\",\n\"jockey\",\n\"jocko\",\n\"jocoque\",\n\"jocose\",\n\"jocote\",\n\"jocu\",\n\"jocular\",\n\"jocum\",\n\"jocuma\",\n\"jocund\",\n\"jodel\",\n\"jodelr\",\n\"joe\",\n\"joebush\",\n\"joewood\",\n\"joey\",\n\"jog\",\n\"jogger\",\n\"joggle\",\n\"joggler\",\n\"joggly\",\n\"johnin\",\n\"join\",\n\"joinant\",\n\"joinder\",\n\"joiner\",\n\"joinery\",\n\"joining\",\n\"joint\",\n\"jointed\",\n\"jointer\",\n\"jointly\",\n\"jointy\",\n\"joist\",\n\"jojoba\",\n\"joke\",\n\"jokelet\",\n\"joker\",\n\"jokish\",\n\"jokist\",\n\"jokul\",\n\"joky\",\n\"joll\",\n\"jollier\",\n\"jollify\",\n\"jollily\",\n\"jollity\",\n\"jollop\",\n\"jolly\",\n\"jolt\",\n\"jolter\",\n\"jolting\",\n\"jolty\",\n\"jonque\",\n\"jonquil\",\n\"joola\",\n\"joom\",\n\"jordan\",\n\"joree\",\n\"jorum\",\n\"joseite\",\n\"josh\",\n\"josher\",\n\"joshi\",\n\"josie\",\n\"joskin\",\n\"joss\",\n\"josser\",\n\"jostle\",\n\"jostler\",\n\"jot\",\n\"jota\",\n\"jotisi\",\n\"jotter\",\n\"jotting\",\n\"jotty\",\n\"joubarb\",\n\"joug\",\n\"jough\",\n\"jouk\",\n\"joule\",\n\"joulean\",\n\"jounce\",\n\"journal\",\n\"journey\",\n\"jours\",\n\"joust\",\n\"jouster\",\n\"jovial\",\n\"jow\",\n\"jowar\",\n\"jowari\",\n\"jowel\",\n\"jower\",\n\"jowery\",\n\"jowl\",\n\"jowler\",\n\"jowlish\",\n\"jowlop\",\n\"jowly\",\n\"jowpy\",\n\"jowser\",\n\"jowter\",\n\"joy\",\n\"joyance\",\n\"joyancy\",\n\"joyant\",\n\"joyful\",\n\"joyhop\",\n\"joyleaf\",\n\"joyless\",\n\"joylet\",\n\"joyous\",\n\"joysome\",\n\"joyweed\",\n\"juba\",\n\"jubate\",\n\"jubbah\",\n\"jubbe\",\n\"jube\",\n\"jubilee\",\n\"jubilus\",\n\"juck\",\n\"juckies\",\n\"jud\",\n\"judcock\",\n\"judex\",\n\"judge\",\n\"judger\",\n\"judices\",\n\"judo\",\n\"jufti\",\n\"jug\",\n\"jugal\",\n\"jugale\",\n\"jugate\",\n\"jugated\",\n\"juger\",\n\"jugerum\",\n\"jugful\",\n\"jugger\",\n\"juggins\",\n\"juggle\",\n\"juggler\",\n\"juglone\",\n\"jugular\",\n\"jugulum\",\n\"jugum\",\n\"juice\",\n\"juicily\",\n\"juicy\",\n\"jujitsu\",\n\"juju\",\n\"jujube\",\n\"jujuism\",\n\"jujuist\",\n\"juke\",\n\"jukebox\",\n\"julep\",\n\"julid\",\n\"julidan\",\n\"julio\",\n\"juloid\",\n\"julole\",\n\"julolin\",\n\"jumart\",\n\"jumba\",\n\"jumble\",\n\"jumbler\",\n\"jumbly\",\n\"jumbo\",\n\"jumbuck\",\n\"jumby\",\n\"jumelle\",\n\"jument\",\n\"jumfru\",\n\"jumma\",\n\"jump\",\n\"jumper\",\n\"jumpy\",\n\"juncite\",\n\"juncous\",\n\"june\",\n\"jungle\",\n\"jungled\",\n\"jungli\",\n\"jungly\",\n\"juniata\",\n\"junior\",\n\"juniper\",\n\"junk\",\n\"junker\",\n\"junket\",\n\"junking\",\n\"junkman\",\n\"junt\",\n\"junta\",\n\"junto\",\n\"jupati\",\n\"jupe\",\n\"jupon\",\n\"jural\",\n\"jurally\",\n\"jurant\",\n\"jurara\",\n\"jurat\",\n\"jurator\",\n\"jure\",\n\"jurel\",\n\"juridic\",\n\"juring\",\n\"jurist\",\n\"juror\",\n\"jury\",\n\"juryman\",\n\"jussel\",\n\"jussion\",\n\"jussive\",\n\"jussory\",\n\"just\",\n\"justen\",\n\"justice\",\n\"justify\",\n\"justly\",\n\"justo\",\n\"jut\",\n\"jute\",\n\"jutka\",\n\"jutting\",\n\"jutty\",\n\"juvenal\",\n\"juvia\",\n\"juvite\",\n\"jyngine\",\n\"jynx\",\n\"k\",\n\"ka\",\n\"kabaya\",\n\"kabel\",\n\"kaberu\",\n\"kabiet\",\n\"kabuki\",\n\"kachin\",\n\"kadaya\",\n\"kadein\",\n\"kados\",\n\"kaffir\",\n\"kafir\",\n\"kafirin\",\n\"kafiz\",\n\"kafta\",\n\"kago\",\n\"kagu\",\n\"kaha\",\n\"kahar\",\n\"kahau\",\n\"kahili\",\n\"kahu\",\n\"kahuna\",\n\"kai\",\n\"kaid\",\n\"kaik\",\n\"kaikara\",\n\"kail\",\n\"kainga\",\n\"kainite\",\n\"kainsi\",\n\"kainyn\",\n\"kairine\",\n\"kaiser\",\n\"kaitaka\",\n\"kaiwi\",\n\"kajawah\",\n\"kaka\",\n\"kakapo\",\n\"kakar\",\n\"kaki\",\n\"kakkak\",\n\"kakke\",\n\"kala\",\n\"kalasie\",\n\"kale\",\n\"kalema\",\n\"kalends\",\n\"kali\",\n\"kalian\",\n\"kalium\",\n\"kallah\",\n\"kallege\",\n\"kalo\",\n\"kalon\",\n\"kalong\",\n\"kalpis\",\n\"kamahi\",\n\"kamala\",\n\"kamansi\",\n\"kamao\",\n\"kamas\",\n\"kamassi\",\n\"kambal\",\n\"kamboh\",\n\"kame\",\n\"kamerad\",\n\"kamias\",\n\"kamichi\",\n\"kamik\",\n\"kampong\",\n\"kan\",\n\"kana\",\n\"kanae\",\n\"kanagi\",\n\"kanap\",\n\"kanara\",\n\"kanari\",\n\"kanat\",\n\"kanchil\",\n\"kande\",\n\"kandol\",\n\"kaneh\",\n\"kang\",\n\"kanga\",\n\"kangani\",\n\"kankie\",\n\"kannume\",\n\"kanoon\",\n\"kans\",\n\"kantele\",\n\"kanten\",\n\"kaolin\",\n\"kapa\",\n\"kapai\",\n\"kapeika\",\n\"kapok\",\n\"kapp\",\n\"kappa\",\n\"kappe\",\n\"kapur\",\n\"kaput\",\n\"karagan\",\n\"karaka\",\n\"karakul\",\n\"karamu\",\n\"karaoke\",\n\"karate\",\n\"karaya\",\n\"karbi\",\n\"karch\",\n\"kareao\",\n\"kareeta\",\n\"karela\",\n\"karite\",\n\"karma\",\n\"karmic\",\n\"karo\",\n\"kaross\",\n\"karou\",\n\"karree\",\n\"karri\",\n\"karroo\",\n\"karsha\",\n\"karst\",\n\"karstic\",\n\"kartel\",\n\"kartos\",\n\"karwar\",\n\"karyon\",\n\"kasa\",\n\"kasbah\",\n\"kasbeke\",\n\"kasher\",\n\"kashga\",\n\"kashi\",\n\"kashima\",\n\"kasida\",\n\"kasm\",\n\"kassu\",\n\"kastura\",\n\"kat\",\n\"katar\",\n\"katcina\",\n\"kath\",\n\"katha\",\n\"kathal\",\n\"katipo\",\n\"katmon\",\n\"katogle\",\n\"katsup\",\n\"katuka\",\n\"katun\",\n\"katurai\",\n\"katydid\",\n\"kauri\",\n\"kava\",\n\"kavaic\",\n\"kavass\",\n\"kawaka\",\n\"kawika\",\n\"kay\",\n\"kayak\",\n\"kayaker\",\n\"kayles\",\n\"kayo\",\n\"kazi\",\n\"kazoo\",\n\"kea\",\n\"keach\",\n\"keacorn\",\n\"keawe\",\n\"keb\",\n\"kebab\",\n\"kebbie\",\n\"kebbuck\",\n\"kechel\",\n\"keck\",\n\"keckle\",\n\"kecksy\",\n\"kecky\",\n\"ked\",\n\"keddah\",\n\"kedge\",\n\"kedger\",\n\"kedlock\",\n\"keech\",\n\"keek\",\n\"keeker\",\n\"keel\",\n\"keelage\",\n\"keeled\",\n\"keeler\",\n\"keelfat\",\n\"keelie\",\n\"keeling\",\n\"keelman\",\n\"keelson\",\n\"keen\",\n\"keena\",\n\"keened\",\n\"keener\",\n\"keenly\",\n\"keep\",\n\"keeper\",\n\"keeping\",\n\"keest\",\n\"keet\",\n\"keeve\",\n\"kef\",\n\"keffel\",\n\"kefir\",\n\"kefiric\",\n\"keg\",\n\"kegler\",\n\"kehaya\",\n\"keita\",\n\"keitloa\",\n\"kekuna\",\n\"kelchin\",\n\"keld\",\n\"kele\",\n\"kelebe\",\n\"keleh\",\n\"kelek\",\n\"kelep\",\n\"kelk\",\n\"kell\",\n\"kella\",\n\"kellion\",\n\"kelly\",\n\"keloid\",\n\"kelp\",\n\"kelper\",\n\"kelpie\",\n\"kelpy\",\n\"kelt\",\n\"kelter\",\n\"kelty\",\n\"kelvin\",\n\"kemb\",\n\"kemp\",\n\"kempite\",\n\"kemple\",\n\"kempt\",\n\"kempy\",\n\"ken\",\n\"kenaf\",\n\"kenareh\",\n\"kench\",\n\"kend\",\n\"kendir\",\n\"kendyr\",\n\"kenlore\",\n\"kenmark\",\n\"kennel\",\n\"kenner\",\n\"kenning\",\n\"kenno\",\n\"keno\",\n\"kenosis\",\n\"kenotic\",\n\"kenspac\",\n\"kent\",\n\"kenyte\",\n\"kep\",\n\"kepi\",\n\"kept\",\n\"kerana\",\n\"kerasin\",\n\"kerat\",\n\"keratin\",\n\"keratto\",\n\"kerchoo\",\n\"kerchug\",\n\"kerel\",\n\"kerf\",\n\"kerflap\",\n\"kerflop\",\n\"kermes\",\n\"kermis\",\n\"kern\",\n\"kernel\",\n\"kerner\",\n\"kernish\",\n\"kernite\",\n\"kernos\",\n\"kerogen\",\n\"kerrie\",\n\"kerril\",\n\"kerrite\",\n\"kerry\",\n\"kersey\",\n\"kerslam\",\n\"kerugma\",\n\"kerwham\",\n\"kerygma\",\n\"kestrel\",\n\"ket\",\n\"keta\",\n\"ketal\",\n\"ketch\",\n\"ketchup\",\n\"keten\",\n\"ketene\",\n\"ketipic\",\n\"keto\",\n\"ketogen\",\n\"ketol\",\n\"ketole\",\n\"ketone\",\n\"ketonic\",\n\"ketose\",\n\"ketosis\",\n\"kette\",\n\"ketting\",\n\"kettle\",\n\"kettler\",\n\"ketty\",\n\"ketuba\",\n\"ketupa\",\n\"ketyl\",\n\"keup\",\n\"kevalin\",\n\"kevel\",\n\"kewpie\",\n\"kex\",\n\"kexy\",\n\"key\",\n\"keyage\",\n\"keyed\",\n\"keyhole\",\n\"keyless\",\n\"keylet\",\n\"keylock\",\n\"keynote\",\n\"keyway\",\n\"khaddar\",\n\"khadi\",\n\"khahoon\",\n\"khaiki\",\n\"khair\",\n\"khaja\",\n\"khajur\",\n\"khaki\",\n\"khakied\",\n\"khalifa\",\n\"khalsa\",\n\"khamsin\",\n\"khan\",\n\"khanate\",\n\"khanda\",\n\"khanjar\",\n\"khanjee\",\n\"khankah\",\n\"khanum\",\n\"khar\",\n\"kharaj\",\n\"kharua\",\n\"khass\",\n\"khat\",\n\"khatib\",\n\"khatri\",\n\"khediva\",\n\"khedive\",\n\"khepesh\",\n\"khet\",\n\"khilat\",\n\"khir\",\n\"khirka\",\n\"khoja\",\n\"khoka\",\n\"khot\",\n\"khu\",\n\"khubber\",\n\"khula\",\n\"khutbah\",\n\"khvat\",\n\"kiack\",\n\"kiaki\",\n\"kialee\",\n\"kiang\",\n\"kiaugh\",\n\"kibber\",\n\"kibble\",\n\"kibbler\",\n\"kibe\",\n\"kibei\",\n\"kibitka\",\n\"kibitz\",\n\"kiblah\",\n\"kibosh\",\n\"kiby\",\n\"kick\",\n\"kickee\",\n\"kicker\",\n\"kicking\",\n\"kickish\",\n\"kickoff\",\n\"kickout\",\n\"kickup\",\n\"kidder\",\n\"kiddier\",\n\"kiddish\",\n\"kiddush\",\n\"kiddy\",\n\"kidhood\",\n\"kidlet\",\n\"kidling\",\n\"kidnap\",\n\"kidney\",\n\"kidskin\",\n\"kidsman\",\n\"kiekie\",\n\"kiel\",\n\"kier\",\n\"kieye\",\n\"kikar\",\n\"kike\",\n\"kiki\",\n\"kiku\",\n\"kikuel\",\n\"kikumon\",\n\"kil\",\n\"kiladja\",\n\"kilah\",\n\"kilan\",\n\"kildee\",\n\"kileh\",\n\"kilerg\",\n\"kiley\",\n\"kilhig\",\n\"kiliare\",\n\"kilim\",\n\"kill\",\n\"killas\",\n\"killcu\",\n\"killeen\",\n\"killer\",\n\"killick\",\n\"killing\",\n\"killy\",\n\"kiln\",\n\"kilneye\",\n\"kilnman\",\n\"kilnrib\",\n\"kilo\",\n\"kilobar\",\n\"kiloton\",\n\"kilovar\",\n\"kilp\",\n\"kilt\",\n\"kilter\",\n\"kiltie\",\n\"kilting\",\n\"kim\",\n\"kimbang\",\n\"kimnel\",\n\"kimono\",\n\"kin\",\n\"kina\",\n\"kinah\",\n\"kinase\",\n\"kinbote\",\n\"kinch\",\n\"kinchin\",\n\"kincob\",\n\"kind\",\n\"kindle\",\n\"kindler\",\n\"kindly\",\n\"kindred\",\n\"kinepox\",\n\"kinesic\",\n\"kinesis\",\n\"kinetic\",\n\"king\",\n\"kingcob\",\n\"kingcup\",\n\"kingdom\",\n\"kinglet\",\n\"kingly\",\n\"kingpin\",\n\"kingrow\",\n\"kink\",\n\"kinkhab\",\n\"kinkily\",\n\"kinkle\",\n\"kinkled\",\n\"kinkly\",\n\"kinky\",\n\"kinless\",\n\"kino\",\n\"kinship\",\n\"kinsman\",\n\"kintar\",\n\"kioea\",\n\"kiosk\",\n\"kiotome\",\n\"kip\",\n\"kipage\",\n\"kipe\",\n\"kippeen\",\n\"kipper\",\n\"kippy\",\n\"kipsey\",\n\"kipskin\",\n\"kiri\",\n\"kirimon\",\n\"kirk\",\n\"kirker\",\n\"kirkify\",\n\"kirking\",\n\"kirkman\",\n\"kirmew\",\n\"kirn\",\n\"kirombo\",\n\"kirsch\",\n\"kirtle\",\n\"kirtled\",\n\"kirve\",\n\"kirver\",\n\"kischen\",\n\"kish\",\n\"kishen\",\n\"kishon\",\n\"kishy\",\n\"kismet\",\n\"kisra\",\n\"kiss\",\n\"kissage\",\n\"kissar\",\n\"kisser\",\n\"kissing\",\n\"kissy\",\n\"kist\",\n\"kistful\",\n\"kiswa\",\n\"kit\",\n\"kitab\",\n\"kitabis\",\n\"kitar\",\n\"kitcat\",\n\"kitchen\",\n\"kite\",\n\"kith\",\n\"kithe\",\n\"kitish\",\n\"kitling\",\n\"kittel\",\n\"kitten\",\n\"kitter\",\n\"kittle\",\n\"kittles\",\n\"kittly\",\n\"kittock\",\n\"kittul\",\n\"kitty\",\n\"kiva\",\n\"kiver\",\n\"kivu\",\n\"kiwi\",\n\"kiyas\",\n\"kiyi\",\n\"klafter\",\n\"klam\",\n\"klavern\",\n\"klaxon\",\n\"klepht\",\n\"kleptic\",\n\"klicket\",\n\"klip\",\n\"klipbok\",\n\"klipdas\",\n\"klippe\",\n\"klippen\",\n\"klister\",\n\"klom\",\n\"klop\",\n\"klops\",\n\"klosh\",\n\"kmet\",\n\"knab\",\n\"knabble\",\n\"knack\",\n\"knacker\",\n\"knacky\",\n\"knag\",\n\"knagged\",\n\"knaggy\",\n\"knap\",\n\"knape\",\n\"knappan\",\n\"knapper\",\n\"knar\",\n\"knark\",\n\"knarred\",\n\"knarry\",\n\"knave\",\n\"knavery\",\n\"knavess\",\n\"knavish\",\n\"knawel\",\n\"knead\",\n\"kneader\",\n\"knee\",\n\"kneecap\",\n\"kneed\",\n\"kneel\",\n\"kneeler\",\n\"kneelet\",\n\"kneepad\",\n\"kneepan\",\n\"knell\",\n\"knelt\",\n\"knet\",\n\"knew\",\n\"knez\",\n\"knezi\",\n\"kniaz\",\n\"kniazi\",\n\"knick\",\n\"knicker\",\n\"knife\",\n\"knifer\",\n\"knight\",\n\"knit\",\n\"knitch\",\n\"knitted\",\n\"knitter\",\n\"knittle\",\n\"knived\",\n\"knivey\",\n\"knob\",\n\"knobbed\",\n\"knobber\",\n\"knobble\",\n\"knobbly\",\n\"knobby\",\n\"knock\",\n\"knocker\",\n\"knockup\",\n\"knoll\",\n\"knoller\",\n\"knolly\",\n\"knop\",\n\"knopite\",\n\"knopped\",\n\"knopper\",\n\"knoppy\",\n\"knosp\",\n\"knosped\",\n\"knot\",\n\"knotted\",\n\"knotter\",\n\"knotty\",\n\"knout\",\n\"know\",\n\"knowe\",\n\"knower\",\n\"knowing\",\n\"known\",\n\"knub\",\n\"knubbly\",\n\"knubby\",\n\"knublet\",\n\"knuckle\",\n\"knuckly\",\n\"knur\",\n\"knurl\",\n\"knurled\",\n\"knurly\",\n\"knut\",\n\"knutty\",\n\"knyaz\",\n\"knyazi\",\n\"ko\",\n\"koa\",\n\"koae\",\n\"koala\",\n\"koali\",\n\"kob\",\n\"koban\",\n\"kobi\",\n\"kobird\",\n\"kobold\",\n\"kobong\",\n\"kobu\",\n\"koda\",\n\"kodak\",\n\"kodaker\",\n\"kodakry\",\n\"kodro\",\n\"koel\",\n\"koff\",\n\"koft\",\n\"koftgar\",\n\"kohemp\",\n\"kohl\",\n\"kohua\",\n\"koi\",\n\"koil\",\n\"koila\",\n\"koilon\",\n\"koine\",\n\"koinon\",\n\"kojang\",\n\"kokako\",\n\"kokam\",\n\"kokan\",\n\"kokil\",\n\"kokio\",\n\"koklas\",\n\"koklass\",\n\"koko\",\n\"kokoon\",\n\"kokowai\",\n\"kokra\",\n\"koku\",\n\"kokum\",\n\"kokumin\",\n\"kola\",\n\"kolach\",\n\"kolea\",\n\"kolhoz\",\n\"kolkhos\",\n\"kolkhoz\",\n\"kollast\",\n\"koller\",\n\"kolo\",\n\"kolobus\",\n\"kolsun\",\n\"komatik\",\n\"kombu\",\n\"kommos\",\n\"kompeni\",\n\"kon\",\n\"kona\",\n\"konak\",\n\"kongoni\",\n\"kongu\",\n\"konini\",\n\"konjak\",\n\"kooka\",\n\"kookery\",\n\"kookri\",\n\"koolah\",\n\"koombar\",\n\"koomkie\",\n\"kootcha\",\n\"kop\",\n\"kopeck\",\n\"koph\",\n\"kopi\",\n\"koppa\",\n\"koppen\",\n\"koppite\",\n\"kor\",\n\"kora\",\n\"koradji\",\n\"korait\",\n\"korakan\",\n\"korari\",\n\"kore\",\n\"korec\",\n\"koreci\",\n\"korero\",\n\"kori\",\n\"korin\",\n\"korona\",\n\"korova\",\n\"korrel\",\n\"koruna\",\n\"korzec\",\n\"kos\",\n\"kosher\",\n\"kosin\",\n\"kosong\",\n\"koswite\",\n\"kotal\",\n\"koto\",\n\"kotuku\",\n\"kotwal\",\n\"kotyle\",\n\"kotylos\",\n\"kou\",\n\"koulan\",\n\"kouza\",\n\"kovil\",\n\"kowhai\",\n\"kowtow\",\n\"koyan\",\n\"kozo\",\n\"kra\",\n\"kraal\",\n\"kraft\",\n\"krait\",\n\"kraken\",\n\"kral\",\n\"krama\",\n\"kran\",\n\"kras\",\n\"krasis\",\n\"krausen\",\n\"kraut\",\n\"kreis\",\n\"krelos\",\n\"kremlin\",\n\"krems\",\n\"kreng\",\n\"krieker\",\n\"krimmer\",\n\"krina\",\n\"krocket\",\n\"krome\",\n\"krona\",\n\"krone\",\n\"kronen\",\n\"kroner\",\n\"kronor\",\n\"kronur\",\n\"kroon\",\n\"krosa\",\n\"krypsis\",\n\"kryptic\",\n\"kryptol\",\n\"krypton\",\n\"kuan\",\n\"kuba\",\n\"kubba\",\n\"kuchen\",\n\"kudize\",\n\"kudos\",\n\"kudu\",\n\"kudzu\",\n\"kuei\",\n\"kuge\",\n\"kugel\",\n\"kuichua\",\n\"kukri\",\n\"kuku\",\n\"kukui\",\n\"kukupa\",\n\"kula\",\n\"kulack\",\n\"kulah\",\n\"kulaite\",\n\"kulak\",\n\"kulang\",\n\"kulimit\",\n\"kulm\",\n\"kulmet\",\n\"kumbi\",\n\"kumhar\",\n\"kumiss\",\n\"kummel\",\n\"kumquat\",\n\"kumrah\",\n\"kunai\",\n\"kung\",\n\"kunk\",\n\"kunkur\",\n\"kunzite\",\n\"kuphar\",\n\"kupper\",\n\"kurbash\",\n\"kurgan\",\n\"kuruma\",\n\"kurung\",\n\"kurus\",\n\"kurvey\",\n\"kusa\",\n\"kusam\",\n\"kusha\",\n\"kuskite\",\n\"kuskos\",\n\"kuskus\",\n\"kusti\",\n\"kusum\",\n\"kutcha\",\n\"kuttab\",\n\"kuttar\",\n\"kuttaur\",\n\"kuvasz\",\n\"kvass\",\n\"kvint\",\n\"kvinter\",\n\"kwamme\",\n\"kwan\",\n\"kwarta\",\n\"kwazoku\",\n\"kyack\",\n\"kyah\",\n\"kyar\",\n\"kyat\",\n\"kyaung\",\n\"kyl\",\n\"kyle\",\n\"kylite\",\n\"kylix\",\n\"kyrine\",\n\"kyte\",\n\"l\",\n\"la\",\n\"laager\",\n\"laang\",\n\"lab\",\n\"labara\",\n\"labarum\",\n\"labba\",\n\"labber\",\n\"labefy\",\n\"label\",\n\"labeler\",\n\"labella\",\n\"labia\",\n\"labial\",\n\"labiate\",\n\"labile\",\n\"labiose\",\n\"labis\",\n\"labium\",\n\"lablab\",\n\"labor\",\n\"labored\",\n\"laborer\",\n\"labour\",\n\"labra\",\n\"labral\",\n\"labret\",\n\"labroid\",\n\"labrose\",\n\"labrum\",\n\"labrys\",\n\"lac\",\n\"lacca\",\n\"laccaic\",\n\"laccase\",\n\"laccol\",\n\"lace\",\n\"laced\",\n\"laceman\",\n\"lacepod\",\n\"lacer\",\n\"lacery\",\n\"lacet\",\n\"lache\",\n\"laches\",\n\"lachsa\",\n\"lacily\",\n\"lacing\",\n\"lacinia\",\n\"lacis\",\n\"lack\",\n\"lacker\",\n\"lackey\",\n\"lackwit\",\n\"lacmoid\",\n\"lacmus\",\n\"laconic\",\n\"lacquer\",\n\"lacrym\",\n\"lactam\",\n\"lactant\",\n\"lactary\",\n\"lactase\",\n\"lactate\",\n\"lacteal\",\n\"lactean\",\n\"lactic\",\n\"lactid\",\n\"lactide\",\n\"lactify\",\n\"lactim\",\n\"lacto\",\n\"lactoid\",\n\"lactol\",\n\"lactone\",\n\"lactose\",\n\"lactyl\",\n\"lacuna\",\n\"lacunae\",\n\"lacunal\",\n\"lacunar\",\n\"lacune\",\n\"lacwork\",\n\"lacy\",\n\"lad\",\n\"ladakin\",\n\"ladanum\",\n\"ladder\",\n\"laddery\",\n\"laddess\",\n\"laddie\",\n\"laddish\",\n\"laddock\",\n\"lade\",\n\"lademan\",\n\"laden\",\n\"lader\",\n\"ladhood\",\n\"ladies\",\n\"ladify\",\n\"lading\",\n\"ladkin\",\n\"ladle\",\n\"ladler\",\n\"ladrone\",\n\"lady\",\n\"ladybug\",\n\"ladydom\",\n\"ladyfly\",\n\"ladyfy\",\n\"ladyish\",\n\"ladyism\",\n\"ladykin\",\n\"ladyly\",\n\"laet\",\n\"laeti\",\n\"laetic\",\n\"lag\",\n\"lagan\",\n\"lagarto\",\n\"lagen\",\n\"lagena\",\n\"lagend\",\n\"lager\",\n\"lagetto\",\n\"laggar\",\n\"laggard\",\n\"lagged\",\n\"laggen\",\n\"lagger\",\n\"laggin\",\n\"lagging\",\n\"laglast\",\n\"lagna\",\n\"lagoon\",\n\"lagwort\",\n\"lai\",\n\"laic\",\n\"laical\",\n\"laich\",\n\"laicism\",\n\"laicity\",\n\"laicize\",\n\"laid\",\n\"laigh\",\n\"lain\",\n\"laine\",\n\"laiose\",\n\"lair\",\n\"lairage\",\n\"laird\",\n\"lairdie\",\n\"lairdly\",\n\"lairman\",\n\"lairy\",\n\"laity\",\n\"lak\",\n\"lakatoi\",\n\"lake\",\n\"lakelet\",\n\"laker\",\n\"lakie\",\n\"laking\",\n\"lakish\",\n\"lakism\",\n\"lakist\",\n\"laky\",\n\"lalang\",\n\"lall\",\n\"lalling\",\n\"lalo\",\n\"lam\",\n\"lama\",\n\"lamaic\",\n\"lamany\",\n\"lamb\",\n\"lamba\",\n\"lambale\",\n\"lambda\",\n\"lambeau\",\n\"lambent\",\n\"lamber\",\n\"lambert\",\n\"lambie\",\n\"lambish\",\n\"lambkin\",\n\"lambly\",\n\"lamboys\",\n\"lamby\",\n\"lame\",\n\"lamedh\",\n\"lamel\",\n\"lamella\",\n\"lamely\",\n\"lament\",\n\"lameter\",\n\"lametta\",\n\"lamia\",\n\"lamiger\",\n\"lamiid\",\n\"lamin\",\n\"lamina\",\n\"laminae\",\n\"laminar\",\n\"lamish\",\n\"lamiter\",\n\"lammas\",\n\"lammer\",\n\"lammock\",\n\"lammy\",\n\"lamnid\",\n\"lamnoid\",\n\"lamp\",\n\"lampad\",\n\"lampas\",\n\"lamper\",\n\"lampern\",\n\"lampers\",\n\"lampfly\",\n\"lampful\",\n\"lamping\",\n\"lampion\",\n\"lampist\",\n\"lamplet\",\n\"lamplit\",\n\"lampman\",\n\"lampoon\",\n\"lamprey\",\n\"lan\",\n\"lanas\",\n\"lanate\",\n\"lanated\",\n\"lanaz\",\n\"lance\",\n\"lanced\",\n\"lancely\",\n\"lancer\",\n\"lances\",\n\"lancet\",\n\"lancha\",\n\"land\",\n\"landau\",\n\"landed\",\n\"lander\",\n\"landing\",\n\"landman\",\n\"landmil\",\n\"lane\",\n\"lanete\",\n\"laneway\",\n\"laney\",\n\"langaha\",\n\"langca\",\n\"langi\",\n\"langite\",\n\"langle\",\n\"langoon\",\n\"langsat\",\n\"langued\",\n\"languet\",\n\"languid\",\n\"languor\",\n\"langur\",\n\"laniary\",\n\"laniate\",\n\"lanific\",\n\"lanioid\",\n\"lanista\",\n\"lank\",\n\"lanket\",\n\"lankily\",\n\"lankish\",\n\"lankly\",\n\"lanky\",\n\"lanner\",\n\"lanolin\",\n\"lanose\",\n\"lansat\",\n\"lanseh\",\n\"lanson\",\n\"lant\",\n\"lantaca\",\n\"lantern\",\n\"lantum\",\n\"lanugo\",\n\"lanum\",\n\"lanx\",\n\"lanyard\",\n\"lap\",\n\"lapacho\",\n\"lapcock\",\n\"lapel\",\n\"lapeler\",\n\"lapful\",\n\"lapillo\",\n\"lapon\",\n\"lappage\",\n\"lapped\",\n\"lapper\",\n\"lappet\",\n\"lapping\",\n\"lapse\",\n\"lapsed\",\n\"lapser\",\n\"lapsi\",\n\"lapsing\",\n\"lapwing\",\n\"lapwork\",\n\"laquear\",\n\"laqueus\",\n\"lar\",\n\"larceny\",\n\"larch\",\n\"larchen\",\n\"lard\",\n\"larder\",\n\"lardite\",\n\"lardon\",\n\"lardy\",\n\"large\",\n\"largely\",\n\"largen\",\n\"largess\",\n\"largish\",\n\"largo\",\n\"lari\",\n\"lariat\",\n\"larick\",\n\"larid\",\n\"larigo\",\n\"larigot\",\n\"lariid\",\n\"larin\",\n\"larine\",\n\"larixin\",\n\"lark\",\n\"larker\",\n\"larking\",\n\"larkish\",\n\"larky\",\n\"larmier\",\n\"larnax\",\n\"laroid\",\n\"larrup\",\n\"larry\",\n\"larva\",\n\"larvae\",\n\"larval\",\n\"larvate\",\n\"larve\",\n\"larvule\",\n\"larynx\",\n\"las\",\n\"lasa\",\n\"lascar\",\n\"laser\",\n\"lash\",\n\"lasher\",\n\"lask\",\n\"lasket\",\n\"lasque\",\n\"lass\",\n\"lasset\",\n\"lassie\",\n\"lasso\",\n\"lassock\",\n\"lassoer\",\n\"last\",\n\"lastage\",\n\"laster\",\n\"lasting\",\n\"lastly\",\n\"lastre\",\n\"lasty\",\n\"lat\",\n\"lata\",\n\"latah\",\n\"latch\",\n\"latcher\",\n\"latchet\",\n\"late\",\n\"latebra\",\n\"lated\",\n\"lateen\",\n\"lately\",\n\"laten\",\n\"latence\",\n\"latency\",\n\"latent\",\n\"later\",\n\"latera\",\n\"laterad\",\n\"lateral\",\n\"latest\",\n\"latex\",\n\"lath\",\n\"lathe\",\n\"lathee\",\n\"lathen\",\n\"lather\",\n\"lathery\",\n\"lathing\",\n\"lathy\",\n\"latices\",\n\"latigo\",\n\"lation\",\n\"latish\",\n\"latitat\",\n\"latite\",\n\"latomy\",\n\"latrant\",\n\"latria\",\n\"latrine\",\n\"latro\",\n\"latrobe\",\n\"latron\",\n\"latten\",\n\"latter\",\n\"lattice\",\n\"latus\",\n\"lauan\",\n\"laud\",\n\"lauder\",\n\"laudist\",\n\"laugh\",\n\"laughee\",\n\"laugher\",\n\"laughy\",\n\"lauia\",\n\"laun\",\n\"launce\",\n\"launch\",\n\"laund\",\n\"launder\",\n\"laundry\",\n\"laur\",\n\"laura\",\n\"laurate\",\n\"laurel\",\n\"lauric\",\n\"laurin\",\n\"laurite\",\n\"laurone\",\n\"lauryl\",\n\"lava\",\n\"lavable\",\n\"lavabo\",\n\"lavacre\",\n\"lavage\",\n\"lavanga\",\n\"lavant\",\n\"lavaret\",\n\"lavatic\",\n\"lave\",\n\"laveer\",\n\"laver\",\n\"lavic\",\n\"lavish\",\n\"lavolta\",\n\"law\",\n\"lawbook\",\n\"lawful\",\n\"lawing\",\n\"lawish\",\n\"lawk\",\n\"lawless\",\n\"lawlike\",\n\"lawman\",\n\"lawn\",\n\"lawned\",\n\"lawner\",\n\"lawnlet\",\n\"lawny\",\n\"lawsuit\",\n\"lawter\",\n\"lawyer\",\n\"lawyery\",\n\"lawzy\",\n\"lax\",\n\"laxate\",\n\"laxism\",\n\"laxist\",\n\"laxity\",\n\"laxly\",\n\"laxness\",\n\"lay\",\n\"layaway\",\n\"layback\",\n\"layboy\",\n\"layer\",\n\"layered\",\n\"layery\",\n\"layette\",\n\"laying\",\n\"layland\",\n\"layman\",\n\"layne\",\n\"layoff\",\n\"layout\",\n\"layover\",\n\"layship\",\n\"laystow\",\n\"lazar\",\n\"lazaret\",\n\"lazarly\",\n\"laze\",\n\"lazily\",\n\"lazule\",\n\"lazuli\",\n\"lazy\",\n\"lazyish\",\n\"lea\",\n\"leach\",\n\"leacher\",\n\"leachy\",\n\"lead\",\n\"leadage\",\n\"leaded\",\n\"leaden\",\n\"leader\",\n\"leadin\",\n\"leading\",\n\"leadman\",\n\"leadoff\",\n\"leadout\",\n\"leadway\",\n\"leady\",\n\"leaf\",\n\"leafage\",\n\"leafboy\",\n\"leafcup\",\n\"leafdom\",\n\"leafed\",\n\"leafen\",\n\"leafer\",\n\"leafery\",\n\"leafit\",\n\"leaflet\",\n\"leafy\",\n\"league\",\n\"leaguer\",\n\"leak\",\n\"leakage\",\n\"leaker\",\n\"leaky\",\n\"leal\",\n\"lealand\",\n\"leally\",\n\"lealty\",\n\"leam\",\n\"leamer\",\n\"lean\",\n\"leaner\",\n\"leaning\",\n\"leanish\",\n\"leanly\",\n\"leant\",\n\"leap\",\n\"leaper\",\n\"leaping\",\n\"leapt\",\n\"lear\",\n\"learn\",\n\"learned\",\n\"learner\",\n\"learnt\",\n\"lease\",\n\"leaser\",\n\"leash\",\n\"leasing\",\n\"leasow\",\n\"least\",\n\"leat\",\n\"leath\",\n\"leather\",\n\"leatman\",\n\"leave\",\n\"leaved\",\n\"leaven\",\n\"leaver\",\n\"leaves\",\n\"leaving\",\n\"leavy\",\n\"leawill\",\n\"leban\",\n\"lebbek\",\n\"lecama\",\n\"lech\",\n\"lecher\",\n\"lechery\",\n\"lechwe\",\n\"leck\",\n\"lecker\",\n\"lectern\",\n\"lection\",\n\"lector\",\n\"lectual\",\n\"lecture\",\n\"lecyth\",\n\"led\",\n\"lede\",\n\"leden\",\n\"ledge\",\n\"ledged\",\n\"ledger\",\n\"ledging\",\n\"ledgy\",\n\"ledol\",\n\"lee\",\n\"leech\",\n\"leecher\",\n\"leeches\",\n\"leed\",\n\"leefang\",\n\"leek\",\n\"leekish\",\n\"leeky\",\n\"leep\",\n\"leepit\",\n\"leer\",\n\"leerily\",\n\"leerish\",\n\"leery\",\n\"lees\",\n\"leet\",\n\"leetman\",\n\"leewan\",\n\"leeward\",\n\"leeway\",\n\"leewill\",\n\"left\",\n\"leftish\",\n\"leftism\",\n\"leftist\",\n\"leg\",\n\"legacy\",\n\"legal\",\n\"legally\",\n\"legate\",\n\"legatee\",\n\"legato\",\n\"legator\",\n\"legend\",\n\"legenda\",\n\"leger\",\n\"leges\",\n\"legged\",\n\"legger\",\n\"legging\",\n\"leggy\",\n\"leghorn\",\n\"legible\",\n\"legibly\",\n\"legific\",\n\"legion\",\n\"legist\",\n\"legit\",\n\"legitim\",\n\"leglen\",\n\"legless\",\n\"leglet\",\n\"leglike\",\n\"legman\",\n\"legoa\",\n\"legpull\",\n\"legrope\",\n\"legua\",\n\"leguan\",\n\"legume\",\n\"legumen\",\n\"legumin\",\n\"lehr\",\n\"lehrman\",\n\"lehua\",\n\"lei\",\n\"leister\",\n\"leisure\",\n\"lek\",\n\"lekach\",\n\"lekane\",\n\"lekha\",\n\"leman\",\n\"lemel\",\n\"lemma\",\n\"lemmata\",\n\"lemming\",\n\"lemnad\",\n\"lemon\",\n\"lemony\",\n\"lempira\",\n\"lemur\",\n\"lemures\",\n\"lemurid\",\n\"lenad\",\n\"lenard\",\n\"lench\",\n\"lend\",\n\"lendee\",\n\"lender\",\n\"lene\",\n\"length\",\n\"lengthy\",\n\"lenient\",\n\"lenify\",\n\"lenis\",\n\"lenitic\",\n\"lenity\",\n\"lennow\",\n\"leno\",\n\"lens\",\n\"lensed\",\n\"lent\",\n\"lenth\",\n\"lentigo\",\n\"lentil\",\n\"lentisc\",\n\"lentisk\",\n\"lento\",\n\"lentoid\",\n\"lentor\",\n\"lentous\",\n\"lenvoi\",\n\"lenvoy\",\n\"leonine\",\n\"leonite\",\n\"leopard\",\n\"leotard\",\n\"lepa\",\n\"leper\",\n\"lepered\",\n\"leporid\",\n\"lepra\",\n\"lepric\",\n\"leproid\",\n\"leproma\",\n\"leprose\",\n\"leprosy\",\n\"leprous\",\n\"leptid\",\n\"leptite\",\n\"leptome\",\n\"lepton\",\n\"leptus\",\n\"lerot\",\n\"lerp\",\n\"lerret\",\n\"lesche\",\n\"lesion\",\n\"lesiy\",\n\"less\",\n\"lessee\",\n\"lessen\",\n\"lesser\",\n\"lessive\",\n\"lessn\",\n\"lesson\",\n\"lessor\",\n\"lest\",\n\"lestrad\",\n\"let\",\n\"letch\",\n\"letchy\",\n\"letdown\",\n\"lete\",\n\"lethal\",\n\"letoff\",\n\"letten\",\n\"letter\",\n\"lettrin\",\n\"lettuce\",\n\"letup\",\n\"leu\",\n\"leuch\",\n\"leucine\",\n\"leucism\",\n\"leucite\",\n\"leuco\",\n\"leucoid\",\n\"leucoma\",\n\"leucon\",\n\"leucous\",\n\"leucyl\",\n\"leud\",\n\"leuk\",\n\"leuma\",\n\"lev\",\n\"levance\",\n\"levant\",\n\"levator\",\n\"levee\",\n\"level\",\n\"leveler\",\n\"levelly\",\n\"lever\",\n\"leverer\",\n\"leveret\",\n\"levers\",\n\"levier\",\n\"levin\",\n\"levir\",\n\"levity\",\n\"levo\",\n\"levulic\",\n\"levulin\",\n\"levy\",\n\"levyist\",\n\"lew\",\n\"lewd\",\n\"lewdly\",\n\"lewis\",\n\"lewth\",\n\"lexia\",\n\"lexical\",\n\"lexicon\",\n\"ley\",\n\"leyland\",\n\"leysing\",\n\"li\",\n\"liable\",\n\"liaison\",\n\"liana\",\n\"liang\",\n\"liar\",\n\"liard\",\n\"libant\",\n\"libate\",\n\"libber\",\n\"libbet\",\n\"libbra\",\n\"libel\",\n\"libelee\",\n\"libeler\",\n\"liber\",\n\"liberal\",\n\"liberty\",\n\"libido\",\n\"libken\",\n\"libra\",\n\"libral\",\n\"library\",\n\"librate\",\n\"licca\",\n\"license\",\n\"lich\",\n\"licham\",\n\"lichen\",\n\"licheny\",\n\"lichi\",\n\"licit\",\n\"licitly\",\n\"lick\",\n\"licker\",\n\"licking\",\n\"licorn\",\n\"licorne\",\n\"lictor\",\n\"lid\",\n\"lidded\",\n\"lidder\",\n\"lidgate\",\n\"lidless\",\n\"lie\",\n\"lied\",\n\"lief\",\n\"liege\",\n\"liegely\",\n\"lieger\",\n\"lien\",\n\"lienal\",\n\"lienee\",\n\"lienic\",\n\"lienor\",\n\"lier\",\n\"lierne\",\n\"lierre\",\n\"liesh\",\n\"lieu\",\n\"lieue\",\n\"lieve\",\n\"life\",\n\"lifeday\",\n\"lifeful\",\n\"lifelet\",\n\"lifer\",\n\"lifey\",\n\"lifo\",\n\"lift\",\n\"lifter\",\n\"lifting\",\n\"liftman\",\n\"ligable\",\n\"ligas\",\n\"ligate\",\n\"ligator\",\n\"ligger\",\n\"light\",\n\"lighten\",\n\"lighter\",\n\"lightly\",\n\"ligne\",\n\"lignify\",\n\"lignin\",\n\"lignite\",\n\"lignone\",\n\"lignose\",\n\"lignum\",\n\"ligula\",\n\"ligular\",\n\"ligule\",\n\"ligulin\",\n\"ligure\",\n\"liin\",\n\"lija\",\n\"likable\",\n\"like\",\n\"likely\",\n\"liken\",\n\"liker\",\n\"likin\",\n\"liking\",\n\"liknon\",\n\"lilac\",\n\"lilacin\",\n\"lilacky\",\n\"lile\",\n\"lilied\",\n\"lill\",\n\"lilt\",\n\"lily\",\n\"lilyfy\",\n\"lim\",\n\"limacel\",\n\"limacon\",\n\"liman\",\n\"limb\",\n\"limbal\",\n\"limbat\",\n\"limbate\",\n\"limbeck\",\n\"limbed\",\n\"limber\",\n\"limbers\",\n\"limbic\",\n\"limbie\",\n\"limbo\",\n\"limbous\",\n\"limbus\",\n\"limby\",\n\"lime\",\n\"limeade\",\n\"limeman\",\n\"limen\",\n\"limer\",\n\"limes\",\n\"limetta\",\n\"limey\",\n\"liminal\",\n\"liming\",\n\"limit\",\n\"limital\",\n\"limited\",\n\"limiter\",\n\"limma\",\n\"limmer\",\n\"limmock\",\n\"limmu\",\n\"limn\",\n\"limner\",\n\"limnery\",\n\"limniad\",\n\"limnite\",\n\"limoid\",\n\"limonin\",\n\"limose\",\n\"limous\",\n\"limp\",\n\"limper\",\n\"limpet\",\n\"limpid\",\n\"limpily\",\n\"limpin\",\n\"limping\",\n\"limpish\",\n\"limpkin\",\n\"limply\",\n\"limpsy\",\n\"limpy\",\n\"limsy\",\n\"limu\",\n\"limulid\",\n\"limy\",\n\"lin\",\n\"lina\",\n\"linable\",\n\"linaga\",\n\"linage\",\n\"linaloa\",\n\"linalol\",\n\"linch\",\n\"linchet\",\n\"linctus\",\n\"lindane\",\n\"linden\",\n\"linder\",\n\"lindo\",\n\"line\",\n\"linea\",\n\"lineage\",\n\"lineal\",\n\"linear\",\n\"lineate\",\n\"linecut\",\n\"lined\",\n\"linelet\",\n\"lineman\",\n\"linen\",\n\"liner\",\n\"ling\",\n\"linga\",\n\"linge\",\n\"lingel\",\n\"linger\",\n\"lingo\",\n\"lingtow\",\n\"lingua\",\n\"lingual\",\n\"linguet\",\n\"lingula\",\n\"lingy\",\n\"linha\",\n\"linhay\",\n\"linie\",\n\"linin\",\n\"lining\",\n\"linitis\",\n\"liniya\",\n\"linja\",\n\"linje\",\n\"link\",\n\"linkage\",\n\"linkboy\",\n\"linked\",\n\"linker\",\n\"linking\",\n\"linkman\",\n\"links\",\n\"linky\",\n\"linn\",\n\"linnet\",\n\"lino\",\n\"linolic\",\n\"linolin\",\n\"linon\",\n\"linous\",\n\"linoxin\",\n\"linoxyn\",\n\"linpin\",\n\"linseed\",\n\"linsey\",\n\"lint\",\n\"lintel\",\n\"linten\",\n\"linter\",\n\"lintern\",\n\"lintie\",\n\"linty\",\n\"linwood\",\n\"liny\",\n\"lion\",\n\"lioncel\",\n\"lionel\",\n\"lioness\",\n\"lionet\",\n\"lionism\",\n\"lionize\",\n\"lionly\",\n\"lip\",\n\"lipa\",\n\"liparid\",\n\"lipase\",\n\"lipemia\",\n\"lipide\",\n\"lipin\",\n\"lipless\",\n\"liplet\",\n\"liplike\",\n\"lipoid\",\n\"lipoma\",\n\"lipopod\",\n\"liposis\",\n\"lipped\",\n\"lippen\",\n\"lipper\",\n\"lipping\",\n\"lippy\",\n\"lipuria\",\n\"lipwork\",\n\"liquate\",\n\"liquefy\",\n\"liqueur\",\n\"liquid\",\n\"liquidy\",\n\"liquor\",\n\"lira\",\n\"lirate\",\n\"lire\",\n\"lirella\",\n\"lis\",\n\"lisere\",\n\"lish\",\n\"lisk\",\n\"lisle\",\n\"lisp\",\n\"lisper\",\n\"lispund\",\n\"liss\",\n\"lissom\",\n\"lissome\",\n\"list\",\n\"listed\",\n\"listel\",\n\"listen\",\n\"lister\",\n\"listing\",\n\"listred\",\n\"lit\",\n\"litany\",\n\"litas\",\n\"litch\",\n\"litchi\",\n\"lite\",\n\"liter\",\n\"literal\",\n\"lith\",\n\"lithe\",\n\"lithely\",\n\"lithi\",\n\"lithia\",\n\"lithic\",\n\"lithify\",\n\"lithite\",\n\"lithium\",\n\"litho\",\n\"lithoid\",\n\"lithous\",\n\"lithy\",\n\"litmus\",\n\"litotes\",\n\"litra\",\n\"litster\",\n\"litten\",\n\"litter\",\n\"littery\",\n\"little\",\n\"lituite\",\n\"liturgy\",\n\"litus\",\n\"lituus\",\n\"litz\",\n\"livable\",\n\"live\",\n\"lived\",\n\"livedo\",\n\"lively\",\n\"liven\",\n\"liver\",\n\"livered\",\n\"livery\",\n\"livid\",\n\"lividly\",\n\"livier\",\n\"living\",\n\"livor\",\n\"livre\",\n\"liwan\",\n\"lixive\",\n\"lizard\",\n\"llama\",\n\"llano\",\n\"llautu\",\n\"llyn\",\n\"lo\",\n\"loa\",\n\"loach\",\n\"load\",\n\"loadage\",\n\"loaded\",\n\"loaden\",\n\"loader\",\n\"loading\",\n\"loaf\",\n\"loafer\",\n\"loafing\",\n\"loaflet\",\n\"loam\",\n\"loamily\",\n\"loaming\",\n\"loamy\",\n\"loan\",\n\"loaner\",\n\"loanin\",\n\"loath\",\n\"loathe\",\n\"loather\",\n\"loathly\",\n\"loave\",\n\"lob\",\n\"lobal\",\n\"lobar\",\n\"lobate\",\n\"lobated\",\n\"lobber\",\n\"lobbish\",\n\"lobby\",\n\"lobbyer\",\n\"lobcock\",\n\"lobe\",\n\"lobed\",\n\"lobelet\",\n\"lobelin\",\n\"lobfig\",\n\"lobing\",\n\"lobiped\",\n\"lobo\",\n\"lobola\",\n\"lobose\",\n\"lobster\",\n\"lobtail\",\n\"lobular\",\n\"lobule\",\n\"lobworm\",\n\"loca\",\n\"locable\",\n\"local\",\n\"locale\",\n\"locally\",\n\"locanda\",\n\"locate\",\n\"locator\",\n\"loch\",\n\"lochage\",\n\"lochan\",\n\"lochia\",\n\"lochial\",\n\"lochus\",\n\"lochy\",\n\"loci\",\n\"lock\",\n\"lockage\",\n\"lockbox\",\n\"locked\",\n\"locker\",\n\"locket\",\n\"lockful\",\n\"locking\",\n\"lockjaw\",\n\"locklet\",\n\"lockman\",\n\"lockout\",\n\"lockpin\",\n\"lockram\",\n\"lockup\",\n\"locky\",\n\"loco\",\n\"locoism\",\n\"locular\",\n\"locule\",\n\"loculus\",\n\"locum\",\n\"locus\",\n\"locust\",\n\"locusta\",\n\"locutor\",\n\"lod\",\n\"lode\",\n\"lodge\",\n\"lodged\",\n\"lodger\",\n\"lodging\",\n\"loess\",\n\"loessal\",\n\"loessic\",\n\"lof\",\n\"loft\",\n\"lofter\",\n\"loftily\",\n\"lofting\",\n\"loftman\",\n\"lofty\",\n\"log\",\n\"loganin\",\n\"logbook\",\n\"logcock\",\n\"loge\",\n\"logeion\",\n\"logeum\",\n\"loggat\",\n\"logged\",\n\"logger\",\n\"loggia\",\n\"loggin\",\n\"logging\",\n\"loggish\",\n\"loghead\",\n\"logia\",\n\"logic\",\n\"logical\",\n\"logie\",\n\"login\",\n\"logion\",\n\"logium\",\n\"loglet\",\n\"loglike\",\n\"logman\",\n\"logoi\",\n\"logos\",\n\"logroll\",\n\"logway\",\n\"logwise\",\n\"logwood\",\n\"logwork\",\n\"logy\",\n\"lohan\",\n\"lohoch\",\n\"loimic\",\n\"loin\",\n\"loined\",\n\"loir\",\n\"loiter\",\n\"loka\",\n\"lokao\",\n\"lokaose\",\n\"loke\",\n\"loket\",\n\"lokiec\",\n\"loll\",\n\"loller\",\n\"lollop\",\n\"lollopy\",\n\"lolly\",\n\"loma\",\n\"lombard\",\n\"lomboy\",\n\"loment\",\n\"lomita\",\n\"lommock\",\n\"lone\",\n\"lonely\",\n\"long\",\n\"longa\",\n\"longan\",\n\"longbow\",\n\"longe\",\n\"longear\",\n\"longer\",\n\"longfin\",\n\"longful\",\n\"longing\",\n\"longish\",\n\"longjaw\",\n\"longly\",\n\"longs\",\n\"longue\",\n\"longway\",\n\"lontar\",\n\"loo\",\n\"looby\",\n\"lood\",\n\"loof\",\n\"loofah\",\n\"loofie\",\n\"look\",\n\"looker\",\n\"looking\",\n\"lookout\",\n\"lookum\",\n\"loom\",\n\"loomer\",\n\"loomery\",\n\"looming\",\n\"loon\",\n\"loonery\",\n\"looney\",\n\"loony\",\n\"loop\",\n\"looper\",\n\"loopful\",\n\"looping\",\n\"loopist\",\n\"looplet\",\n\"loopy\",\n\"loose\",\n\"loosely\",\n\"loosen\",\n\"looser\",\n\"loosing\",\n\"loosish\",\n\"loot\",\n\"looten\",\n\"looter\",\n\"lootie\",\n\"lop\",\n\"lope\",\n\"loper\",\n\"lophiid\",\n\"lophine\",\n\"loppard\",\n\"lopper\",\n\"loppet\",\n\"lopping\",\n\"loppy\",\n\"lopseed\",\n\"loquat\",\n\"loquent\",\n\"lora\",\n\"loral\",\n\"loran\",\n\"lorate\",\n\"lorcha\",\n\"lord\",\n\"lording\",\n\"lordkin\",\n\"lordlet\",\n\"lordly\",\n\"lordy\",\n\"lore\",\n\"loreal\",\n\"lored\",\n\"lori\",\n\"loric\",\n\"lorica\",\n\"lorilet\",\n\"lorimer\",\n\"loriot\",\n\"loris\",\n\"lormery\",\n\"lorn\",\n\"loro\",\n\"lorry\",\n\"lors\",\n\"lorum\",\n\"lory\",\n\"losable\",\n\"lose\",\n\"losel\",\n\"loser\",\n\"losh\",\n\"losing\",\n\"loss\",\n\"lost\",\n\"lot\",\n\"lota\",\n\"lotase\",\n\"lote\",\n\"lotic\",\n\"lotion\",\n\"lotment\",\n\"lotrite\",\n\"lots\",\n\"lotter\",\n\"lottery\",\n\"lotto\",\n\"lotus\",\n\"lotusin\",\n\"louch\",\n\"loud\",\n\"louden\",\n\"loudish\",\n\"loudly\",\n\"louey\",\n\"lough\",\n\"louk\",\n\"loukoum\",\n\"loulu\",\n\"lounder\",\n\"lounge\",\n\"lounger\",\n\"loungy\",\n\"loup\",\n\"loupe\",\n\"lour\",\n\"lourdy\",\n\"louse\",\n\"lousily\",\n\"louster\",\n\"lousy\",\n\"lout\",\n\"louter\",\n\"louther\",\n\"loutish\",\n\"louty\",\n\"louvar\",\n\"louver\",\n\"lovable\",\n\"lovably\",\n\"lovage\",\n\"love\",\n\"loveful\",\n\"lovely\",\n\"loveman\",\n\"lover\",\n\"lovered\",\n\"loverly\",\n\"loving\",\n\"low\",\n\"lowa\",\n\"lowan\",\n\"lowbell\",\n\"lowborn\",\n\"lowboy\",\n\"lowbred\",\n\"lowdah\",\n\"lowder\",\n\"loweite\",\n\"lower\",\n\"lowerer\",\n\"lowery\",\n\"lowish\",\n\"lowland\",\n\"lowlily\",\n\"lowly\",\n\"lowmen\",\n\"lowmost\",\n\"lown\",\n\"lowness\",\n\"lownly\",\n\"lowth\",\n\"lowwood\",\n\"lowy\",\n\"lox\",\n\"loxia\",\n\"loxic\",\n\"loxotic\",\n\"loy\",\n\"loyal\",\n\"loyally\",\n\"loyalty\",\n\"lozenge\",\n\"lozengy\",\n\"lubber\",\n\"lube\",\n\"lubra\",\n\"lubric\",\n\"lubrify\",\n\"lucanid\",\n\"lucarne\",\n\"lucban\",\n\"luce\",\n\"lucence\",\n\"lucency\",\n\"lucent\",\n\"lucern\",\n\"lucerne\",\n\"lucet\",\n\"lucible\",\n\"lucid\",\n\"lucida\",\n\"lucidly\",\n\"lucifee\",\n\"lucific\",\n\"lucigen\",\n\"lucivee\",\n\"luck\",\n\"lucken\",\n\"luckful\",\n\"luckie\",\n\"luckily\",\n\"lucky\",\n\"lucre\",\n\"lucrify\",\n\"lucule\",\n\"lucumia\",\n\"lucy\",\n\"ludden\",\n\"ludibry\",\n\"ludo\",\n\"lue\",\n\"lues\",\n\"luetic\",\n\"lufbery\",\n\"luff\",\n\"lug\",\n\"luge\",\n\"luger\",\n\"luggage\",\n\"luggar\",\n\"lugged\",\n\"lugger\",\n\"luggie\",\n\"lugmark\",\n\"lugsail\",\n\"lugsome\",\n\"lugworm\",\n\"luhinga\",\n\"luigino\",\n\"luke\",\n\"lukely\",\n\"lulab\",\n\"lull\",\n\"lullaby\",\n\"luller\",\n\"lulu\",\n\"lum\",\n\"lumbago\",\n\"lumbang\",\n\"lumbar\",\n\"lumber\",\n\"lumen\",\n\"luminal\",\n\"lumine\",\n\"lummox\",\n\"lummy\",\n\"lump\",\n\"lumper\",\n\"lumpet\",\n\"lumpily\",\n\"lumping\",\n\"lumpish\",\n\"lumpkin\",\n\"lumpman\",\n\"lumpy\",\n\"luna\",\n\"lunacy\",\n\"lunar\",\n\"lunare\",\n\"lunary\",\n\"lunate\",\n\"lunatic\",\n\"lunatum\",\n\"lunch\",\n\"luncher\",\n\"lune\",\n\"lunes\",\n\"lunette\",\n\"lung\",\n\"lunge\",\n\"lunged\",\n\"lunger\",\n\"lungful\",\n\"lungi\",\n\"lungie\",\n\"lungis\",\n\"lungy\",\n\"lunn\",\n\"lunoid\",\n\"lunt\",\n\"lunula\",\n\"lunular\",\n\"lunule\",\n\"lunulet\",\n\"lupe\",\n\"lupeol\",\n\"lupeose\",\n\"lupine\",\n\"lupinin\",\n\"lupis\",\n\"lupoid\",\n\"lupous\",\n\"lupulic\",\n\"lupulin\",\n\"lupulus\",\n\"lupus\",\n\"lura\",\n\"lural\",\n\"lurch\",\n\"lurcher\",\n\"lurdan\",\n\"lure\",\n\"lureful\",\n\"lurer\",\n\"lurg\",\n\"lurid\",\n\"luridly\",\n\"lurk\",\n\"lurker\",\n\"lurky\",\n\"lurrier\",\n\"lurry\",\n\"lush\",\n\"lusher\",\n\"lushly\",\n\"lushy\",\n\"lusk\",\n\"lusky\",\n\"lusory\",\n\"lust\",\n\"luster\",\n\"lustful\",\n\"lustily\",\n\"lustra\",\n\"lustral\",\n\"lustrum\",\n\"lusty\",\n\"lut\",\n\"lutany\",\n\"lute\",\n\"luteal\",\n\"lutecia\",\n\"lutein\",\n\"lutelet\",\n\"luteo\",\n\"luteoma\",\n\"luteous\",\n\"luter\",\n\"luteway\",\n\"lutfisk\",\n\"luthern\",\n\"luthier\",\n\"luting\",\n\"lutist\",\n\"lutose\",\n\"lutrin\",\n\"lutrine\",\n\"lux\",\n\"luxate\",\n\"luxe\",\n\"luxury\",\n\"luxus\",\n\"ly\",\n\"lyam\",\n\"lyard\",\n\"lyceal\",\n\"lyceum\",\n\"lycid\",\n\"lycopin\",\n\"lycopod\",\n\"lycosid\",\n\"lyctid\",\n\"lyddite\",\n\"lydite\",\n\"lye\",\n\"lyery\",\n\"lygaeid\",\n\"lying\",\n\"lyingly\",\n\"lymph\",\n\"lymphad\",\n\"lymphy\",\n\"lyncean\",\n\"lynch\",\n\"lyncher\",\n\"lyncine\",\n\"lynx\",\n\"lyra\",\n\"lyrate\",\n\"lyrated\",\n\"lyraway\",\n\"lyre\",\n\"lyreman\",\n\"lyric\",\n\"lyrical\",\n\"lyrism\",\n\"lyrist\",\n\"lys\",\n\"lysate\",\n\"lyse\",\n\"lysin\",\n\"lysine\",\n\"lysis\",\n\"lysogen\",\n\"lyssa\",\n\"lyssic\",\n\"lytic\",\n\"lytta\",\n\"lyxose\",\n\"m\",\n\"ma\",\n\"maam\",\n\"mabi\",\n\"mabolo\",\n\"mac\",\n\"macabre\",\n\"macaco\",\n\"macadam\",\n\"macan\",\n\"macana\",\n\"macao\",\n\"macaque\",\n\"macaw\",\n\"macco\",\n\"mace\",\n\"maceman\",\n\"macer\",\n\"machan\",\n\"machar\",\n\"machete\",\n\"machi\",\n\"machila\",\n\"machin\",\n\"machine\",\n\"machree\",\n\"macies\",\n\"mack\",\n\"mackins\",\n\"mackle\",\n\"macle\",\n\"macled\",\n\"maco\",\n\"macrame\",\n\"macro\",\n\"macron\",\n\"macuca\",\n\"macula\",\n\"macular\",\n\"macule\",\n\"macuta\",\n\"mad\",\n\"madam\",\n\"madame\",\n\"madcap\",\n\"madden\",\n\"madder\",\n\"madding\",\n\"maddish\",\n\"maddle\",\n\"made\",\n\"madefy\",\n\"madhuca\",\n\"madid\",\n\"madling\",\n\"madly\",\n\"madman\",\n\"madnep\",\n\"madness\",\n\"mado\",\n\"madoqua\",\n\"madrier\",\n\"madrona\",\n\"madship\",\n\"maduro\",\n\"madweed\",\n\"madwort\",\n\"mae\",\n\"maenad\",\n\"maestri\",\n\"maestro\",\n\"maffia\",\n\"maffick\",\n\"maffle\",\n\"mafflin\",\n\"mafic\",\n\"mafoo\",\n\"mafura\",\n\"mag\",\n\"magadis\",\n\"magani\",\n\"magas\",\n\"mage\",\n\"magenta\",\n\"magged\",\n\"maggle\",\n\"maggot\",\n\"maggoty\",\n\"magi\",\n\"magic\",\n\"magical\",\n\"magiric\",\n\"magma\",\n\"magnate\",\n\"magnes\",\n\"magnet\",\n\"magneta\",\n\"magneto\",\n\"magnify\",\n\"magnum\",\n\"magot\",\n\"magpie\",\n\"magpied\",\n\"magsman\",\n\"maguari\",\n\"maguey\",\n\"maha\",\n\"mahaleb\",\n\"mahalla\",\n\"mahant\",\n\"mahar\",\n\"maharao\",\n\"mahatma\",\n\"mahmal\",\n\"mahmudi\",\n\"mahoe\",\n\"maholi\",\n\"mahone\",\n\"mahout\",\n\"mahseer\",\n\"mahua\",\n\"mahuang\",\n\"maid\",\n\"maidan\",\n\"maiden\",\n\"maidish\",\n\"maidism\",\n\"maidkin\",\n\"maidy\",\n\"maiefic\",\n\"maigre\",\n\"maiid\",\n\"mail\",\n\"mailbag\",\n\"mailbox\",\n\"mailed\",\n\"mailer\",\n\"mailie\",\n\"mailman\",\n\"maim\",\n\"maimed\",\n\"maimer\",\n\"maimon\",\n\"main\",\n\"mainly\",\n\"mainour\",\n\"mainpin\",\n\"mains\",\n\"maint\",\n\"maintop\",\n\"maioid\",\n\"maire\",\n\"maize\",\n\"maizer\",\n\"majagua\",\n\"majesty\",\n\"majo\",\n\"majoon\",\n\"major\",\n\"makable\",\n\"make\",\n\"makedom\",\n\"maker\",\n\"makhzan\",\n\"maki\",\n\"making\",\n\"makluk\",\n\"mako\",\n\"makuk\",\n\"mal\",\n\"mala\",\n\"malacia\",\n\"malacon\",\n\"malady\",\n\"malagma\",\n\"malaise\",\n\"malakin\",\n\"malambo\",\n\"malanga\",\n\"malapi\",\n\"malar\",\n\"malaria\",\n\"malarin\",\n\"malate\",\n\"malati\",\n\"malax\",\n\"malduck\",\n\"male\",\n\"malease\",\n\"maleate\",\n\"maleic\",\n\"malella\",\n\"maleo\",\n\"malfed\",\n\"mali\",\n\"malic\",\n\"malice\",\n\"malicho\",\n\"malign\",\n\"malik\",\n\"maline\",\n\"malines\",\n\"malism\",\n\"malison\",\n\"malist\",\n\"malkin\",\n\"mall\",\n\"mallard\",\n\"malleal\",\n\"mallear\",\n\"mallee\",\n\"mallein\",\n\"mallet\",\n\"malleus\",\n\"mallow\",\n\"mallum\",\n\"mallus\",\n\"malm\",\n\"malmsey\",\n\"malmy\",\n\"malo\",\n\"malodor\",\n\"malonic\",\n\"malonyl\",\n\"malouah\",\n\"malpais\",\n\"malt\",\n\"maltase\",\n\"malter\",\n\"maltha\",\n\"malting\",\n\"maltman\",\n\"maltose\",\n\"malty\",\n\"mamba\",\n\"mambo\",\n\"mamma\",\n\"mammal\",\n\"mammary\",\n\"mammate\",\n\"mammee\",\n\"mammer\",\n\"mammock\",\n\"mammon\",\n\"mammoth\",\n\"mammula\",\n\"mammy\",\n\"mamo\",\n\"man\",\n\"mana\",\n\"manacle\",\n\"manage\",\n\"managee\",\n\"manager\",\n\"manaism\",\n\"manakin\",\n\"manal\",\n\"manas\",\n\"manatee\",\n\"manavel\",\n\"manbird\",\n\"manbot\",\n\"manche\",\n\"manchet\",\n\"mancono\",\n\"mancus\",\n\"mand\",\n\"mandala\",\n\"mandant\",\n\"mandate\",\n\"mandil\",\n\"mandola\",\n\"mandom\",\n\"mandora\",\n\"mandore\",\n\"mandra\",\n\"mandrel\",\n\"mandrin\",\n\"mandua\",\n\"mandyas\",\n\"mane\",\n\"maned\",\n\"manege\",\n\"manei\",\n\"manent\",\n\"manes\",\n\"maness\",\n\"maney\",\n\"manful\",\n\"mang\",\n\"manga\",\n\"mangal\",\n\"mange\",\n\"mangeao\",\n\"mangel\",\n\"manger\",\n\"mangi\",\n\"mangily\",\n\"mangle\",\n\"mangler\",\n\"mango\",\n\"mangona\",\n\"mangue\",\n\"mangy\",\n\"manhead\",\n\"manhole\",\n\"manhood\",\n\"mani\",\n\"mania\",\n\"maniac\",\n\"manic\",\n\"manid\",\n\"manify\",\n\"manikin\",\n\"manila\",\n\"manilla\",\n\"manille\",\n\"manioc\",\n\"maniple\",\n\"manism\",\n\"manist\",\n\"manito\",\n\"maniu\",\n\"manjak\",\n\"mank\",\n\"mankin\",\n\"mankind\",\n\"manless\",\n\"manlet\",\n\"manlike\",\n\"manlily\",\n\"manling\",\n\"manly\",\n\"manna\",\n\"mannan\",\n\"manner\",\n\"manners\",\n\"manness\",\n\"mannide\",\n\"mannie\",\n\"mannify\",\n\"manning\",\n\"mannish\",\n\"mannite\",\n\"mannose\",\n\"manny\",\n\"mano\",\n\"manoc\",\n\"manomin\",\n\"manor\",\n\"manque\",\n\"manred\",\n\"manrent\",\n\"manroot\",\n\"manrope\",\n\"mansard\",\n\"manse\",\n\"manship\",\n\"mansion\",\n\"manso\",\n\"mant\",\n\"manta\",\n\"mantal\",\n\"manteau\",\n\"mantel\",\n\"manter\",\n\"mantes\",\n\"mantic\",\n\"mantid\",\n\"mantis\",\n\"mantle\",\n\"mantled\",\n\"mantlet\",\n\"manto\",\n\"mantoid\",\n\"mantra\",\n\"mantrap\",\n\"mantua\",\n\"manual\",\n\"manuao\",\n\"manuka\",\n\"manul\",\n\"manuma\",\n\"manumea\",\n\"manumit\",\n\"manure\",\n\"manurer\",\n\"manus\",\n\"manward\",\n\"manway\",\n\"manweed\",\n\"manwise\",\n\"many\",\n\"manzana\",\n\"manzil\",\n\"mao\",\n\"maomao\",\n\"map\",\n\"mapach\",\n\"mapau\",\n\"mapland\",\n\"maple\",\n\"mapo\",\n\"mapper\",\n\"mappist\",\n\"mappy\",\n\"mapwise\",\n\"maqui\",\n\"maquis\",\n\"mar\",\n\"marabou\",\n\"maraca\",\n\"maracan\",\n\"marae\",\n\"maral\",\n\"marang\",\n\"marara\",\n\"mararie\",\n\"marasca\",\n\"maraud\",\n\"marble\",\n\"marbled\",\n\"marbler\",\n\"marbles\",\n\"marbly\",\n\"marc\",\n\"marcel\",\n\"march\",\n\"marcher\",\n\"marcid\",\n\"marco\",\n\"marconi\",\n\"marcor\",\n\"mardy\",\n\"mare\",\n\"maremma\",\n\"marengo\",\n\"marfire\",\n\"margay\",\n\"marge\",\n\"margent\",\n\"margin\",\n\"margosa\",\n\"marhala\",\n\"maria\",\n\"marid\",\n\"marimba\",\n\"marina\",\n\"marine\",\n\"mariner\",\n\"mariola\",\n\"maris\",\n\"marish\",\n\"marital\",\n\"mark\",\n\"marka\",\n\"marked\",\n\"marker\",\n\"market\",\n\"markhor\",\n\"marking\",\n\"markka\",\n\"markman\",\n\"markup\",\n\"marl\",\n\"marled\",\n\"marler\",\n\"marli\",\n\"marlin\",\n\"marline\",\n\"marlite\",\n\"marlock\",\n\"marlpit\",\n\"marly\",\n\"marm\",\n\"marmit\",\n\"marmite\",\n\"marmose\",\n\"marmot\",\n\"maro\",\n\"marok\",\n\"maroon\",\n\"marplot\",\n\"marque\",\n\"marquee\",\n\"marquis\",\n\"marrano\",\n\"marree\",\n\"marrer\",\n\"married\",\n\"marrier\",\n\"marron\",\n\"marrot\",\n\"marrow\",\n\"marrowy\",\n\"marry\",\n\"marryer\",\n\"marsh\",\n\"marshal\",\n\"marshy\",\n\"marsoon\",\n\"mart\",\n\"martel\",\n\"marten\",\n\"martext\",\n\"martial\",\n\"martin\",\n\"martite\",\n\"martlet\",\n\"martyr\",\n\"martyry\",\n\"maru\",\n\"marvel\",\n\"marver\",\n\"mary\",\n\"marybud\",\n\"mas\",\n\"masa\",\n\"mascara\",\n\"mascled\",\n\"mascot\",\n\"masculy\",\n\"masdeu\",\n\"mash\",\n\"masha\",\n\"mashal\",\n\"masher\",\n\"mashie\",\n\"mashing\",\n\"mashman\",\n\"mashru\",\n\"mashy\",\n\"masjid\",\n\"mask\",\n\"masked\",\n\"masker\",\n\"maskoid\",\n\"maslin\",\n\"mason\",\n\"masoned\",\n\"masoner\",\n\"masonic\",\n\"masonry\",\n\"masooka\",\n\"masoola\",\n\"masque\",\n\"masquer\",\n\"mass\",\n\"massa\",\n\"massage\",\n\"masse\",\n\"massel\",\n\"masser\",\n\"masseur\",\n\"massier\",\n\"massif\",\n\"massily\",\n\"massive\",\n\"massoy\",\n\"massula\",\n\"massy\",\n\"mast\",\n\"mastaba\",\n\"mastage\",\n\"mastax\",\n\"masted\",\n\"master\",\n\"mastery\",\n\"mastful\",\n\"mastic\",\n\"mastiff\",\n\"masting\",\n\"mastman\",\n\"mastoid\",\n\"masty\",\n\"masu\",\n\"mat\",\n\"mataco\",\n\"matador\",\n\"matai\",\n\"matalan\",\n\"matanza\",\n\"matapan\",\n\"matapi\",\n\"matara\",\n\"matax\",\n\"match\",\n\"matcher\",\n\"matchy\",\n\"mate\",\n\"mately\",\n\"mater\",\n\"matey\",\n\"math\",\n\"mathes\",\n\"matico\",\n\"matin\",\n\"matinal\",\n\"matinee\",\n\"mating\",\n\"matins\",\n\"matipo\",\n\"matka\",\n\"matless\",\n\"matlow\",\n\"matra\",\n\"matral\",\n\"matrass\",\n\"matreed\",\n\"matric\",\n\"matris\",\n\"matrix\",\n\"matron\",\n\"matross\",\n\"matsu\",\n\"matsuri\",\n\"matta\",\n\"mattaro\",\n\"matte\",\n\"matted\",\n\"matter\",\n\"mattery\",\n\"matti\",\n\"matting\",\n\"mattock\",\n\"mattoid\",\n\"mattoir\",\n\"mature\",\n\"maturer\",\n\"matweed\",\n\"maty\",\n\"matzo\",\n\"matzoon\",\n\"matzos\",\n\"matzoth\",\n\"mau\",\n\"maud\",\n\"maudle\",\n\"maudlin\",\n\"mauger\",\n\"maugh\",\n\"maul\",\n\"mauler\",\n\"mauley\",\n\"mauling\",\n\"maumet\",\n\"maun\",\n\"maund\",\n\"maunder\",\n\"maundy\",\n\"maunge\",\n\"mauther\",\n\"mauve\",\n\"mauvine\",\n\"maux\",\n\"mavis\",\n\"maw\",\n\"mawk\",\n\"mawkish\",\n\"mawky\",\n\"mawp\",\n\"maxilla\",\n\"maxim\",\n\"maxima\",\n\"maximal\",\n\"maximed\",\n\"maximum\",\n\"maximus\",\n\"maxixe\",\n\"maxwell\",\n\"may\",\n\"maya\",\n\"maybe\",\n\"maybush\",\n\"maycock\",\n\"mayday\",\n\"mayfish\",\n\"mayhap\",\n\"mayhem\",\n\"maynt\",\n\"mayor\",\n\"mayoral\",\n\"maypop\",\n\"maysin\",\n\"mayten\",\n\"mayweed\",\n\"maza\",\n\"mazame\",\n\"mazard\",\n\"maze\",\n\"mazed\",\n\"mazedly\",\n\"mazeful\",\n\"mazer\",\n\"mazic\",\n\"mazily\",\n\"mazuca\",\n\"mazuma\",\n\"mazurka\",\n\"mazut\",\n\"mazy\",\n\"mazzard\",\n\"mbalolo\",\n\"mbori\",\n\"me\",\n\"meable\",\n\"mead\",\n\"meader\",\n\"meadow\",\n\"meadowy\",\n\"meager\",\n\"meagre\",\n\"meak\",\n\"meal\",\n\"mealer\",\n\"mealies\",\n\"mealily\",\n\"mealman\",\n\"mealy\",\n\"mean\",\n\"meander\",\n\"meaned\",\n\"meaner\",\n\"meaning\",\n\"meanish\",\n\"meanly\",\n\"meant\",\n\"mease\",\n\"measle\",\n\"measled\",\n\"measles\",\n\"measly\",\n\"measure\",\n\"meat\",\n\"meatal\",\n\"meated\",\n\"meatily\",\n\"meatman\",\n\"meatus\",\n\"meaty\",\n\"mecate\",\n\"mecon\",\n\"meconic\",\n\"meconin\",\n\"medal\",\n\"medaled\",\n\"medalet\",\n\"meddle\",\n\"meddler\",\n\"media\",\n\"mediacy\",\n\"mediad\",\n\"medial\",\n\"median\",\n\"mediant\",\n\"mediate\",\n\"medic\",\n\"medical\",\n\"medico\",\n\"mediety\",\n\"medimn\",\n\"medimno\",\n\"medino\",\n\"medio\",\n\"medium\",\n\"medius\",\n\"medlar\",\n\"medley\",\n\"medrick\",\n\"medulla\",\n\"medusal\",\n\"medusan\",\n\"meebos\",\n\"meece\",\n\"meed\",\n\"meek\",\n\"meeken\",\n\"meekly\",\n\"meered\",\n\"meerkat\",\n\"meese\",\n\"meet\",\n\"meeten\",\n\"meeter\",\n\"meeting\",\n\"meetly\",\n\"megabar\",\n\"megaerg\",\n\"megafog\",\n\"megapod\",\n\"megaron\",\n\"megaton\",\n\"megerg\",\n\"megilp\",\n\"megmho\",\n\"megohm\",\n\"megrim\",\n\"mehalla\",\n\"mehari\",\n\"mehtar\",\n\"meile\",\n\"mein\",\n\"meinie\",\n\"meio\",\n\"meiobar\",\n\"meiosis\",\n\"meiotic\",\n\"meith\",\n\"mel\",\n\"mela\",\n\"melada\",\n\"melagra\",\n\"melam\",\n\"melamed\",\n\"melange\",\n\"melanic\",\n\"melanin\",\n\"melano\",\n\"melasma\",\n\"melch\",\n\"meld\",\n\"melder\",\n\"meldrop\",\n\"mele\",\n\"melee\",\n\"melena\",\n\"melene\",\n\"melenic\",\n\"melic\",\n\"melilot\",\n\"meline\",\n\"melisma\",\n\"melitis\",\n\"mell\",\n\"mellate\",\n\"mellay\",\n\"meller\",\n\"mellit\",\n\"mellite\",\n\"mellon\",\n\"mellow\",\n\"mellowy\",\n\"melodia\",\n\"melodic\",\n\"melody\",\n\"meloe\",\n\"meloid\",\n\"melon\",\n\"melonry\",\n\"melos\",\n\"melosa\",\n\"melt\",\n\"meltage\",\n\"melted\",\n\"melter\",\n\"melters\",\n\"melting\",\n\"melton\",\n\"mem\",\n\"member\",\n\"membral\",\n\"memento\",\n\"meminna\",\n\"memo\",\n\"memoir\",\n\"memoria\",\n\"memory\",\n\"men\",\n\"menace\",\n\"menacer\",\n\"menacme\",\n\"menage\",\n\"menald\",\n\"mend\",\n\"mendee\",\n\"mender\",\n\"mending\",\n\"mendole\",\n\"mends\",\n\"menfolk\",\n\"meng\",\n\"menhir\",\n\"menial\",\n\"meninx\",\n\"menkind\",\n\"mennom\",\n\"mensa\",\n\"mensal\",\n\"mense\",\n\"menses\",\n\"mensk\",\n\"mensual\",\n\"mental\",\n\"mentary\",\n\"menthol\",\n\"menthyl\",\n\"mention\",\n\"mentor\",\n\"mentum\",\n\"menu\",\n\"meny\",\n\"menyie\",\n\"menzie\",\n\"merbaby\",\n\"mercal\",\n\"mercer\",\n\"mercery\",\n\"merch\",\n\"merchet\",\n\"mercy\",\n\"mere\",\n\"merel\",\n\"merely\",\n\"merfold\",\n\"merfolk\",\n\"merge\",\n\"merger\",\n\"mergh\",\n\"meriah\",\n\"merice\",\n\"meril\",\n\"merism\",\n\"merist\",\n\"merit\",\n\"merited\",\n\"meriter\",\n\"merk\",\n\"merkhet\",\n\"merkin\",\n\"merl\",\n\"merle\",\n\"merlin\",\n\"merlon\",\n\"mermaid\",\n\"merman\",\n\"mero\",\n\"merop\",\n\"meropia\",\n\"meros\",\n\"merrily\",\n\"merrow\",\n\"merry\",\n\"merse\",\n\"mesa\",\n\"mesad\",\n\"mesail\",\n\"mesal\",\n\"mesally\",\n\"mesange\",\n\"mesarch\",\n\"mescal\",\n\"mese\",\n\"mesem\",\n\"mesenna\",\n\"mesh\",\n\"meshed\",\n\"meshy\",\n\"mesiad\",\n\"mesial\",\n\"mesian\",\n\"mesic\",\n\"mesilla\",\n\"mesion\",\n\"mesityl\",\n\"mesne\",\n\"meso\",\n\"mesobar\",\n\"mesode\",\n\"mesodic\",\n\"mesole\",\n\"meson\",\n\"mesonic\",\n\"mesopic\",\n\"mespil\",\n\"mess\",\n\"message\",\n\"messan\",\n\"messe\",\n\"messer\",\n\"messet\",\n\"messily\",\n\"messin\",\n\"messing\",\n\"messman\",\n\"messor\",\n\"messrs\",\n\"messtin\",\n\"messy\",\n\"mestee\",\n\"mester\",\n\"mestiza\",\n\"mestizo\",\n\"mestome\",\n\"met\",\n\"meta\",\n\"metad\",\n\"metage\",\n\"metal\",\n\"metaler\",\n\"metamer\",\n\"metanym\",\n\"metate\",\n\"metayer\",\n\"mete\",\n\"metel\",\n\"meteor\",\n\"meter\",\n\"methane\",\n\"methene\",\n\"mether\",\n\"methid\",\n\"methide\",\n\"methine\",\n\"method\",\n\"methyl\",\n\"metic\",\n\"metier\",\n\"metis\",\n\"metochy\",\n\"metonym\",\n\"metope\",\n\"metopic\",\n\"metopon\",\n\"metra\",\n\"metreta\",\n\"metrete\",\n\"metria\",\n\"metric\",\n\"metrics\",\n\"metrify\",\n\"metrist\",\n\"mettar\",\n\"mettle\",\n\"mettled\",\n\"metusia\",\n\"metze\",\n\"meuse\",\n\"meute\",\n\"mew\",\n\"meward\",\n\"mewer\",\n\"mewl\",\n\"mewler\",\n\"mezcal\",\n\"mezuzah\",\n\"mezzo\",\n\"mho\",\n\"mi\",\n\"miamia\",\n\"mian\",\n\"miaow\",\n\"miaower\",\n\"mias\",\n\"miasm\",\n\"miasma\",\n\"miasmal\",\n\"miasmic\",\n\"miaul\",\n\"miauler\",\n\"mib\",\n\"mica\",\n\"micate\",\n\"mice\",\n\"micelle\",\n\"miche\",\n\"micher\",\n\"miching\",\n\"micht\",\n\"mick\",\n\"mickle\",\n\"mico\",\n\"micrify\",\n\"micro\",\n\"microbe\",\n\"microhm\",\n\"micron\",\n\"miction\",\n\"mid\",\n\"midday\",\n\"midden\",\n\"middle\",\n\"middler\",\n\"middy\",\n\"mide\",\n\"midge\",\n\"midget\",\n\"midgety\",\n\"midgy\",\n\"midiron\",\n\"midland\",\n\"midleg\",\n\"midmain\",\n\"midmorn\",\n\"midmost\",\n\"midnoon\",\n\"midpit\",\n\"midrash\",\n\"midrib\",\n\"midriff\",\n\"mids\",\n\"midship\",\n\"midst\",\n\"midtap\",\n\"midvein\",\n\"midward\",\n\"midway\",\n\"midweek\",\n\"midwife\",\n\"midwise\",\n\"midyear\",\n\"mien\",\n\"miff\",\n\"miffy\",\n\"mig\",\n\"might\",\n\"mightnt\",\n\"mighty\",\n\"miglio\",\n\"mignon\",\n\"migrant\",\n\"migrate\",\n\"mihrab\",\n\"mijl\",\n\"mikado\",\n\"mike\",\n\"mikie\",\n\"mil\",\n\"mila\",\n\"milady\",\n\"milch\",\n\"milcher\",\n\"milchy\",\n\"mild\",\n\"milden\",\n\"milder\",\n\"mildew\",\n\"mildewy\",\n\"mildish\",\n\"mildly\",\n\"mile\",\n\"mileage\",\n\"miler\",\n\"mileway\",\n\"milfoil\",\n\"milha\",\n\"miliary\",\n\"milieu\",\n\"militia\",\n\"milium\",\n\"milk\",\n\"milken\",\n\"milker\",\n\"milkily\",\n\"milking\",\n\"milkman\",\n\"milksop\",\n\"milky\",\n\"mill\",\n\"milla\",\n\"millage\",\n\"milldam\",\n\"mille\",\n\"milled\",\n\"miller\",\n\"millet\",\n\"millful\",\n\"milliad\",\n\"millile\",\n\"milline\",\n\"milling\",\n\"million\",\n\"millman\",\n\"milner\",\n\"milo\",\n\"milord\",\n\"milpa\",\n\"milreis\",\n\"milsey\",\n\"milsie\",\n\"milt\",\n\"milter\",\n\"milty\",\n\"milvine\",\n\"mim\",\n\"mima\",\n\"mimbar\",\n\"mimble\",\n\"mime\",\n\"mimeo\",\n\"mimer\",\n\"mimesis\",\n\"mimetic\",\n\"mimic\",\n\"mimical\",\n\"mimicry\",\n\"mimine\",\n\"mimly\",\n\"mimmest\",\n\"mimmock\",\n\"mimmood\",\n\"mimmoud\",\n\"mimosis\",\n\"mimp\",\n\"mimsey\",\n\"min\",\n\"mina\",\n\"minable\",\n\"minar\",\n\"minaret\",\n\"minaway\",\n\"mince\",\n\"mincer\",\n\"mincing\",\n\"mind\",\n\"minded\",\n\"minder\",\n\"mindful\",\n\"minding\",\n\"mine\",\n\"miner\",\n\"mineral\",\n\"minery\",\n\"mines\",\n\"minette\",\n\"ming\",\n\"minge\",\n\"mingle\",\n\"mingler\",\n\"mingy\",\n\"minhag\",\n\"minhah\",\n\"miniate\",\n\"minibus\",\n\"minicam\",\n\"minify\",\n\"minikin\",\n\"minim\",\n\"minima\",\n\"minimal\",\n\"minimum\",\n\"minimus\",\n\"mining\",\n\"minion\",\n\"minish\",\n\"minium\",\n\"miniver\",\n\"minivet\",\n\"mink\",\n\"minkery\",\n\"minkish\",\n\"minnie\",\n\"minning\",\n\"minnow\",\n\"minny\",\n\"mino\",\n\"minoize\",\n\"minor\",\n\"minot\",\n\"minster\",\n\"mint\",\n\"mintage\",\n\"minter\",\n\"mintman\",\n\"minty\",\n\"minuend\",\n\"minuet\",\n\"minus\",\n\"minute\",\n\"minuter\",\n\"minutia\",\n\"minx\",\n\"minxish\",\n\"miny\",\n\"minyan\",\n\"miqra\",\n\"mir\",\n\"mirach\",\n\"miracle\",\n\"mirador\",\n\"mirage\",\n\"miragy\",\n\"mirate\",\n\"mirbane\",\n\"mird\",\n\"mirdaha\",\n\"mire\",\n\"mirid\",\n\"mirific\",\n\"mirish\",\n\"mirk\",\n\"miro\",\n\"mirror\",\n\"mirrory\",\n\"mirth\",\n\"miry\",\n\"mirza\",\n\"misact\",\n\"misadd\",\n\"misaim\",\n\"misally\",\n\"misbias\",\n\"misbill\",\n\"misbind\",\n\"misbode\",\n\"misborn\",\n\"misbusy\",\n\"miscall\",\n\"miscast\",\n\"mischio\",\n\"miscoin\",\n\"miscook\",\n\"miscrop\",\n\"miscue\",\n\"miscut\",\n\"misdate\",\n\"misdaub\",\n\"misdeal\",\n\"misdeed\",\n\"misdeem\",\n\"misdiet\",\n\"misdo\",\n\"misdoer\",\n\"misdraw\",\n\"mise\",\n\"misease\",\n\"misedit\",\n\"miser\",\n\"miserly\",\n\"misery\",\n\"misfare\",\n\"misfile\",\n\"misfire\",\n\"misfit\",\n\"misfond\",\n\"misform\",\n\"misgive\",\n\"misgo\",\n\"misgrow\",\n\"mishap\",\n\"mishmee\",\n\"misjoin\",\n\"miskeep\",\n\"misken\",\n\"miskill\",\n\"misknow\",\n\"misky\",\n\"mislay\",\n\"mislead\",\n\"mislear\",\n\"misled\",\n\"mislest\",\n\"mislike\",\n\"mislive\",\n\"mismade\",\n\"mismake\",\n\"mismate\",\n\"mismove\",\n\"misname\",\n\"misobey\",\n\"mispage\",\n\"mispart\",\n\"mispay\",\n\"mispick\",\n\"misplay\",\n\"misput\",\n\"misrate\",\n\"misread\",\n\"misrule\",\n\"miss\",\n\"missal\",\n\"missay\",\n\"misseem\",\n\"missel\",\n\"misset\",\n\"missile\",\n\"missing\",\n\"mission\",\n\"missis\",\n\"missish\",\n\"missive\",\n\"misstay\",\n\"misstep\",\n\"missy\",\n\"mist\",\n\"mistake\",\n\"mistbow\",\n\"misted\",\n\"mistell\",\n\"mistend\",\n\"mister\",\n\"misterm\",\n\"mistful\",\n\"mistic\",\n\"mistide\",\n\"mistify\",\n\"mistily\",\n\"mistime\",\n\"mistle\",\n\"mistone\",\n\"mistook\",\n\"mistral\",\n\"mistry\",\n\"misturn\",\n\"misty\",\n\"misura\",\n\"misuse\",\n\"misuser\",\n\"miswed\",\n\"miswish\",\n\"misword\",\n\"misyoke\",\n\"mite\",\n\"miter\",\n\"mitered\",\n\"miterer\",\n\"mitis\",\n\"mitome\",\n\"mitosis\",\n\"mitotic\",\n\"mitra\",\n\"mitral\",\n\"mitrate\",\n\"mitre\",\n\"mitrer\",\n\"mitt\",\n\"mitten\",\n\"mitty\",\n\"mity\",\n\"miurus\",\n\"mix\",\n\"mixable\",\n\"mixed\",\n\"mixedly\",\n\"mixen\",\n\"mixer\",\n\"mixhill\",\n\"mixible\",\n\"mixite\",\n\"mixtion\",\n\"mixture\",\n\"mixy\",\n\"mizmaze\",\n\"mizzen\",\n\"mizzle\",\n\"mizzler\",\n\"mizzly\",\n\"mizzy\",\n\"mneme\",\n\"mnemic\",\n\"mnesic\",\n\"mnestic\",\n\"mnioid\",\n\"mo\",\n\"moan\",\n\"moanful\",\n\"moaning\",\n\"moat\",\n\"mob\",\n\"mobable\",\n\"mobber\",\n\"mobbish\",\n\"mobbism\",\n\"mobbist\",\n\"mobby\",\n\"mobcap\",\n\"mobed\",\n\"mobile\",\n\"moble\",\n\"moblike\",\n\"mobship\",\n\"mobsman\",\n\"mobster\",\n\"mocha\",\n\"mochras\",\n\"mock\",\n\"mockado\",\n\"mocker\",\n\"mockery\",\n\"mockful\",\n\"mocmain\",\n\"mocuck\",\n\"modal\",\n\"modally\",\n\"mode\",\n\"model\",\n\"modeler\",\n\"modena\",\n\"modern\",\n\"modest\",\n\"modesty\",\n\"modicum\",\n\"modify\",\n\"modish\",\n\"modist\",\n\"modiste\",\n\"modius\",\n\"modular\",\n\"module\",\n\"modulo\",\n\"modulus\",\n\"moellon\",\n\"mofette\",\n\"moff\",\n\"mog\",\n\"mogador\",\n\"mogdad\",\n\"moggan\",\n\"moggy\",\n\"mogo\",\n\"moguey\",\n\"moha\",\n\"mohabat\",\n\"mohair\",\n\"mohar\",\n\"mohel\",\n\"moho\",\n\"mohr\",\n\"mohur\",\n\"moider\",\n\"moidore\",\n\"moieter\",\n\"moiety\",\n\"moil\",\n\"moiler\",\n\"moiles\",\n\"moiley\",\n\"moiling\",\n\"moineau\",\n\"moio\",\n\"moire\",\n\"moise\",\n\"moist\",\n\"moisten\",\n\"moistly\",\n\"moisty\",\n\"moit\",\n\"moity\",\n\"mojarra\",\n\"mojo\",\n\"moke\",\n\"moki\",\n\"moko\",\n\"moksha\",\n\"mokum\",\n\"moky\",\n\"mola\",\n\"molal\",\n\"molar\",\n\"molary\",\n\"molassy\",\n\"molave\",\n\"mold\",\n\"molder\",\n\"moldery\",\n\"molding\",\n\"moldy\",\n\"mole\",\n\"moleism\",\n\"moler\",\n\"molest\",\n\"molimen\",\n\"moline\",\n\"molka\",\n\"molland\",\n\"molle\",\n\"mollie\",\n\"mollify\",\n\"mollusk\",\n\"molly\",\n\"molman\",\n\"moloid\",\n\"moloker\",\n\"molompi\",\n\"molosse\",\n\"molpe\",\n\"molt\",\n\"molten\",\n\"molter\",\n\"moly\",\n\"mombin\",\n\"momble\",\n\"mome\",\n\"moment\",\n\"momenta\",\n\"momism\",\n\"momme\",\n\"mommet\",\n\"mommy\",\n\"momo\",\n\"mon\",\n\"mona\",\n\"monad\",\n\"monadic\",\n\"monaene\",\n\"monal\",\n\"monarch\",\n\"monas\",\n\"monase\",\n\"monaxon\",\n\"mone\",\n\"monel\",\n\"monepic\",\n\"moner\",\n\"moneral\",\n\"moneran\",\n\"moneric\",\n\"moneron\",\n\"monesia\",\n\"money\",\n\"moneyed\",\n\"moneyer\",\n\"mong\",\n\"monger\",\n\"mongery\",\n\"mongler\",\n\"mongrel\",\n\"mongst\",\n\"monial\",\n\"moniker\",\n\"monism\",\n\"monist\",\n\"monitor\",\n\"monk\",\n\"monkdom\",\n\"monkery\",\n\"monkess\",\n\"monkey\",\n\"monkish\",\n\"monkism\",\n\"monkly\",\n\"monny\",\n\"mono\",\n\"monoazo\",\n\"monocle\",\n\"monocot\",\n\"monodic\",\n\"monody\",\n\"monoid\",\n\"monomer\",\n\"mononch\",\n\"monont\",\n\"mononym\",\n\"monose\",\n\"monotic\",\n\"monsoon\",\n\"monster\",\n\"montage\",\n\"montana\",\n\"montane\",\n\"montant\",\n\"monte\",\n\"montem\",\n\"month\",\n\"monthly\",\n\"monthon\",\n\"montjoy\",\n\"monton\",\n\"monture\",\n\"moo\",\n\"mooch\",\n\"moocha\",\n\"moocher\",\n\"mood\",\n\"mooder\",\n\"moodily\",\n\"moodish\",\n\"moodle\",\n\"moody\",\n\"mooing\",\n\"mool\",\n\"moolet\",\n\"mools\",\n\"moolum\",\n\"moon\",\n\"moonack\",\n\"mooned\",\n\"mooner\",\n\"moonery\",\n\"mooneye\",\n\"moonily\",\n\"mooning\",\n\"moonish\",\n\"moonite\",\n\"moonja\",\n\"moonjah\",\n\"moonlet\",\n\"moonlit\",\n\"moonman\",\n\"moonset\",\n\"moonway\",\n\"moony\",\n\"moop\",\n\"moor\",\n\"moorage\",\n\"mooring\",\n\"moorish\",\n\"moorman\",\n\"moorn\",\n\"moorpan\",\n\"moors\",\n\"moorup\",\n\"moory\",\n\"moosa\",\n\"moose\",\n\"moosey\",\n\"moost\",\n\"moot\",\n\"mooter\",\n\"mooth\",\n\"mooting\",\n\"mootman\",\n\"mop\",\n\"mopane\",\n\"mope\",\n\"moper\",\n\"moph\",\n\"mophead\",\n\"moping\",\n\"mopish\",\n\"mopla\",\n\"mopper\",\n\"moppet\",\n\"moppy\",\n\"mopsy\",\n\"mopus\",\n\"mor\",\n\"mora\",\n\"moraine\",\n\"moral\",\n\"morale\",\n\"morally\",\n\"morals\",\n\"morass\",\n\"morassy\",\n\"morat\",\n\"morate\",\n\"moray\",\n\"morbid\",\n\"morbify\",\n\"mordant\",\n\"mordent\",\n\"mordore\",\n\"more\",\n\"moreen\",\n\"moreish\",\n\"morel\",\n\"morella\",\n\"morello\",\n\"mores\",\n\"morfrey\",\n\"morg\",\n\"morga\",\n\"morgan\",\n\"morgay\",\n\"morgen\",\n\"morglay\",\n\"morgue\",\n\"moric\",\n\"moriche\",\n\"morin\",\n\"morinel\",\n\"morion\",\n\"morkin\",\n\"morlop\",\n\"mormaor\",\n\"mormo\",\n\"mormon\",\n\"mormyr\",\n\"mormyre\",\n\"morn\",\n\"morne\",\n\"morned\",\n\"morning\",\n\"moro\",\n\"moroc\",\n\"morocco\",\n\"moron\",\n\"moroncy\",\n\"morong\",\n\"moronic\",\n\"moronry\",\n\"morose\",\n\"morosis\",\n\"morph\",\n\"morphea\",\n\"morphew\",\n\"morphia\",\n\"morphic\",\n\"morphon\",\n\"morris\",\n\"morrow\",\n\"morsal\",\n\"morse\",\n\"morsel\",\n\"morsing\",\n\"morsure\",\n\"mort\",\n\"mortal\",\n\"mortar\",\n\"mortary\",\n\"morth\",\n\"mortier\",\n\"mortify\",\n\"mortise\",\n\"morula\",\n\"morular\",\n\"morule\",\n\"morvin\",\n\"morwong\",\n\"mosaic\",\n\"mosaist\",\n\"mosette\",\n\"mosey\",\n\"mosker\",\n\"mosque\",\n\"moss\",\n\"mossed\",\n\"mosser\",\n\"mossery\",\n\"mossful\",\n\"mossy\",\n\"most\",\n\"moste\",\n\"mostly\",\n\"mot\",\n\"mote\",\n\"moted\",\n\"motel\",\n\"moter\",\n\"motet\",\n\"motey\",\n\"moth\",\n\"mothed\",\n\"mother\",\n\"mothery\",\n\"mothy\",\n\"motif\",\n\"motific\",\n\"motile\",\n\"motion\",\n\"motive\",\n\"motley\",\n\"motmot\",\n\"motor\",\n\"motored\",\n\"motoric\",\n\"motory\",\n\"mott\",\n\"motte\",\n\"mottle\",\n\"mottled\",\n\"mottler\",\n\"motto\",\n\"mottoed\",\n\"motyka\",\n\"mou\",\n\"mouche\",\n\"moud\",\n\"moudie\",\n\"moudy\",\n\"mouflon\",\n\"mouille\",\n\"moujik\",\n\"moul\",\n\"mould\",\n\"moulded\",\n\"moule\",\n\"moulin\",\n\"mouls\",\n\"moulter\",\n\"mouly\",\n\"mound\",\n\"moundy\",\n\"mount\",\n\"mounted\",\n\"mounter\",\n\"moup\",\n\"mourn\",\n\"mourner\",\n\"mouse\",\n\"mouser\",\n\"mousery\",\n\"mousey\",\n\"mousily\",\n\"mousing\",\n\"mousle\",\n\"mousmee\",\n\"mousse\",\n\"moustoc\",\n\"mousy\",\n\"mout\",\n\"moutan\",\n\"mouth\",\n\"mouthed\",\n\"mouther\",\n\"mouthy\",\n\"mouton\",\n\"mouzah\",\n\"movable\",\n\"movably\",\n\"movant\",\n\"move\",\n\"mover\",\n\"movie\",\n\"moving\",\n\"mow\",\n\"mowable\",\n\"mowana\",\n\"mowburn\",\n\"mowch\",\n\"mowcht\",\n\"mower\",\n\"mowha\",\n\"mowie\",\n\"mowing\",\n\"mowland\",\n\"mown\",\n\"mowra\",\n\"mowrah\",\n\"mowse\",\n\"mowt\",\n\"mowth\",\n\"moxa\",\n\"moy\",\n\"moyen\",\n\"moyenne\",\n\"moyite\",\n\"moyle\",\n\"moyo\",\n\"mozing\",\n\"mpret\",\n\"mu\",\n\"muang\",\n\"mubarat\",\n\"mucago\",\n\"mucaro\",\n\"mucedin\",\n\"much\",\n\"muchly\",\n\"mucic\",\n\"mucid\",\n\"mucific\",\n\"mucigen\",\n\"mucin\",\n\"muck\",\n\"mucker\",\n\"mucket\",\n\"muckite\",\n\"muckle\",\n\"muckman\",\n\"muckna\",\n\"mucksy\",\n\"mucky\",\n\"mucluc\",\n\"mucoid\",\n\"muconic\",\n\"mucopus\",\n\"mucor\",\n\"mucosa\",\n\"mucosal\",\n\"mucose\",\n\"mucous\",\n\"mucro\",\n\"mucus\",\n\"mucusin\",\n\"mud\",\n\"mudar\",\n\"mudbank\",\n\"mudcap\",\n\"mudd\",\n\"mudde\",\n\"mudden\",\n\"muddify\",\n\"muddily\",\n\"mudding\",\n\"muddish\",\n\"muddle\",\n\"muddler\",\n\"muddy\",\n\"mudee\",\n\"mudfish\",\n\"mudflow\",\n\"mudhead\",\n\"mudhole\",\n\"mudir\",\n\"mudiria\",\n\"mudland\",\n\"mudlark\",\n\"mudless\",\n\"mudra\",\n\"mudsill\",\n\"mudweed\",\n\"mudwort\",\n\"muermo\",\n\"muezzin\",\n\"muff\",\n\"muffed\",\n\"muffet\",\n\"muffin\",\n\"muffish\",\n\"muffle\",\n\"muffled\",\n\"muffler\",\n\"mufflin\",\n\"muffy\",\n\"mufti\",\n\"mufty\",\n\"mug\",\n\"muga\",\n\"mugful\",\n\"mugg\",\n\"mugger\",\n\"mugget\",\n\"muggily\",\n\"muggins\",\n\"muggish\",\n\"muggles\",\n\"muggy\",\n\"mugient\",\n\"mugweed\",\n\"mugwort\",\n\"mugwump\",\n\"muid\",\n\"muir\",\n\"muist\",\n\"mukluk\",\n\"muktar\",\n\"mukti\",\n\"mulatta\",\n\"mulatto\",\n\"mulch\",\n\"mulcher\",\n\"mulct\",\n\"mulder\",\n\"mule\",\n\"muleman\",\n\"muleta\",\n\"muletta\",\n\"muley\",\n\"mulga\",\n\"mulier\",\n\"mulish\",\n\"mulism\",\n\"mulita\",\n\"mulk\",\n\"mull\",\n\"mulla\",\n\"mullah\",\n\"mullar\",\n\"mullein\",\n\"muller\",\n\"mullet\",\n\"mullets\",\n\"mulley\",\n\"mullid\",\n\"mullion\",\n\"mullite\",\n\"mullock\",\n\"mulloid\",\n\"mulmul\",\n\"mulse\",\n\"mulsify\",\n\"mult\",\n\"multum\",\n\"multure\",\n\"mum\",\n\"mumble\",\n\"mumbler\",\n\"mummer\",\n\"mummery\",\n\"mummick\",\n\"mummied\",\n\"mummify\",\n\"mumming\",\n\"mummy\",\n\"mumness\",\n\"mump\",\n\"mumper\",\n\"mumpish\",\n\"mumps\",\n\"mun\",\n\"munch\",\n\"muncher\",\n\"munchet\",\n\"mund\",\n\"mundane\",\n\"mundic\",\n\"mundify\",\n\"mundil\",\n\"mundle\",\n\"mung\",\n\"munga\",\n\"munge\",\n\"mungey\",\n\"mungo\",\n\"mungofa\",\n\"munguba\",\n\"mungy\",\n\"munific\",\n\"munity\",\n\"munj\",\n\"munjeet\",\n\"munnion\",\n\"munshi\",\n\"munt\",\n\"muntin\",\n\"muntjac\",\n\"mura\",\n\"murage\",\n\"mural\",\n\"muraled\",\n\"murally\",\n\"murchy\",\n\"murder\",\n\"murdrum\",\n\"mure\",\n\"murex\",\n\"murexan\",\n\"murga\",\n\"murgavi\",\n\"murgeon\",\n\"muriate\",\n\"muricid\",\n\"murid\",\n\"murine\",\n\"murinus\",\n\"muriti\",\n\"murium\",\n\"murk\",\n\"murkily\",\n\"murkish\",\n\"murkly\",\n\"murky\",\n\"murlin\",\n\"murly\",\n\"murmur\",\n\"murphy\",\n\"murra\",\n\"murrain\",\n\"murre\",\n\"murrey\",\n\"murrina\",\n\"murshid\",\n\"muruxi\",\n\"murva\",\n\"murza\",\n\"musal\",\n\"musang\",\n\"musar\",\n\"muscade\",\n\"muscat\",\n\"muscid\",\n\"muscle\",\n\"muscled\",\n\"muscly\",\n\"muscoid\",\n\"muscone\",\n\"muscose\",\n\"muscot\",\n\"muscovy\",\n\"muscule\",\n\"muse\",\n\"mused\",\n\"museful\",\n\"museist\",\n\"muser\",\n\"musery\",\n\"musette\",\n\"museum\",\n\"mush\",\n\"musha\",\n\"mushaa\",\n\"mushed\",\n\"musher\",\n\"mushily\",\n\"mushla\",\n\"mushru\",\n\"mushy\",\n\"music\",\n\"musical\",\n\"musico\",\n\"musie\",\n\"musily\",\n\"musimon\",\n\"musing\",\n\"musk\",\n\"muskat\",\n\"muskeg\",\n\"musket\",\n\"muskie\",\n\"muskish\",\n\"muskrat\",\n\"musky\",\n\"muslin\",\n\"musnud\",\n\"musquaw\",\n\"musrol\",\n\"muss\",\n\"mussal\",\n\"mussel\",\n\"mussily\",\n\"mussuk\",\n\"mussy\",\n\"must\",\n\"mustang\",\n\"mustard\",\n\"mustee\",\n\"muster\",\n\"mustify\",\n\"mustily\",\n\"mustnt\",\n\"musty\",\n\"muta\",\n\"mutable\",\n\"mutably\",\n\"mutage\",\n\"mutant\",\n\"mutase\",\n\"mutate\",\n\"mutch\",\n\"mute\",\n\"mutedly\",\n\"mutely\",\n\"muth\",\n\"mutic\",\n\"mutiny\",\n\"mutism\",\n\"mutist\",\n\"mutive\",\n\"mutsje\",\n\"mutt\",\n\"mutter\",\n\"mutton\",\n\"muttony\",\n\"mutual\",\n\"mutuary\",\n\"mutule\",\n\"mutuum\",\n\"mux\",\n\"muyusa\",\n\"muzhik\",\n\"muzz\",\n\"muzzily\",\n\"muzzle\",\n\"muzzler\",\n\"muzzy\",\n\"my\",\n\"myal\",\n\"myalgia\",\n\"myalgic\",\n\"myalism\",\n\"myall\",\n\"myarian\",\n\"myatony\",\n\"mycele\",\n\"mycelia\",\n\"mycoid\",\n\"mycose\",\n\"mycosin\",\n\"mycosis\",\n\"mycotic\",\n\"mydine\",\n\"myelic\",\n\"myelin\",\n\"myeloic\",\n\"myeloid\",\n\"myeloma\",\n\"myelon\",\n\"mygale\",\n\"mygalid\",\n\"myiasis\",\n\"myiosis\",\n\"myitis\",\n\"mykiss\",\n\"mymarid\",\n\"myna\",\n\"myocele\",\n\"myocyte\",\n\"myogen\",\n\"myogram\",\n\"myoid\",\n\"myology\",\n\"myoma\",\n\"myomere\",\n\"myoneme\",\n\"myope\",\n\"myophan\",\n\"myopia\",\n\"myopic\",\n\"myops\",\n\"myopy\",\n\"myosin\",\n\"myosis\",\n\"myosote\",\n\"myotic\",\n\"myotome\",\n\"myotomy\",\n\"myotony\",\n\"myowun\",\n\"myoxine\",\n\"myrcene\",\n\"myrcia\",\n\"myriad\",\n\"myriare\",\n\"myrica\",\n\"myricin\",\n\"myricyl\",\n\"myringa\",\n\"myron\",\n\"myronic\",\n\"myrosin\",\n\"myrrh\",\n\"myrrhed\",\n\"myrrhic\",\n\"myrrhol\",\n\"myrrhy\",\n\"myrtal\",\n\"myrtle\",\n\"myrtol\",\n\"mysel\",\n\"myself\",\n\"mysell\",\n\"mysid\",\n\"mysoid\",\n\"mysost\",\n\"myst\",\n\"mystax\",\n\"mystery\",\n\"mystes\",\n\"mystic\",\n\"mystify\",\n\"myth\",\n\"mythify\",\n\"mythism\",\n\"mythist\",\n\"mythize\",\n\"mythos\",\n\"mythus\",\n\"mytilid\",\n\"myxa\",\n\"myxemia\",\n\"myxo\",\n\"myxoid\",\n\"myxoma\",\n\"myxopod\",\n\"myzont\",\n\"n\",\n\"na\",\n\"naa\",\n\"naam\",\n\"nab\",\n\"nabak\",\n\"nabber\",\n\"nabk\",\n\"nabla\",\n\"nable\",\n\"nabob\",\n\"nabobry\",\n\"nabs\",\n\"nacarat\",\n\"nace\",\n\"nacelle\",\n\"nach\",\n\"nachani\",\n\"nacket\",\n\"nacre\",\n\"nacred\",\n\"nacrine\",\n\"nacrite\",\n\"nacrous\",\n\"nacry\",\n\"nadder\",\n\"nadir\",\n\"nadiral\",\n\"nae\",\n\"naebody\",\n\"naegate\",\n\"nael\",\n\"naether\",\n\"nag\",\n\"naga\",\n\"nagaika\",\n\"nagana\",\n\"nagara\",\n\"nagger\",\n\"naggin\",\n\"nagging\",\n\"naggish\",\n\"naggle\",\n\"naggly\",\n\"naggy\",\n\"naght\",\n\"nagmaal\",\n\"nagman\",\n\"nagnag\",\n\"nagnail\",\n\"nagor\",\n\"nagsman\",\n\"nagster\",\n\"nagual\",\n\"naiad\",\n\"naiant\",\n\"naid\",\n\"naif\",\n\"naifly\",\n\"naig\",\n\"naigie\",\n\"naik\",\n\"nail\",\n\"nailbin\",\n\"nailer\",\n\"nailery\",\n\"nailing\",\n\"nailrod\",\n\"naily\",\n\"nain\",\n\"nainsel\",\n\"naio\",\n\"naipkin\",\n\"nairy\",\n\"nais\",\n\"naish\",\n\"naither\",\n\"naive\",\n\"naively\",\n\"naivete\",\n\"naivety\",\n\"nak\",\n\"nake\",\n\"naked\",\n\"nakedly\",\n\"naker\",\n\"nakhod\",\n\"nakhoda\",\n\"nako\",\n\"nakong\",\n\"nakoo\",\n\"nallah\",\n\"nam\",\n\"namable\",\n\"namaqua\",\n\"namaz\",\n\"namda\",\n\"name\",\n\"namely\",\n\"namer\",\n\"naming\",\n\"nammad\",\n\"nan\",\n\"nana\",\n\"nancy\",\n\"nandi\",\n\"nandine\",\n\"nandow\",\n\"nandu\",\n\"nane\",\n\"nanes\",\n\"nanga\",\n\"nanism\",\n\"nankeen\",\n\"nankin\",\n\"nanny\",\n\"nanoid\",\n\"nanpie\",\n\"nant\",\n\"nantle\",\n\"naology\",\n\"naos\",\n\"nap\",\n\"napa\",\n\"napal\",\n\"napalm\",\n\"nape\",\n\"napead\",\n\"naperer\",\n\"napery\",\n\"naphtha\",\n\"naphtho\",\n\"naphtol\",\n\"napkin\",\n\"napless\",\n\"napoo\",\n\"nappe\",\n\"napped\",\n\"napper\",\n\"napping\",\n\"nappy\",\n\"napron\",\n\"napu\",\n\"nar\",\n\"narcism\",\n\"narcist\",\n\"narcoma\",\n\"narcose\",\n\"narcous\",\n\"nard\",\n\"nardine\",\n\"nardoo\",\n\"nares\",\n\"nargil\",\n\"narial\",\n\"naric\",\n\"narica\",\n\"narine\",\n\"nark\",\n\"narky\",\n\"narr\",\n\"narra\",\n\"narras\",\n\"narrate\",\n\"narrow\",\n\"narrowy\",\n\"narthex\",\n\"narwhal\",\n\"nary\",\n\"nasab\",\n\"nasal\",\n\"nasalis\",\n\"nasally\",\n\"nasard\",\n\"nascent\",\n\"nasch\",\n\"nash\",\n\"nashgab\",\n\"nashgob\",\n\"nasi\",\n\"nasial\",\n\"nasion\",\n\"nasitis\",\n\"nasrol\",\n\"nast\",\n\"nastic\",\n\"nastika\",\n\"nastily\",\n\"nasty\",\n\"nasus\",\n\"nasute\",\n\"nasutus\",\n\"nat\",\n\"nataka\",\n\"natal\",\n\"natals\",\n\"natant\",\n\"natator\",\n\"natch\",\n\"nates\",\n\"nathe\",\n\"nather\",\n\"nation\",\n\"native\",\n\"natr\",\n\"natrium\",\n\"natron\",\n\"natter\",\n\"nattily\",\n\"nattle\",\n\"natty\",\n\"natuary\",\n\"natural\",\n\"nature\",\n\"naucrar\",\n\"nauger\",\n\"naught\",\n\"naughty\",\n\"naumk\",\n\"naunt\",\n\"nauntle\",\n\"nausea\",\n\"naut\",\n\"nautch\",\n\"nauther\",\n\"nautic\",\n\"nautics\",\n\"naval\",\n\"navally\",\n\"navar\",\n\"navarch\",\n\"nave\",\n\"navel\",\n\"naveled\",\n\"navet\",\n\"navette\",\n\"navew\",\n\"navite\",\n\"navvy\",\n\"navy\",\n\"naw\",\n\"nawab\",\n\"nawt\",\n\"nay\",\n\"nayaur\",\n\"naysay\",\n\"nayward\",\n\"nayword\",\n\"naze\",\n\"nazim\",\n\"nazir\",\n\"ne\",\n\"nea\",\n\"neal\",\n\"neanic\",\n\"neap\",\n\"neaped\",\n\"nearby\",\n\"nearest\",\n\"nearish\",\n\"nearly\",\n\"neat\",\n\"neaten\",\n\"neath\",\n\"neatify\",\n\"neatly\",\n\"neb\",\n\"neback\",\n\"nebbed\",\n\"nebbuck\",\n\"nebbuk\",\n\"nebby\",\n\"nebel\",\n\"nebris\",\n\"nebula\",\n\"nebulae\",\n\"nebular\",\n\"nebule\",\n\"neck\",\n\"neckar\",\n\"necked\",\n\"necker\",\n\"neckful\",\n\"necking\",\n\"necklet\",\n\"necktie\",\n\"necrose\",\n\"nectar\",\n\"nectary\",\n\"nedder\",\n\"neddy\",\n\"nee\",\n\"neebor\",\n\"neebour\",\n\"need\",\n\"needer\",\n\"needful\",\n\"needham\",\n\"needily\",\n\"needing\",\n\"needle\",\n\"needled\",\n\"needler\",\n\"needles\",\n\"needly\",\n\"needs\",\n\"needy\",\n\"neeger\",\n\"neeld\",\n\"neele\",\n\"neem\",\n\"neep\",\n\"neepour\",\n\"neer\",\n\"neese\",\n\"neet\",\n\"neetup\",\n\"neeze\",\n\"nef\",\n\"nefast\",\n\"neffy\",\n\"neftgil\",\n\"negate\",\n\"negator\",\n\"neger\",\n\"neglect\",\n\"negrine\",\n\"negro\",\n\"negus\",\n\"nei\",\n\"neif\",\n\"neigh\",\n\"neigher\",\n\"neiper\",\n\"neist\",\n\"neither\",\n\"nekton\",\n\"nelson\",\n\"nema\",\n\"nematic\",\n\"nemeses\",\n\"nemesic\",\n\"nemoral\",\n\"nenta\",\n\"neo\",\n\"neocyte\",\n\"neogamy\",\n\"neolith\",\n\"neology\",\n\"neon\",\n\"neonate\",\n\"neorama\",\n\"neossin\",\n\"neoteny\",\n\"neotype\",\n\"neoza\",\n\"nep\",\n\"neper\",\n\"nephele\",\n\"nephesh\",\n\"nephew\",\n\"nephria\",\n\"nephric\",\n\"nephron\",\n\"nephros\",\n\"nepman\",\n\"nepotal\",\n\"nepote\",\n\"nepotic\",\n\"nereite\",\n\"nerine\",\n\"neritic\",\n\"nerval\",\n\"nervate\",\n\"nerve\",\n\"nerver\",\n\"nervid\",\n\"nervily\",\n\"nervine\",\n\"nerving\",\n\"nervish\",\n\"nervism\",\n\"nervose\",\n\"nervous\",\n\"nervule\",\n\"nervure\",\n\"nervy\",\n\"nese\",\n\"nesh\",\n\"neshly\",\n\"nesiote\",\n\"ness\",\n\"nest\",\n\"nestage\",\n\"nester\",\n\"nestful\",\n\"nestle\",\n\"nestler\",\n\"nesty\",\n\"net\",\n\"netball\",\n\"netbush\",\n\"netcha\",\n\"nete\",\n\"neter\",\n\"netful\",\n\"neth\",\n\"nether\",\n\"neti\",\n\"netleaf\",\n\"netlike\",\n\"netman\",\n\"netop\",\n\"netsman\",\n\"netsuke\",\n\"netted\",\n\"netter\",\n\"netting\",\n\"nettle\",\n\"nettler\",\n\"nettly\",\n\"netty\",\n\"netwise\",\n\"network\",\n\"neuma\",\n\"neume\",\n\"neumic\",\n\"neurad\",\n\"neural\",\n\"neurale\",\n\"neuric\",\n\"neurin\",\n\"neurine\",\n\"neurism\",\n\"neurite\",\n\"neuroid\",\n\"neuroma\",\n\"neuron\",\n\"neurone\",\n\"neurula\",\n\"neuter\",\n\"neutral\",\n\"neutron\",\n\"neve\",\n\"nevel\",\n\"never\",\n\"nevo\",\n\"nevoid\",\n\"nevoy\",\n\"nevus\",\n\"new\",\n\"newcal\",\n\"newcome\",\n\"newel\",\n\"newelty\",\n\"newing\",\n\"newings\",\n\"newish\",\n\"newly\",\n\"newness\",\n\"news\",\n\"newsboy\",\n\"newsful\",\n\"newsman\",\n\"newsy\",\n\"newt\",\n\"newtake\",\n\"newton\",\n\"nexal\",\n\"next\",\n\"nextly\",\n\"nexum\",\n\"nexus\",\n\"neyanda\",\n\"ngai\",\n\"ngaio\",\n\"ngapi\",\n\"ni\",\n\"niacin\",\n\"niata\",\n\"nib\",\n\"nibbana\",\n\"nibbed\",\n\"nibber\",\n\"nibble\",\n\"nibbler\",\n\"nibby\",\n\"niblick\",\n\"niblike\",\n\"nibong\",\n\"nibs\",\n\"nibsome\",\n\"nice\",\n\"niceish\",\n\"nicely\",\n\"nicety\",\n\"niche\",\n\"nicher\",\n\"nick\",\n\"nickel\",\n\"nicker\",\n\"nickey\",\n\"nicking\",\n\"nickle\",\n\"nicky\",\n\"nicolo\",\n\"nicotia\",\n\"nicotic\",\n\"nictate\",\n\"nid\",\n\"nidal\",\n\"nidana\",\n\"niddick\",\n\"niddle\",\n\"nide\",\n\"nidge\",\n\"nidget\",\n\"nidgety\",\n\"nidi\",\n\"nidify\",\n\"niding\",\n\"nidor\",\n\"nidulus\",\n\"nidus\",\n\"niece\",\n\"nielled\",\n\"niello\",\n\"niepa\",\n\"nieve\",\n\"nieveta\",\n\"nife\",\n\"niffer\",\n\"nific\",\n\"nifle\",\n\"nifling\",\n\"nifty\",\n\"nig\",\n\"niggard\",\n\"nigger\",\n\"niggery\",\n\"niggle\",\n\"niggler\",\n\"niggly\",\n\"nigh\",\n\"nighly\",\n\"night\",\n\"nighted\",\n\"nightie\",\n\"nightly\",\n\"nights\",\n\"nignay\",\n\"nignye\",\n\"nigori\",\n\"nigre\",\n\"nigrify\",\n\"nigrine\",\n\"nigrous\",\n\"nigua\",\n\"nikau\",\n\"nil\",\n\"nilgai\",\n\"nim\",\n\"nimb\",\n\"nimbed\",\n\"nimbi\",\n\"nimble\",\n\"nimbly\",\n\"nimbose\",\n\"nimbus\",\n\"nimiety\",\n\"niminy\",\n\"nimious\",\n\"nimmer\",\n\"nimshi\",\n\"nincom\",\n\"nine\",\n\"ninepin\",\n\"nineted\",\n\"ninety\",\n\"ninny\",\n\"ninon\",\n\"ninth\",\n\"ninthly\",\n\"nintu\",\n\"ninut\",\n\"niobate\",\n\"niobic\",\n\"niobite\",\n\"niobium\",\n\"niobous\",\n\"niog\",\n\"niota\",\n\"nip\",\n\"nipa\",\n\"nipper\",\n\"nippers\",\n\"nippily\",\n\"nipping\",\n\"nipple\",\n\"nippy\",\n\"nipter\",\n\"nirles\",\n\"nirvana\",\n\"nisei\",\n\"nishiki\",\n\"nisnas\",\n\"nispero\",\n\"nisse\",\n\"nisus\",\n\"nit\",\n\"nitch\",\n\"nitency\",\n\"niter\",\n\"nitered\",\n\"nither\",\n\"nithing\",\n\"nitid\",\n\"nito\",\n\"niton\",\n\"nitrate\",\n\"nitric\",\n\"nitride\",\n\"nitrify\",\n\"nitrile\",\n\"nitrite\",\n\"nitro\",\n\"nitrous\",\n\"nitryl\",\n\"nitter\",\n\"nitty\",\n\"nitwit\",\n\"nival\",\n\"niveous\",\n\"nix\",\n\"nixie\",\n\"niyoga\",\n\"nizam\",\n\"nizamut\",\n\"nizy\",\n\"njave\",\n\"no\",\n\"noa\",\n\"nob\",\n\"nobber\",\n\"nobbily\",\n\"nobble\",\n\"nobbler\",\n\"nobbut\",\n\"nobby\",\n\"noble\",\n\"nobley\",\n\"nobly\",\n\"nobody\",\n\"nobs\",\n\"nocake\",\n\"nocent\",\n\"nock\",\n\"nocket\",\n\"nocktat\",\n\"noctuid\",\n\"noctule\",\n\"nocturn\",\n\"nocuity\",\n\"nocuous\",\n\"nod\",\n\"nodal\",\n\"nodated\",\n\"nodder\",\n\"nodding\",\n\"noddle\",\n\"noddy\",\n\"node\",\n\"noded\",\n\"nodi\",\n\"nodiak\",\n\"nodical\",\n\"nodose\",\n\"nodous\",\n\"nodular\",\n\"nodule\",\n\"noduled\",\n\"nodulus\",\n\"nodus\",\n\"noel\",\n\"noetic\",\n\"noetics\",\n\"nog\",\n\"nogada\",\n\"nogal\",\n\"noggen\",\n\"noggin\",\n\"nogging\",\n\"noghead\",\n\"nohow\",\n\"noil\",\n\"noilage\",\n\"noiler\",\n\"noily\",\n\"noint\",\n\"noir\",\n\"noise\",\n\"noisily\",\n\"noisome\",\n\"noisy\",\n\"nokta\",\n\"noll\",\n\"nolle\",\n\"nolo\",\n\"noma\",\n\"nomad\",\n\"nomadic\",\n\"nomancy\",\n\"nomarch\",\n\"nombril\",\n\"nome\",\n\"nomial\",\n\"nomic\",\n\"nomina\",\n\"nominal\",\n\"nominee\",\n\"nominy\",\n\"nomism\",\n\"nomisma\",\n\"nomos\",\n\"non\",\n\"nonacid\",\n\"nonact\",\n\"nonage\",\n\"nonagon\",\n\"nonaid\",\n\"nonair\",\n\"nonane\",\n\"nonary\",\n\"nonbase\",\n\"nonce\",\n\"noncock\",\n\"noncom\",\n\"noncome\",\n\"noncon\",\n\"nonda\",\n\"nondo\",\n\"none\",\n\"nonego\",\n\"nonene\",\n\"nonent\",\n\"nonepic\",\n\"nones\",\n\"nonet\",\n\"nonevil\",\n\"nonfact\",\n\"nonfarm\",\n\"nonfat\",\n\"nonfood\",\n\"nonform\",\n\"nonfrat\",\n\"nongas\",\n\"nongod\",\n\"nongold\",\n\"nongray\",\n\"nongrey\",\n\"nonhero\",\n\"nonic\",\n\"nonion\",\n\"nonius\",\n\"nonjury\",\n\"nonlife\",\n\"nonly\",\n\"nonnant\",\n\"nonnat\",\n\"nonoic\",\n\"nonoily\",\n\"nonomad\",\n\"nonpaid\",\n\"nonpar\",\n\"nonpeak\",\n\"nonplus\",\n\"nonpoet\",\n\"nonport\",\n\"nonrun\",\n\"nonsale\",\n\"nonsane\",\n\"nonself\",\n\"nonsine\",\n\"nonskid\",\n\"nonslip\",\n\"nonstop\",\n\"nonsuit\",\n\"nontan\",\n\"nontax\",\n\"nonterm\",\n\"nonuple\",\n\"nonuse\",\n\"nonuser\",\n\"nonwar\",\n\"nonya\",\n\"nonyl\",\n\"nonylic\",\n\"nonzero\",\n\"noodle\",\n\"nook\",\n\"nooked\",\n\"nookery\",\n\"nooking\",\n\"nooklet\",\n\"nooky\",\n\"noology\",\n\"noon\",\n\"noonday\",\n\"nooning\",\n\"noonlit\",\n\"noop\",\n\"noose\",\n\"nooser\",\n\"nopal\",\n\"nopalry\",\n\"nope\",\n\"nor\",\n\"norard\",\n\"norate\",\n\"noreast\",\n\"norelin\",\n\"norgine\",\n\"nori\",\n\"noria\",\n\"norie\",\n\"norimon\",\n\"norite\",\n\"norland\",\n\"norm\",\n\"norma\",\n\"normal\",\n\"norsel\",\n\"north\",\n\"norther\",\n\"norward\",\n\"norwest\",\n\"nose\",\n\"nosean\",\n\"nosed\",\n\"nosegay\",\n\"noser\",\n\"nosey\",\n\"nosine\",\n\"nosing\",\n\"nosism\",\n\"nostic\",\n\"nostril\",\n\"nostrum\",\n\"nosy\",\n\"not\",\n\"notable\",\n\"notably\",\n\"notaeal\",\n\"notaeum\",\n\"notal\",\n\"notan\",\n\"notary\",\n\"notate\",\n\"notator\",\n\"notch\",\n\"notched\",\n\"notchel\",\n\"notcher\",\n\"notchy\",\n\"note\",\n\"noted\",\n\"notedly\",\n\"notekin\",\n\"notelet\",\n\"noter\",\n\"nother\",\n\"nothing\",\n\"nothous\",\n\"notice\",\n\"noticer\",\n\"notify\",\n\"notion\",\n\"notitia\",\n\"notour\",\n\"notself\",\n\"notum\",\n\"nougat\",\n\"nought\",\n\"noun\",\n\"nounal\",\n\"nounize\",\n\"noup\",\n\"nourice\",\n\"nourish\",\n\"nous\",\n\"nouther\",\n\"nova\",\n\"novalia\",\n\"novate\",\n\"novator\",\n\"novcic\",\n\"novel\",\n\"novelet\",\n\"novella\",\n\"novelly\",\n\"novelry\",\n\"novelty\",\n\"novem\",\n\"novena\",\n\"novene\",\n\"novice\",\n\"novity\",\n\"now\",\n\"nowaday\",\n\"noway\",\n\"noways\",\n\"nowed\",\n\"nowel\",\n\"nowhat\",\n\"nowhen\",\n\"nowhere\",\n\"nowhit\",\n\"nowise\",\n\"nowness\",\n\"nowt\",\n\"nowy\",\n\"noxa\",\n\"noxal\",\n\"noxally\",\n\"noxious\",\n\"noy\",\n\"noyade\",\n\"noyau\",\n\"nozzle\",\n\"nozzler\",\n\"nth\",\n\"nu\",\n\"nuance\",\n\"nub\",\n\"nubbin\",\n\"nubble\",\n\"nubbly\",\n\"nubby\",\n\"nubia\",\n\"nubile\",\n\"nucal\",\n\"nucha\",\n\"nuchal\",\n\"nucin\",\n\"nucleal\",\n\"nuclear\",\n\"nuclei\",\n\"nuclein\",\n\"nucleon\",\n\"nucleus\",\n\"nuclide\",\n\"nucule\",\n\"nuculid\",\n\"nudate\",\n\"nuddle\",\n\"nude\",\n\"nudely\",\n\"nudge\",\n\"nudger\",\n\"nudiped\",\n\"nudish\",\n\"nudism\",\n\"nudist\",\n\"nudity\",\n\"nugator\",\n\"nuggar\",\n\"nugget\",\n\"nuggety\",\n\"nugify\",\n\"nuke\",\n\"nul\",\n\"null\",\n\"nullah\",\n\"nullify\",\n\"nullism\",\n\"nullity\",\n\"nullo\",\n\"numb\",\n\"number\",\n\"numbing\",\n\"numble\",\n\"numbles\",\n\"numbly\",\n\"numda\",\n\"numdah\",\n\"numen\",\n\"numeral\",\n\"numero\",\n\"nummary\",\n\"nummi\",\n\"nummus\",\n\"numud\",\n\"nun\",\n\"nunatak\",\n\"nunbird\",\n\"nunch\",\n\"nuncio\",\n\"nuncle\",\n\"nundine\",\n\"nunhood\",\n\"nunky\",\n\"nunlet\",\n\"nunlike\",\n\"nunnari\",\n\"nunnery\",\n\"nunni\",\n\"nunnify\",\n\"nunnish\",\n\"nunship\",\n\"nuptial\",\n\"nuque\",\n\"nuraghe\",\n\"nurhag\",\n\"nurly\",\n\"nurse\",\n\"nurser\",\n\"nursery\",\n\"nursing\",\n\"nursle\",\n\"nursy\",\n\"nurture\",\n\"nusfiah\",\n\"nut\",\n\"nutant\",\n\"nutate\",\n\"nutcake\",\n\"nutgall\",\n\"nuthook\",\n\"nutlet\",\n\"nutlike\",\n\"nutmeg\",\n\"nutpick\",\n\"nutria\",\n\"nutrice\",\n\"nutrify\",\n\"nutseed\",\n\"nutted\",\n\"nutter\",\n\"nuttery\",\n\"nuttily\",\n\"nutting\",\n\"nuttish\",\n\"nutty\",\n\"nuzzer\",\n\"nuzzle\",\n\"nyanza\",\n\"nye\",\n\"nylast\",\n\"nylon\",\n\"nymil\",\n\"nymph\",\n\"nympha\",\n\"nymphae\",\n\"nymphal\",\n\"nymphet\",\n\"nymphic\",\n\"nymphid\",\n\"nymphly\",\n\"nyxis\",\n\"o\",\n\"oadal\",\n\"oaf\",\n\"oafdom\",\n\"oafish\",\n\"oak\",\n\"oaken\",\n\"oaklet\",\n\"oaklike\",\n\"oakling\",\n\"oakum\",\n\"oakweb\",\n\"oakwood\",\n\"oaky\",\n\"oam\",\n\"oar\",\n\"oarage\",\n\"oarcock\",\n\"oared\",\n\"oarfish\",\n\"oarhole\",\n\"oarial\",\n\"oaric\",\n\"oaritic\",\n\"oaritis\",\n\"oarium\",\n\"oarless\",\n\"oarlike\",\n\"oarlock\",\n\"oarlop\",\n\"oarman\",\n\"oarsman\",\n\"oarweed\",\n\"oary\",\n\"oasal\",\n\"oasean\",\n\"oases\",\n\"oasis\",\n\"oasitic\",\n\"oast\",\n\"oat\",\n\"oatbin\",\n\"oatcake\",\n\"oatear\",\n\"oaten\",\n\"oatfowl\",\n\"oath\",\n\"oathay\",\n\"oathed\",\n\"oathful\",\n\"oathlet\",\n\"oatland\",\n\"oatlike\",\n\"oatmeal\",\n\"oatseed\",\n\"oaty\",\n\"oban\",\n\"obclude\",\n\"obe\",\n\"obeah\",\n\"obeche\",\n\"obeism\",\n\"obelia\",\n\"obeliac\",\n\"obelial\",\n\"obelion\",\n\"obelisk\",\n\"obelism\",\n\"obelize\",\n\"obelus\",\n\"obese\",\n\"obesely\",\n\"obesity\",\n\"obex\",\n\"obey\",\n\"obeyer\",\n\"obi\",\n\"obispo\",\n\"obit\",\n\"obitual\",\n\"object\",\n\"objure\",\n\"oblate\",\n\"obley\",\n\"oblige\",\n\"obliged\",\n\"obligee\",\n\"obliger\",\n\"obligor\",\n\"oblique\",\n\"oblong\",\n\"obloquy\",\n\"oboe\",\n\"oboist\",\n\"obol\",\n\"obolary\",\n\"obole\",\n\"obolet\",\n\"obolus\",\n\"oboval\",\n\"obovate\",\n\"obovoid\",\n\"obscene\",\n\"obscure\",\n\"obsede\",\n\"obsequy\",\n\"observe\",\n\"obsess\",\n\"obtain\",\n\"obtect\",\n\"obtest\",\n\"obtrude\",\n\"obtund\",\n\"obtuse\",\n\"obverse\",\n\"obvert\",\n\"obviate\",\n\"obvious\",\n\"obvolve\",\n\"ocarina\",\n\"occamy\",\n\"occiput\",\n\"occlude\",\n\"occluse\",\n\"occult\",\n\"occupy\",\n\"occur\",\n\"ocean\",\n\"oceaned\",\n\"oceanet\",\n\"oceanic\",\n\"ocellar\",\n\"ocelli\",\n\"ocellus\",\n\"oceloid\",\n\"ocelot\",\n\"och\",\n\"ochava\",\n\"ochavo\",\n\"ocher\",\n\"ochery\",\n\"ochone\",\n\"ochrea\",\n\"ochro\",\n\"ochroid\",\n\"ochrous\",\n\"ocht\",\n\"ock\",\n\"oclock\",\n\"ocote\",\n\"ocque\",\n\"ocracy\",\n\"ocrea\",\n\"ocreate\",\n\"octad\",\n\"octadic\",\n\"octagon\",\n\"octan\",\n\"octane\",\n\"octant\",\n\"octapla\",\n\"octarch\",\n\"octary\",\n\"octaval\",\n\"octave\",\n\"octavic\",\n\"octavo\",\n\"octene\",\n\"octet\",\n\"octic\",\n\"octine\",\n\"octoad\",\n\"octoate\",\n\"octofid\",\n\"octoic\",\n\"octoid\",\n\"octonal\",\n\"octoon\",\n\"octoped\",\n\"octopi\",\n\"octopod\",\n\"octopus\",\n\"octose\",\n\"octoyl\",\n\"octroi\",\n\"octroy\",\n\"octuor\",\n\"octuple\",\n\"octuply\",\n\"octyl\",\n\"octyne\",\n\"ocuby\",\n\"ocular\",\n\"oculary\",\n\"oculate\",\n\"oculist\",\n\"oculus\",\n\"od\",\n\"oda\",\n\"odacoid\",\n\"odal\",\n\"odalisk\",\n\"odaller\",\n\"odalman\",\n\"odd\",\n\"oddish\",\n\"oddity\",\n\"oddlegs\",\n\"oddly\",\n\"oddman\",\n\"oddment\",\n\"oddness\",\n\"odds\",\n\"oddsman\",\n\"ode\",\n\"odel\",\n\"odelet\",\n\"odeon\",\n\"odeum\",\n\"odic\",\n\"odinite\",\n\"odious\",\n\"odist\",\n\"odium\",\n\"odology\",\n\"odontic\",\n\"odoom\",\n\"odor\",\n\"odorant\",\n\"odorate\",\n\"odored\",\n\"odorful\",\n\"odorize\",\n\"odorous\",\n\"odso\",\n\"odum\",\n\"odyl\",\n\"odylic\",\n\"odylism\",\n\"odylist\",\n\"odylize\",\n\"oe\",\n\"oecist\",\n\"oecus\",\n\"oenin\",\n\"oenolin\",\n\"oenomel\",\n\"oer\",\n\"oersted\",\n\"oes\",\n\"oestrid\",\n\"oestrin\",\n\"oestrum\",\n\"oestrus\",\n\"of\",\n\"off\",\n\"offal\",\n\"offbeat\",\n\"offcast\",\n\"offcome\",\n\"offcut\",\n\"offend\",\n\"offense\",\n\"offer\",\n\"offeree\",\n\"offerer\",\n\"offeror\",\n\"offhand\",\n\"office\",\n\"officer\",\n\"offing\",\n\"offish\",\n\"offlet\",\n\"offlook\",\n\"offscum\",\n\"offset\",\n\"offtake\",\n\"offtype\",\n\"offward\",\n\"oflete\",\n\"oft\",\n\"often\",\n\"oftens\",\n\"ofter\",\n\"oftest\",\n\"oftly\",\n\"oftness\",\n\"ofttime\",\n\"ogaire\",\n\"ogam\",\n\"ogamic\",\n\"ogdoad\",\n\"ogdoas\",\n\"ogee\",\n\"ogeed\",\n\"ogham\",\n\"oghamic\",\n\"ogival\",\n\"ogive\",\n\"ogived\",\n\"ogle\",\n\"ogler\",\n\"ogmic\",\n\"ogre\",\n\"ogreish\",\n\"ogreism\",\n\"ogress\",\n\"ogrish\",\n\"ogrism\",\n\"ogtiern\",\n\"ogum\",\n\"oh\",\n\"ohelo\",\n\"ohia\",\n\"ohm\",\n\"ohmage\",\n\"ohmic\",\n\"oho\",\n\"ohoy\",\n\"oidioid\",\n\"oii\",\n\"oil\",\n\"oilbird\",\n\"oilcan\",\n\"oilcoat\",\n\"oilcup\",\n\"oildom\",\n\"oiled\",\n\"oiler\",\n\"oilery\",\n\"oilfish\",\n\"oilhole\",\n\"oilily\",\n\"oilless\",\n\"oillet\",\n\"oillike\",\n\"oilman\",\n\"oilseed\",\n\"oilskin\",\n\"oilway\",\n\"oily\",\n\"oilyish\",\n\"oime\",\n\"oinomel\",\n\"oint\",\n\"oisin\",\n\"oitava\",\n\"oka\",\n\"okapi\",\n\"okee\",\n\"okenite\",\n\"oket\",\n\"oki\",\n\"okia\",\n\"okonite\",\n\"okra\",\n\"okrug\",\n\"olam\",\n\"olamic\",\n\"old\",\n\"olden\",\n\"older\",\n\"oldish\",\n\"oldland\",\n\"oldness\",\n\"oldster\",\n\"oldwife\",\n\"oleana\",\n\"olease\",\n\"oleate\",\n\"olefin\",\n\"olefine\",\n\"oleic\",\n\"olein\",\n\"olena\",\n\"olenid\",\n\"olent\",\n\"oleo\",\n\"oleose\",\n\"oleous\",\n\"olfact\",\n\"olfacty\",\n\"oliban\",\n\"olid\",\n\"oligist\",\n\"olio\",\n\"olitory\",\n\"oliva\",\n\"olivary\",\n\"olive\",\n\"olived\",\n\"olivet\",\n\"olivil\",\n\"olivile\",\n\"olivine\",\n\"olla\",\n\"ollamh\",\n\"ollapod\",\n\"ollock\",\n\"olm\",\n\"ologist\",\n\"ology\",\n\"olomao\",\n\"olona\",\n\"oloroso\",\n\"olpe\",\n\"oltonde\",\n\"oltunna\",\n\"olycook\",\n\"olykoek\",\n\"om\",\n\"omagra\",\n\"omalgia\",\n\"omao\",\n\"omasum\",\n\"omber\",\n\"omega\",\n\"omegoid\",\n\"omelet\",\n\"omen\",\n\"omened\",\n\"omental\",\n\"omentum\",\n\"omer\",\n\"omicron\",\n\"omina\",\n\"ominous\",\n\"omit\",\n\"omitis\",\n\"omitter\",\n\"omlah\",\n\"omneity\",\n\"omniana\",\n\"omnibus\",\n\"omnific\",\n\"omnify\",\n\"omnist\",\n\"omnium\",\n\"on\",\n\"ona\",\n\"onager\",\n\"onagra\",\n\"onanism\",\n\"onanist\",\n\"onca\",\n\"once\",\n\"oncetta\",\n\"oncia\",\n\"oncin\",\n\"oncome\",\n\"oncosis\",\n\"oncost\",\n\"ondatra\",\n\"ondine\",\n\"ondy\",\n\"one\",\n\"onefold\",\n\"onegite\",\n\"onehow\",\n\"oneiric\",\n\"oneism\",\n\"onement\",\n\"oneness\",\n\"oner\",\n\"onerary\",\n\"onerous\",\n\"onery\",\n\"oneself\",\n\"onetime\",\n\"oneyer\",\n\"onfall\",\n\"onflow\",\n\"ongaro\",\n\"ongoing\",\n\"onicolo\",\n\"onion\",\n\"onionet\",\n\"oniony\",\n\"onium\",\n\"onkos\",\n\"onlay\",\n\"onlepy\",\n\"onliest\",\n\"onlook\",\n\"only\",\n\"onmarch\",\n\"onrush\",\n\"ons\",\n\"onset\",\n\"onshore\",\n\"onside\",\n\"onsight\",\n\"onstand\",\n\"onstead\",\n\"onsweep\",\n\"ontal\",\n\"onto\",\n\"onus\",\n\"onward\",\n\"onwards\",\n\"onycha\",\n\"onychia\",\n\"onychin\",\n\"onym\",\n\"onymal\",\n\"onymity\",\n\"onymize\",\n\"onymous\",\n\"onymy\",\n\"onyx\",\n\"onyxis\",\n\"onza\",\n\"ooblast\",\n\"oocyst\",\n\"oocyte\",\n\"oodles\",\n\"ooecial\",\n\"ooecium\",\n\"oofbird\",\n\"ooftish\",\n\"oofy\",\n\"oogamy\",\n\"oogeny\",\n\"ooglea\",\n\"oogone\",\n\"oograph\",\n\"ooid\",\n\"ooidal\",\n\"oolak\",\n\"oolemma\",\n\"oolite\",\n\"oolitic\",\n\"oolly\",\n\"oologic\",\n\"oology\",\n\"oolong\",\n\"oomancy\",\n\"oometer\",\n\"oometry\",\n\"oons\",\n\"oont\",\n\"oopak\",\n\"oophore\",\n\"oophyte\",\n\"ooplasm\",\n\"ooplast\",\n\"oopod\",\n\"oopodal\",\n\"oorali\",\n\"oord\",\n\"ooscope\",\n\"ooscopy\",\n\"oosperm\",\n\"oospore\",\n\"ootheca\",\n\"ootid\",\n\"ootype\",\n\"ooze\",\n\"oozily\",\n\"oozooid\",\n\"oozy\",\n\"opacate\",\n\"opacify\",\n\"opacite\",\n\"opacity\",\n\"opacous\",\n\"opah\",\n\"opal\",\n\"opaled\",\n\"opaline\",\n\"opalish\",\n\"opalize\",\n\"opaloid\",\n\"opaque\",\n\"ope\",\n\"opelet\",\n\"open\",\n\"opener\",\n\"opening\",\n\"openly\",\n\"opera\",\n\"operae\",\n\"operand\",\n\"operant\",\n\"operate\",\n\"opercle\",\n\"operose\",\n\"ophic\",\n\"ophioid\",\n\"ophite\",\n\"ophitic\",\n\"ophryon\",\n\"opianic\",\n\"opianyl\",\n\"opiate\",\n\"opiatic\",\n\"opiism\",\n\"opinant\",\n\"opine\",\n\"opiner\",\n\"opinion\",\n\"opium\",\n\"opossum\",\n\"oppidan\",\n\"oppose\",\n\"opposed\",\n\"opposer\",\n\"opposit\",\n\"oppress\",\n\"oppugn\",\n\"opsonic\",\n\"opsonin\",\n\"opsy\",\n\"opt\",\n\"optable\",\n\"optably\",\n\"optant\",\n\"optate\",\n\"optic\",\n\"optical\",\n\"opticon\",\n\"optics\",\n\"optimal\",\n\"optime\",\n\"optimum\",\n\"option\",\n\"optive\",\n\"opulent\",\n\"opulus\",\n\"opus\",\n\"oquassa\",\n\"or\",\n\"ora\",\n\"orach\",\n\"oracle\",\n\"orad\",\n\"orage\",\n\"oral\",\n\"oraler\",\n\"oralism\",\n\"oralist\",\n\"orality\",\n\"oralize\",\n\"orally\",\n\"oralogy\",\n\"orang\",\n\"orange\",\n\"oranger\",\n\"orangey\",\n\"orant\",\n\"orarian\",\n\"orarion\",\n\"orarium\",\n\"orary\",\n\"orate\",\n\"oration\",\n\"orator\",\n\"oratory\",\n\"oratrix\",\n\"orb\",\n\"orbed\",\n\"orbic\",\n\"orbical\",\n\"orbicle\",\n\"orbific\",\n\"orbit\",\n\"orbital\",\n\"orbitar\",\n\"orbite\",\n\"orbless\",\n\"orblet\",\n\"orby\",\n\"orc\",\n\"orcanet\",\n\"orcein\",\n\"orchard\",\n\"orchat\",\n\"orchel\",\n\"orchic\",\n\"orchid\",\n\"orchil\",\n\"orcin\",\n\"orcinol\",\n\"ordain\",\n\"ordeal\",\n\"order\",\n\"ordered\",\n\"orderer\",\n\"orderly\",\n\"ordinal\",\n\"ordinar\",\n\"ordinee\",\n\"ordines\",\n\"ordu\",\n\"ordure\",\n\"ore\",\n\"oread\",\n\"orectic\",\n\"orellin\",\n\"oreman\",\n\"orenda\",\n\"oreweed\",\n\"orewood\",\n\"orexis\",\n\"orf\",\n\"orfgild\",\n\"organ\",\n\"organal\",\n\"organdy\",\n\"organer\",\n\"organic\",\n\"organon\",\n\"organry\",\n\"organum\",\n\"orgasm\",\n\"orgeat\",\n\"orgia\",\n\"orgiac\",\n\"orgiacs\",\n\"orgiasm\",\n\"orgiast\",\n\"orgic\",\n\"orgue\",\n\"orgy\",\n\"orgyia\",\n\"oribi\",\n\"oriel\",\n\"oriency\",\n\"orient\",\n\"orifice\",\n\"oriform\",\n\"origan\",\n\"origin\",\n\"orignal\",\n\"orihon\",\n\"orillon\",\n\"oriole\",\n\"orison\",\n\"oristic\",\n\"orle\",\n\"orlean\",\n\"orlet\",\n\"orlo\",\n\"orlop\",\n\"ormer\",\n\"ormolu\",\n\"orna\",\n\"ornate\",\n\"ornery\",\n\"ornis\",\n\"ornoite\",\n\"oroanal\",\n\"orogen\",\n\"orogeny\",\n\"oroide\",\n\"orology\",\n\"oronoco\",\n\"orotund\",\n\"orphan\",\n\"orpheon\",\n\"orpheum\",\n\"orphrey\",\n\"orpine\",\n\"orrery\",\n\"orrhoid\",\n\"orris\",\n\"orsel\",\n\"orselle\",\n\"ort\",\n\"ortalid\",\n\"ortet\",\n\"orthal\",\n\"orthian\",\n\"orthic\",\n\"orthid\",\n\"orthite\",\n\"ortho\",\n\"orthose\",\n\"orthron\",\n\"ortiga\",\n\"ortive\",\n\"ortolan\",\n\"ortygan\",\n\"ory\",\n\"oryssid\",\n\"os\",\n\"osamin\",\n\"osamine\",\n\"osazone\",\n\"oscella\",\n\"oscheal\",\n\"oscin\",\n\"oscine\",\n\"oscnode\",\n\"oscular\",\n\"oscule\",\n\"osculum\",\n\"ose\",\n\"osela\",\n\"oshac\",\n\"oside\",\n\"osier\",\n\"osiered\",\n\"osiery\",\n\"osmate\",\n\"osmatic\",\n\"osmesis\",\n\"osmetic\",\n\"osmic\",\n\"osmin\",\n\"osmina\",\n\"osmious\",\n\"osmium\",\n\"osmose\",\n\"osmosis\",\n\"osmotic\",\n\"osmous\",\n\"osmund\",\n\"osone\",\n\"osophy\",\n\"osprey\",\n\"ossal\",\n\"osse\",\n\"ossein\",\n\"osselet\",\n\"osseous\",\n\"ossicle\",\n\"ossific\",\n\"ossify\",\n\"ossuary\",\n\"osteal\",\n\"ostein\",\n\"ostemia\",\n\"ostent\",\n\"osteoid\",\n\"osteoma\",\n\"ostial\",\n\"ostiary\",\n\"ostiate\",\n\"ostiole\",\n\"ostitis\",\n\"ostium\",\n\"ostmark\",\n\"ostosis\",\n\"ostrich\",\n\"otalgia\",\n\"otalgic\",\n\"otalgy\",\n\"otarian\",\n\"otarine\",\n\"otary\",\n\"otate\",\n\"other\",\n\"othmany\",\n\"otiant\",\n\"otiatry\",\n\"otic\",\n\"otidine\",\n\"otidium\",\n\"otiose\",\n\"otitic\",\n\"otitis\",\n\"otkon\",\n\"otocyst\",\n\"otolite\",\n\"otolith\",\n\"otology\",\n\"otosis\",\n\"ototomy\",\n\"ottar\",\n\"otter\",\n\"otterer\",\n\"otto\",\n\"oturia\",\n\"ouabain\",\n\"ouabaio\",\n\"ouabe\",\n\"ouakari\",\n\"ouch\",\n\"ouenite\",\n\"ouf\",\n\"ough\",\n\"ought\",\n\"oughtnt\",\n\"oukia\",\n\"oulap\",\n\"ounce\",\n\"ounds\",\n\"ouphe\",\n\"ouphish\",\n\"our\",\n\"ourie\",\n\"ouroub\",\n\"ours\",\n\"ourself\",\n\"oust\",\n\"ouster\",\n\"out\",\n\"outact\",\n\"outage\",\n\"outarde\",\n\"outask\",\n\"outawe\",\n\"outback\",\n\"outbake\",\n\"outban\",\n\"outbar\",\n\"outbark\",\n\"outbawl\",\n\"outbeam\",\n\"outbear\",\n\"outbeg\",\n\"outbent\",\n\"outbid\",\n\"outblot\",\n\"outblow\",\n\"outbond\",\n\"outbook\",\n\"outborn\",\n\"outbow\",\n\"outbowl\",\n\"outbox\",\n\"outbrag\",\n\"outbray\",\n\"outbred\",\n\"outbud\",\n\"outbulk\",\n\"outburn\",\n\"outbuy\",\n\"outbuzz\",\n\"outby\",\n\"outcant\",\n\"outcase\",\n\"outcast\",\n\"outcity\",\n\"outcome\",\n\"outcrop\",\n\"outcrow\",\n\"outcry\",\n\"outcull\",\n\"outcure\",\n\"outcut\",\n\"outdare\",\n\"outdate\",\n\"outdo\",\n\"outdoer\",\n\"outdoor\",\n\"outdraw\",\n\"outdure\",\n\"outeat\",\n\"outecho\",\n\"outed\",\n\"outedge\",\n\"outen\",\n\"outer\",\n\"outerly\",\n\"outeye\",\n\"outeyed\",\n\"outface\",\n\"outfall\",\n\"outfame\",\n\"outfast\",\n\"outfawn\",\n\"outfeat\",\n\"outfish\",\n\"outfit\",\n\"outflow\",\n\"outflue\",\n\"outflux\",\n\"outfly\",\n\"outfold\",\n\"outfool\",\n\"outfoot\",\n\"outform\",\n\"outfort\",\n\"outgain\",\n\"outgame\",\n\"outgang\",\n\"outgas\",\n\"outgate\",\n\"outgaze\",\n\"outgive\",\n\"outglad\",\n\"outglow\",\n\"outgnaw\",\n\"outgo\",\n\"outgoer\",\n\"outgone\",\n\"outgrin\",\n\"outgrow\",\n\"outgun\",\n\"outgush\",\n\"outhaul\",\n\"outhear\",\n\"outheel\",\n\"outher\",\n\"outhire\",\n\"outhiss\",\n\"outhit\",\n\"outhold\",\n\"outhowl\",\n\"outhue\",\n\"outhunt\",\n\"outhurl\",\n\"outhut\",\n\"outhymn\",\n\"outing\",\n\"outish\",\n\"outjazz\",\n\"outjest\",\n\"outjet\",\n\"outjinx\",\n\"outjump\",\n\"outjut\",\n\"outkick\",\n\"outkill\",\n\"outking\",\n\"outkiss\",\n\"outknee\",\n\"outlaid\",\n\"outland\",\n\"outlash\",\n\"outlast\",\n\"outlaw\",\n\"outlay\",\n\"outlean\",\n\"outleap\",\n\"outler\",\n\"outlet\",\n\"outlie\",\n\"outlier\",\n\"outlimb\",\n\"outlimn\",\n\"outline\",\n\"outlip\",\n\"outlive\",\n\"outlook\",\n\"outlord\",\n\"outlove\",\n\"outlung\",\n\"outly\",\n\"outman\",\n\"outmate\",\n\"outmode\",\n\"outmost\",\n\"outmove\",\n\"outname\",\n\"outness\",\n\"outnook\",\n\"outoven\",\n\"outpace\",\n\"outpage\",\n\"outpart\",\n\"outpass\",\n\"outpath\",\n\"outpay\",\n\"outpeal\",\n\"outpeep\",\n\"outpeer\",\n\"outpick\",\n\"outpipe\",\n\"outpity\",\n\"outplan\",\n\"outplay\",\n\"outplod\",\n\"outplot\",\n\"outpoll\",\n\"outpomp\",\n\"outpop\",\n\"outport\",\n\"outpost\",\n\"outpour\",\n\"outpray\",\n\"outpry\",\n\"outpull\",\n\"outpurl\",\n\"outpush\",\n\"output\",\n\"outrace\",\n\"outrage\",\n\"outrail\",\n\"outrank\",\n\"outrant\",\n\"outrap\",\n\"outrate\",\n\"outrave\",\n\"outray\",\n\"outre\",\n\"outread\",\n\"outrede\",\n\"outrick\",\n\"outride\",\n\"outrig\",\n\"outring\",\n\"outroar\",\n\"outroll\",\n\"outroot\",\n\"outrove\",\n\"outrow\",\n\"outrun\",\n\"outrush\",\n\"outsail\",\n\"outsay\",\n\"outsea\",\n\"outseam\",\n\"outsee\",\n\"outseek\",\n\"outsell\",\n\"outsert\",\n\"outset\",\n\"outshot\",\n\"outshow\",\n\"outshut\",\n\"outside\",\n\"outsift\",\n\"outsigh\",\n\"outsin\",\n\"outsing\",\n\"outsit\",\n\"outsize\",\n\"outskip\",\n\"outsoar\",\n\"outsole\",\n\"outspan\",\n\"outspin\",\n\"outspit\",\n\"outspue\",\n\"outstay\",\n\"outstep\",\n\"outsuck\",\n\"outsulk\",\n\"outsum\",\n\"outswim\",\n\"outtalk\",\n\"outtask\",\n\"outtear\",\n\"outtell\",\n\"outtire\",\n\"outtoil\",\n\"outtop\",\n\"outtrot\",\n\"outturn\",\n\"outvie\",\n\"outvier\",\n\"outvote\",\n\"outwait\",\n\"outwake\",\n\"outwale\",\n\"outwalk\",\n\"outwall\",\n\"outwar\",\n\"outward\",\n\"outwash\",\n\"outwave\",\n\"outwear\",\n\"outweed\",\n\"outweep\",\n\"outwell\",\n\"outwent\",\n\"outwick\",\n\"outwile\",\n\"outwill\",\n\"outwind\",\n\"outwing\",\n\"outwish\",\n\"outwit\",\n\"outwith\",\n\"outwoe\",\n\"outwood\",\n\"outword\",\n\"outwore\",\n\"outwork\",\n\"outworn\",\n\"outyard\",\n\"outyell\",\n\"outyelp\",\n\"outzany\",\n\"ouzel\",\n\"ova\",\n\"oval\",\n\"ovalish\",\n\"ovalize\",\n\"ovally\",\n\"ovaloid\",\n\"ovant\",\n\"ovarial\",\n\"ovarian\",\n\"ovarin\",\n\"ovarium\",\n\"ovary\",\n\"ovate\",\n\"ovated\",\n\"ovately\",\n\"ovation\",\n\"oven\",\n\"ovenful\",\n\"ovenly\",\n\"ovenman\",\n\"over\",\n\"overact\",\n\"overage\",\n\"overall\",\n\"overapt\",\n\"overarm\",\n\"overawe\",\n\"overawn\",\n\"overbet\",\n\"overbid\",\n\"overbig\",\n\"overbit\",\n\"overbow\",\n\"overbuy\",\n\"overby\",\n\"overcap\",\n\"overcow\",\n\"overcoy\",\n\"overcry\",\n\"overcup\",\n\"overcut\",\n\"overdo\",\n\"overdry\",\n\"overdue\",\n\"overdye\",\n\"overeat\",\n\"overegg\",\n\"overeye\",\n\"overfag\",\n\"overfar\",\n\"overfat\",\n\"overfed\",\n\"overfee\",\n\"overfew\",\n\"overfit\",\n\"overfix\",\n\"overfly\",\n\"overget\",\n\"overgo\",\n\"overgod\",\n\"overgun\",\n\"overhit\",\n\"overhot\",\n\"overink\",\n\"overjob\",\n\"overjoy\",\n\"overlap\",\n\"overlax\",\n\"overlay\",\n\"overleg\",\n\"overlie\",\n\"overlip\",\n\"overlow\",\n\"overly\",\n\"overman\",\n\"overmix\",\n\"overnet\",\n\"overnew\",\n\"overpay\",\n\"overpet\",\n\"overply\",\n\"overpot\",\n\"overrim\",\n\"overrun\",\n\"oversad\",\n\"oversea\",\n\"oversee\",\n\"overset\",\n\"oversew\",\n\"oversot\",\n\"oversow\",\n\"overt\",\n\"overtax\",\n\"overtip\",\n\"overtly\",\n\"overtoe\",\n\"overtop\",\n\"overuse\",\n\"overway\",\n\"overweb\",\n\"overwet\",\n\"overwin\",\n\"ovest\",\n\"ovey\",\n\"ovicell\",\n\"ovicide\",\n\"ovicyst\",\n\"oviduct\",\n\"oviform\",\n\"ovigerm\",\n\"ovile\",\n\"ovine\",\n\"ovinia\",\n\"ovipara\",\n\"ovisac\",\n\"ovism\",\n\"ovist\",\n\"ovistic\",\n\"ovocyte\",\n\"ovoid\",\n\"ovoidal\",\n\"ovolo\",\n\"ovology\",\n\"ovular\",\n\"ovulary\",\n\"ovulate\",\n\"ovule\",\n\"ovulist\",\n\"ovum\",\n\"ow\",\n\"owd\",\n\"owe\",\n\"owelty\",\n\"ower\",\n\"owerby\",\n\"owght\",\n\"owing\",\n\"owk\",\n\"owl\",\n\"owldom\",\n\"owler\",\n\"owlery\",\n\"owlet\",\n\"owlhead\",\n\"owling\",\n\"owlish\",\n\"owlism\",\n\"owllike\",\n\"owly\",\n\"own\",\n\"owner\",\n\"ownhood\",\n\"ownness\",\n\"ownself\",\n\"owrehip\",\n\"owrelay\",\n\"owse\",\n\"owsen\",\n\"owser\",\n\"owtchah\",\n\"ox\",\n\"oxacid\",\n\"oxalan\",\n\"oxalate\",\n\"oxalic\",\n\"oxalite\",\n\"oxalyl\",\n\"oxamate\",\n\"oxamic\",\n\"oxamid\",\n\"oxamide\",\n\"oxan\",\n\"oxanate\",\n\"oxane\",\n\"oxanic\",\n\"oxazine\",\n\"oxazole\",\n\"oxbane\",\n\"oxberry\",\n\"oxbird\",\n\"oxbiter\",\n\"oxblood\",\n\"oxbow\",\n\"oxboy\",\n\"oxbrake\",\n\"oxcart\",\n\"oxcheek\",\n\"oxea\",\n\"oxeate\",\n\"oxen\",\n\"oxeote\",\n\"oxer\",\n\"oxetone\",\n\"oxeye\",\n\"oxfly\",\n\"oxgang\",\n\"oxgoad\",\n\"oxhead\",\n\"oxheal\",\n\"oxheart\",\n\"oxhide\",\n\"oxhoft\",\n\"oxhorn\",\n\"oxhouse\",\n\"oxhuvud\",\n\"oxidant\",\n\"oxidase\",\n\"oxidate\",\n\"oxide\",\n\"oxidic\",\n\"oxidize\",\n\"oximate\",\n\"oxime\",\n\"oxland\",\n\"oxlike\",\n\"oxlip\",\n\"oxman\",\n\"oxonic\",\n\"oxonium\",\n\"oxozone\",\n\"oxphony\",\n\"oxreim\",\n\"oxshoe\",\n\"oxskin\",\n\"oxtail\",\n\"oxter\",\n\"oxwort\",\n\"oxy\",\n\"oxyacid\",\n\"oxygas\",\n\"oxygen\",\n\"oxyl\",\n\"oxymel\",\n\"oxyntic\",\n\"oxyopia\",\n\"oxysalt\",\n\"oxytone\",\n\"oyapock\",\n\"oyer\",\n\"oyster\",\n\"ozena\",\n\"ozonate\",\n\"ozone\",\n\"ozoned\",\n\"ozonic\",\n\"ozonide\",\n\"ozonify\",\n\"ozonize\",\n\"ozonous\",\n\"ozophen\",\n\"ozotype\",\n\"p\",\n\"pa\",\n\"paal\",\n\"paar\",\n\"paauw\",\n\"pabble\",\n\"pablo\",\n\"pabouch\",\n\"pabular\",\n\"pabulum\",\n\"pac\",\n\"paca\",\n\"pacable\",\n\"pacate\",\n\"pacay\",\n\"pacaya\",\n\"pace\",\n\"paced\",\n\"pacer\",\n\"pachak\",\n\"pachisi\",\n\"pacific\",\n\"pacify\",\n\"pack\",\n\"package\",\n\"packer\",\n\"packery\",\n\"packet\",\n\"packly\",\n\"packman\",\n\"packway\",\n\"paco\",\n\"pact\",\n\"paction\",\n\"pad\",\n\"padder\",\n\"padding\",\n\"paddle\",\n\"paddled\",\n\"paddler\",\n\"paddock\",\n\"paddy\",\n\"padella\",\n\"padfoot\",\n\"padge\",\n\"padle\",\n\"padlike\",\n\"padlock\",\n\"padnag\",\n\"padre\",\n\"padtree\",\n\"paean\",\n\"paegel\",\n\"paegle\",\n\"paenula\",\n\"paeon\",\n\"paeonic\",\n\"paga\",\n\"pagan\",\n\"paganic\",\n\"paganly\",\n\"paganry\",\n\"page\",\n\"pageant\",\n\"pagedom\",\n\"pageful\",\n\"pager\",\n\"pagina\",\n\"paginal\",\n\"pagoda\",\n\"pagrus\",\n\"pagurid\",\n\"pagus\",\n\"pah\",\n\"paha\",\n\"pahi\",\n\"pahlavi\",\n\"pahmi\",\n\"paho\",\n\"pahutan\",\n\"paigle\",\n\"paik\",\n\"pail\",\n\"pailful\",\n\"pailou\",\n\"pain\",\n\"pained\",\n\"painful\",\n\"paining\",\n\"paint\",\n\"painted\",\n\"painter\",\n\"painty\",\n\"paip\",\n\"pair\",\n\"paired\",\n\"pairer\",\n\"pais\",\n\"paisa\",\n\"paiwari\",\n\"pajama\",\n\"pajock\",\n\"pakchoi\",\n\"pakeha\",\n\"paktong\",\n\"pal\",\n\"palace\",\n\"palaced\",\n\"paladin\",\n\"palaite\",\n\"palama\",\n\"palame\",\n\"palanka\",\n\"palar\",\n\"palas\",\n\"palatal\",\n\"palate\",\n\"palated\",\n\"palatic\",\n\"palaver\",\n\"palay\",\n\"palazzi\",\n\"palch\",\n\"pale\",\n\"palea\",\n\"paleate\",\n\"paled\",\n\"palely\",\n\"paleola\",\n\"paler\",\n\"palet\",\n\"paletot\",\n\"palette\",\n\"paletz\",\n\"palfrey\",\n\"palgat\",\n\"pali\",\n\"palikar\",\n\"palila\",\n\"palinal\",\n\"paling\",\n\"palisfy\",\n\"palish\",\n\"palkee\",\n\"pall\",\n\"palla\",\n\"pallae\",\n\"pallah\",\n\"pallall\",\n\"palled\",\n\"pallet\",\n\"palli\",\n\"pallial\",\n\"pallid\",\n\"pallion\",\n\"pallium\",\n\"pallone\",\n\"pallor\",\n\"pally\",\n\"palm\",\n\"palma\",\n\"palmad\",\n\"palmar\",\n\"palmary\",\n\"palmate\",\n\"palmed\",\n\"palmer\",\n\"palmery\",\n\"palmful\",\n\"palmist\",\n\"palmite\",\n\"palmito\",\n\"palmo\",\n\"palmula\",\n\"palmus\",\n\"palmy\",\n\"palmyra\",\n\"palolo\",\n\"palp\",\n\"palpal\",\n\"palpate\",\n\"palped\",\n\"palpi\",\n\"palpon\",\n\"palpus\",\n\"palsied\",\n\"palster\",\n\"palsy\",\n\"palt\",\n\"palter\",\n\"paltry\",\n\"paludal\",\n\"paludic\",\n\"palule\",\n\"palulus\",\n\"palus\",\n\"paly\",\n\"pam\",\n\"pament\",\n\"pamment\",\n\"pampas\",\n\"pampean\",\n\"pamper\",\n\"pampero\",\n\"pampre\",\n\"pan\",\n\"panace\",\n\"panacea\",\n\"panache\",\n\"panada\",\n\"panade\",\n\"panama\",\n\"panaris\",\n\"panary\",\n\"panax\",\n\"pancake\",\n\"pand\",\n\"panda\",\n\"pandal\",\n\"pandan\",\n\"pandect\",\n\"pandemy\",\n\"pander\",\n\"pandita\",\n\"pandle\",\n\"pandora\",\n\"pandour\",\n\"pandrop\",\n\"pandura\",\n\"pandy\",\n\"pane\",\n\"paned\",\n\"paneity\",\n\"panel\",\n\"panela\",\n\"paneler\",\n\"panfil\",\n\"panfish\",\n\"panful\",\n\"pang\",\n\"pangamy\",\n\"pangane\",\n\"pangen\",\n\"pangene\",\n\"pangful\",\n\"pangi\",\n\"panhead\",\n\"panic\",\n\"panical\",\n\"panicky\",\n\"panicle\",\n\"panisc\",\n\"panisca\",\n\"panisic\",\n\"pank\",\n\"pankin\",\n\"panman\",\n\"panmixy\",\n\"panmug\",\n\"pannade\",\n\"pannage\",\n\"pannam\",\n\"panne\",\n\"pannel\",\n\"panner\",\n\"pannery\",\n\"pannier\",\n\"panning\",\n\"pannose\",\n\"pannum\",\n\"pannus\",\n\"panocha\",\n\"panoche\",\n\"panoply\",\n\"panoram\",\n\"panse\",\n\"panside\",\n\"pansied\",\n\"pansy\",\n\"pant\",\n\"pantas\",\n\"panter\",\n\"panther\",\n\"pantie\",\n\"panties\",\n\"pantile\",\n\"panting\",\n\"pantle\",\n\"pantler\",\n\"panto\",\n\"pantod\",\n\"panton\",\n\"pantoon\",\n\"pantoum\",\n\"pantry\",\n\"pants\",\n\"pantun\",\n\"panty\",\n\"panung\",\n\"panurgy\",\n\"panyar\",\n\"paolo\",\n\"paon\",\n\"pap\",\n\"papa\",\n\"papable\",\n\"papabot\",\n\"papacy\",\n\"papain\",\n\"papal\",\n\"papally\",\n\"papalty\",\n\"papane\",\n\"papaw\",\n\"papaya\",\n\"papboat\",\n\"pape\",\n\"paper\",\n\"papered\",\n\"paperer\",\n\"papern\",\n\"papery\",\n\"papess\",\n\"papey\",\n\"papilla\",\n\"papion\",\n\"papish\",\n\"papism\",\n\"papist\",\n\"papize\",\n\"papless\",\n\"papmeat\",\n\"papoose\",\n\"pappi\",\n\"pappose\",\n\"pappox\",\n\"pappus\",\n\"pappy\",\n\"papreg\",\n\"paprica\",\n\"paprika\",\n\"papula\",\n\"papular\",\n\"papule\",\n\"papyr\",\n\"papyral\",\n\"papyri\",\n\"papyrin\",\n\"papyrus\",\n\"paquet\",\n\"par\",\n\"para\",\n\"parable\",\n\"paracme\",\n\"parade\",\n\"parader\",\n\"parado\",\n\"parados\",\n\"paradox\",\n\"parafle\",\n\"parage\",\n\"paragon\",\n\"parah\",\n\"paraiba\",\n\"parale\",\n\"param\",\n\"paramo\",\n\"parang\",\n\"parao\",\n\"parapet\",\n\"paraph\",\n\"parapod\",\n\"pararek\",\n\"parasol\",\n\"paraspy\",\n\"parate\",\n\"paraxon\",\n\"parbake\",\n\"parboil\",\n\"parcel\",\n\"parch\",\n\"parcher\",\n\"parchy\",\n\"parcook\",\n\"pard\",\n\"pardao\",\n\"parded\",\n\"pardesi\",\n\"pardine\",\n\"pardner\",\n\"pardo\",\n\"pardon\",\n\"pare\",\n\"parel\",\n\"parella\",\n\"paren\",\n\"parent\",\n\"parer\",\n\"paresis\",\n\"paretic\",\n\"parfait\",\n\"pargana\",\n\"parge\",\n\"parget\",\n\"pargo\",\n\"pari\",\n\"pariah\",\n\"parial\",\n\"parian\",\n\"paries\",\n\"parify\",\n\"parilla\",\n\"parine\",\n\"paring\",\n\"parish\",\n\"parisis\",\n\"parison\",\n\"parity\",\n\"park\",\n\"parka\",\n\"parkee\",\n\"parker\",\n\"parkin\",\n\"parking\",\n\"parkish\",\n\"parkway\",\n\"parky\",\n\"parlay\",\n\"parle\",\n\"parley\",\n\"parling\",\n\"parlish\",\n\"parlor\",\n\"parlous\",\n\"parly\",\n\"parma\",\n\"parmak\",\n\"parnas\",\n\"parnel\",\n\"paroch\",\n\"parode\",\n\"parodic\",\n\"parodos\",\n\"parody\",\n\"paroecy\",\n\"parol\",\n\"parole\",\n\"parolee\",\n\"paroli\",\n\"paronym\",\n\"parotic\",\n\"parotid\",\n\"parotis\",\n\"parous\",\n\"parpal\",\n\"parquet\",\n\"parr\",\n\"parrel\",\n\"parrier\",\n\"parrock\",\n\"parrot\",\n\"parroty\",\n\"parry\",\n\"parse\",\n\"parsec\",\n\"parser\",\n\"parsley\",\n\"parsnip\",\n\"parson\",\n\"parsony\",\n\"part\",\n\"partake\",\n\"partan\",\n\"parted\",\n\"parter\",\n\"partial\",\n\"partile\",\n\"partite\",\n\"partlet\",\n\"partly\",\n\"partner\",\n\"parto\",\n\"partook\",\n\"parture\",\n\"party\",\n\"parulis\",\n\"parure\",\n\"paruria\",\n\"parvenu\",\n\"parvis\",\n\"parvule\",\n\"pasan\",\n\"pasang\",\n\"paschal\",\n\"pascual\",\n\"pash\",\n\"pasha\",\n\"pashm\",\n\"pasi\",\n\"pasmo\",\n\"pasquil\",\n\"pasquin\",\n\"pass\",\n\"passade\",\n\"passado\",\n\"passage\",\n\"passant\",\n\"passe\",\n\"passee\",\n\"passen\",\n\"passer\",\n\"passewa\",\n\"passing\",\n\"passion\",\n\"passir\",\n\"passive\",\n\"passkey\",\n\"passman\",\n\"passo\",\n\"passout\",\n\"passus\",\n\"passway\",\n\"past\",\n\"paste\",\n\"pasted\",\n\"pastel\",\n\"paster\",\n\"pastern\",\n\"pasteur\",\n\"pastil\",\n\"pastile\",\n\"pastime\",\n\"pasting\",\n\"pastor\",\n\"pastose\",\n\"pastry\",\n\"pasture\",\n\"pasty\",\n\"pasul\",\n\"pat\",\n\"pata\",\n\"pataca\",\n\"patacao\",\n\"pataco\",\n\"patagon\",\n\"pataka\",\n\"patamar\",\n\"patao\",\n\"patapat\",\n\"pataque\",\n\"patas\",\n\"patball\",\n\"patch\",\n\"patcher\",\n\"patchy\",\n\"pate\",\n\"patefy\",\n\"patel\",\n\"patella\",\n\"paten\",\n\"patency\",\n\"patener\",\n\"patent\",\n\"pater\",\n\"patera\",\n\"patesi\",\n\"path\",\n\"pathed\",\n\"pathema\",\n\"pathic\",\n\"pathlet\",\n\"pathos\",\n\"pathway\",\n\"pathy\",\n\"patible\",\n\"patient\",\n\"patina\",\n\"patine\",\n\"patined\",\n\"patio\",\n\"patly\",\n\"patness\",\n\"pato\",\n\"patois\",\n\"patola\",\n\"patonce\",\n\"patria\",\n\"patrial\",\n\"patrice\",\n\"patrico\",\n\"patrin\",\n\"patriot\",\n\"patrist\",\n\"patrix\",\n\"patrol\",\n\"patron\",\n\"patroon\",\n\"patta\",\n\"patte\",\n\"pattee\",\n\"patten\",\n\"patter\",\n\"pattern\",\n\"pattu\",\n\"patty\",\n\"patu\",\n\"patwari\",\n\"paty\",\n\"pau\",\n\"paucify\",\n\"paucity\",\n\"paughty\",\n\"paukpan\",\n\"paular\",\n\"paulie\",\n\"paulin\",\n\"paunch\",\n\"paunchy\",\n\"paup\",\n\"pauper\",\n\"pausal\",\n\"pause\",\n\"pauser\",\n\"paussid\",\n\"paut\",\n\"pauxi\",\n\"pavage\",\n\"pavan\",\n\"pavane\",\n\"pave\",\n\"paver\",\n\"pavid\",\n\"pavier\",\n\"paving\",\n\"pavior\",\n\"paviour\",\n\"pavis\",\n\"paviser\",\n\"pavisor\",\n\"pavy\",\n\"paw\",\n\"pawdite\",\n\"pawer\",\n\"pawing\",\n\"pawk\",\n\"pawkery\",\n\"pawkily\",\n\"pawkrie\",\n\"pawky\",\n\"pawl\",\n\"pawn\",\n\"pawnage\",\n\"pawnee\",\n\"pawner\",\n\"pawnie\",\n\"pawnor\",\n\"pawpaw\",\n\"pax\",\n\"paxilla\",\n\"paxiuba\",\n\"paxwax\",\n\"pay\",\n\"payable\",\n\"payably\",\n\"payday\",\n\"payed\",\n\"payee\",\n\"payeny\",\n\"payer\",\n\"paying\",\n\"payment\",\n\"paynim\",\n\"payoff\",\n\"payong\",\n\"payor\",\n\"payroll\",\n\"pea\",\n\"peace\",\n\"peach\",\n\"peachen\",\n\"peacher\",\n\"peachy\",\n\"peacoat\",\n\"peacock\",\n\"peacod\",\n\"peafowl\",\n\"peag\",\n\"peage\",\n\"peahen\",\n\"peai\",\n\"peaiism\",\n\"peak\",\n\"peaked\",\n\"peaker\",\n\"peakily\",\n\"peaking\",\n\"peakish\",\n\"peaky\",\n\"peal\",\n\"pealike\",\n\"pean\",\n\"peanut\",\n\"pear\",\n\"pearl\",\n\"pearled\",\n\"pearler\",\n\"pearlet\",\n\"pearlin\",\n\"pearly\",\n\"peart\",\n\"pearten\",\n\"peartly\",\n\"peasant\",\n\"peasen\",\n\"peason\",\n\"peasy\",\n\"peat\",\n\"peatery\",\n\"peatman\",\n\"peaty\",\n\"peavey\",\n\"peavy\",\n\"peba\",\n\"pebble\",\n\"pebbled\",\n\"pebbly\",\n\"pebrine\",\n\"pecan\",\n\"peccant\",\n\"peccary\",\n\"peccavi\",\n\"pech\",\n\"pecht\",\n\"pecite\",\n\"peck\",\n\"pecked\",\n\"pecker\",\n\"pecket\",\n\"peckful\",\n\"peckish\",\n\"peckle\",\n\"peckled\",\n\"peckly\",\n\"pecky\",\n\"pectase\",\n\"pectate\",\n\"pecten\",\n\"pectic\",\n\"pectin\",\n\"pectize\",\n\"pectora\",\n\"pectose\",\n\"pectous\",\n\"pectus\",\n\"ped\",\n\"peda\",\n\"pedage\",\n\"pedagog\",\n\"pedal\",\n\"pedaler\",\n\"pedant\",\n\"pedary\",\n\"pedate\",\n\"pedated\",\n\"pedder\",\n\"peddle\",\n\"peddler\",\n\"pedee\",\n\"pedes\",\n\"pedesis\",\n\"pedicab\",\n\"pedicel\",\n\"pedicle\",\n\"pedion\",\n\"pedlar\",\n\"pedlary\",\n\"pedocal\",\n\"pedrail\",\n\"pedrero\",\n\"pedro\",\n\"pedule\",\n\"pedum\",\n\"pee\",\n\"peed\",\n\"peek\",\n\"peel\",\n\"peele\",\n\"peeled\",\n\"peeler\",\n\"peeling\",\n\"peelman\",\n\"peen\",\n\"peenge\",\n\"peeoy\",\n\"peep\",\n\"peeper\",\n\"peepeye\",\n\"peepy\",\n\"peer\",\n\"peerage\",\n\"peerdom\",\n\"peeress\",\n\"peerie\",\n\"peerly\",\n\"peery\",\n\"peesash\",\n\"peeve\",\n\"peeved\",\n\"peever\",\n\"peevish\",\n\"peewee\",\n\"peg\",\n\"pega\",\n\"pegall\",\n\"pegasid\",\n\"pegbox\",\n\"pegged\",\n\"pegger\",\n\"pegging\",\n\"peggle\",\n\"peggy\",\n\"pegless\",\n\"peglet\",\n\"peglike\",\n\"pegman\",\n\"pegwood\",\n\"peho\",\n\"peine\",\n\"peisage\",\n\"peise\",\n\"peiser\",\n\"peixere\",\n\"pekan\",\n\"pekin\",\n\"pekoe\",\n\"peladic\",\n\"pelage\",\n\"pelagic\",\n\"pelamyd\",\n\"pelanos\",\n\"pelean\",\n\"pelecan\",\n\"pelf\",\n\"pelican\",\n\"pelick\",\n\"pelike\",\n\"peliom\",\n\"pelioma\",\n\"pelisse\",\n\"pelite\",\n\"pelitic\",\n\"pell\",\n\"pellage\",\n\"pellar\",\n\"pellard\",\n\"pellas\",\n\"pellate\",\n\"peller\",\n\"pellet\",\n\"pellety\",\n\"pellile\",\n\"pellock\",\n\"pelmet\",\n\"pelon\",\n\"peloria\",\n\"peloric\",\n\"pelorus\",\n\"pelota\",\n\"peloton\",\n\"pelt\",\n\"pelta\",\n\"peltast\",\n\"peltate\",\n\"pelter\",\n\"pelting\",\n\"peltry\",\n\"pelu\",\n\"peludo\",\n\"pelves\",\n\"pelvic\",\n\"pelvis\",\n\"pembina\",\n\"pemican\",\n\"pen\",\n\"penal\",\n\"penally\",\n\"penalty\",\n\"penance\",\n\"penang\",\n\"penates\",\n\"penbard\",\n\"pence\",\n\"pencel\",\n\"pencil\",\n\"pend\",\n\"penda\",\n\"pendant\",\n\"pendent\",\n\"pending\",\n\"pendle\",\n\"pendom\",\n\"pendule\",\n\"penfold\",\n\"penful\",\n\"pengo\",\n\"penguin\",\n\"penhead\",\n\"penial\",\n\"penide\",\n\"penile\",\n\"penis\",\n\"penk\",\n\"penlike\",\n\"penman\",\n\"penna\",\n\"pennae\",\n\"pennage\",\n\"pennant\",\n\"pennate\",\n\"penner\",\n\"pennet\",\n\"penni\",\n\"pennia\",\n\"pennied\",\n\"pennill\",\n\"penning\",\n\"pennon\",\n\"penny\",\n\"penrack\",\n\"penship\",\n\"pensile\",\n\"pension\",\n\"pensive\",\n\"penster\",\n\"pensum\",\n\"pensy\",\n\"pent\",\n\"penta\",\n\"pentace\",\n\"pentad\",\n\"pentail\",\n\"pentane\",\n\"pentene\",\n\"pentine\",\n\"pentit\",\n\"pentite\",\n\"pentode\",\n\"pentoic\",\n\"pentol\",\n\"pentose\",\n\"pentrit\",\n\"pentyl\",\n\"pentyne\",\n\"penuchi\",\n\"penult\",\n\"penury\",\n\"peon\",\n\"peonage\",\n\"peonism\",\n\"peony\",\n\"people\",\n\"peopler\",\n\"peoplet\",\n\"peotomy\",\n\"pep\",\n\"pepful\",\n\"pepino\",\n\"peplos\",\n\"peplum\",\n\"peplus\",\n\"pepo\",\n\"pepper\",\n\"peppery\",\n\"peppily\",\n\"peppin\",\n\"peppy\",\n\"pepsin\",\n\"pepsis\",\n\"peptic\",\n\"peptide\",\n\"peptize\",\n\"peptone\",\n\"per\",\n\"peracid\",\n\"peract\",\n\"perbend\",\n\"percale\",\n\"percent\",\n\"percept\",\n\"perch\",\n\"percha\",\n\"percher\",\n\"percid\",\n\"percoct\",\n\"percoid\",\n\"percur\",\n\"percuss\",\n\"perdu\",\n\"perdure\",\n\"pereion\",\n\"pereira\",\n\"peres\",\n\"perfect\",\n\"perfidy\",\n\"perform\",\n\"perfume\",\n\"perfumy\",\n\"perfuse\",\n\"pergola\",\n\"perhaps\",\n\"peri\",\n\"periapt\",\n\"peridot\",\n\"perigee\",\n\"perigon\",\n\"peril\",\n\"perine\",\n\"period\",\n\"periost\",\n\"perique\",\n\"perish\",\n\"perit\",\n\"perite\",\n\"periwig\",\n\"perjink\",\n\"perjure\",\n\"perjury\",\n\"perk\",\n\"perkily\",\n\"perkin\",\n\"perking\",\n\"perkish\",\n\"perky\",\n\"perle\",\n\"perlid\",\n\"perlite\",\n\"perloir\",\n\"perm\",\n\"permit\",\n\"permute\",\n\"pern\",\n\"pernine\",\n\"pernor\",\n\"pernyi\",\n\"peroba\",\n\"peropod\",\n\"peropus\",\n\"peroral\",\n\"perosis\",\n\"perotic\",\n\"peroxy\",\n\"peroxyl\",\n\"perpend\",\n\"perpera\",\n\"perplex\",\n\"perrier\",\n\"perron\",\n\"perry\",\n\"persalt\",\n\"perse\",\n\"persico\",\n\"persis\",\n\"persist\",\n\"person\",\n\"persona\",\n\"pert\",\n\"pertain\",\n\"perten\",\n\"pertish\",\n\"pertly\",\n\"perturb\",\n\"pertuse\",\n\"perty\",\n\"peruke\",\n\"perula\",\n\"perule\",\n\"perusal\",\n\"peruse\",\n\"peruser\",\n\"pervade\",\n\"pervert\",\n\"pes\",\n\"pesa\",\n\"pesade\",\n\"pesage\",\n\"peseta\",\n\"peshkar\",\n\"peshwa\",\n\"peskily\",\n\"pesky\",\n\"peso\",\n\"pess\",\n\"pessary\",\n\"pest\",\n\"peste\",\n\"pester\",\n\"pestful\",\n\"pestify\",\n\"pestle\",\n\"pet\",\n\"petal\",\n\"petaled\",\n\"petalon\",\n\"petaly\",\n\"petard\",\n\"petary\",\n\"petasos\",\n\"petasus\",\n\"petcock\",\n\"pete\",\n\"peteca\",\n\"peteman\",\n\"peter\",\n\"petful\",\n\"petiole\",\n\"petit\",\n\"petite\",\n\"petitor\",\n\"petkin\",\n\"petling\",\n\"peto\",\n\"petrary\",\n\"petre\",\n\"petrean\",\n\"petrel\",\n\"petrie\",\n\"petrify\",\n\"petrol\",\n\"petrosa\",\n\"petrous\",\n\"petted\",\n\"petter\",\n\"pettily\",\n\"pettish\",\n\"pettle\",\n\"petty\",\n\"petune\",\n\"petwood\",\n\"petzite\",\n\"peuhl\",\n\"pew\",\n\"pewage\",\n\"pewdom\",\n\"pewee\",\n\"pewful\",\n\"pewing\",\n\"pewit\",\n\"pewless\",\n\"pewmate\",\n\"pewter\",\n\"pewtery\",\n\"pewy\",\n\"peyote\",\n\"peyotl\",\n\"peyton\",\n\"peytrel\",\n\"pfennig\",\n\"pfui\",\n\"pfund\",\n\"phacoid\",\n\"phaeism\",\n\"phaeton\",\n\"phage\",\n\"phalanx\",\n\"phalera\",\n\"phallic\",\n\"phallin\",\n\"phallus\",\n\"phanic\",\n\"phano\",\n\"phantom\",\n\"phare\",\n\"pharmic\",\n\"pharos\",\n\"pharynx\",\n\"phase\",\n\"phaseal\",\n\"phasemy\",\n\"phases\",\n\"phasic\",\n\"phasis\",\n\"phasm\",\n\"phasma\",\n\"phasmid\",\n\"pheal\",\n\"phellem\",\n\"phemic\",\n\"phenate\",\n\"phene\",\n\"phenene\",\n\"phenic\",\n\"phenin\",\n\"phenol\",\n\"phenyl\",\n\"pheon\",\n\"phew\",\n\"phi\",\n\"phial\",\n\"phiale\",\n\"philter\",\n\"philtra\",\n\"phit\",\n\"phiz\",\n\"phizes\",\n\"phizog\",\n\"phlegm\",\n\"phlegma\",\n\"phlegmy\",\n\"phloem\",\n\"phloxin\",\n\"pho\",\n\"phobiac\",\n\"phobic\",\n\"phobism\",\n\"phobist\",\n\"phoby\",\n\"phoca\",\n\"phocal\",\n\"phocid\",\n\"phocine\",\n\"phocoid\",\n\"phoebe\",\n\"phoenix\",\n\"phoh\",\n\"pholad\",\n\"pholcid\",\n\"pholido\",\n\"phon\",\n\"phonal\",\n\"phonate\",\n\"phone\",\n\"phoneme\",\n\"phonic\",\n\"phonics\",\n\"phonism\",\n\"phono\",\n\"phony\",\n\"phoo\",\n\"phoresy\",\n\"phoria\",\n\"phorid\",\n\"phorone\",\n\"phos\",\n\"phose\",\n\"phosis\",\n\"phospho\",\n\"phossy\",\n\"phot\",\n\"photal\",\n\"photic\",\n\"photics\",\n\"photism\",\n\"photo\",\n\"photoma\",\n\"photon\",\n\"phragma\",\n\"phrasal\",\n\"phrase\",\n\"phraser\",\n\"phrasy\",\n\"phrator\",\n\"phratry\",\n\"phrenic\",\n\"phrynid\",\n\"phrynin\",\n\"phthor\",\n\"phu\",\n\"phugoid\",\n\"phulwa\",\n\"phut\",\n\"phycite\",\n\"phyla\",\n\"phyle\",\n\"phylic\",\n\"phyllin\",\n\"phylon\",\n\"phylum\",\n\"phyma\",\n\"phymata\",\n\"physic\",\n\"physics\",\n\"phytase\",\n\"phytic\",\n\"phytin\",\n\"phytoid\",\n\"phytol\",\n\"phytoma\",\n\"phytome\",\n\"phyton\",\n\"phytyl\",\n\"pi\",\n\"pia\",\n\"piaba\",\n\"piacaba\",\n\"piacle\",\n\"piaffe\",\n\"piaffer\",\n\"pial\",\n\"pialyn\",\n\"pian\",\n\"pianic\",\n\"pianino\",\n\"pianism\",\n\"pianist\",\n\"piannet\",\n\"piano\",\n\"pianola\",\n\"piaster\",\n\"piastre\",\n\"piation\",\n\"piazine\",\n\"piazza\",\n\"pibcorn\",\n\"pibroch\",\n\"pic\",\n\"pica\",\n\"picador\",\n\"pical\",\n\"picamar\",\n\"picara\",\n\"picarel\",\n\"picaro\",\n\"picary\",\n\"piccolo\",\n\"pice\",\n\"picene\",\n\"piceous\",\n\"pichi\",\n\"picine\",\n\"pick\",\n\"pickage\",\n\"pickax\",\n\"picked\",\n\"pickee\",\n\"pickeer\",\n\"picker\",\n\"pickery\",\n\"picket\",\n\"pickle\",\n\"pickler\",\n\"pickman\",\n\"pickmaw\",\n\"pickup\",\n\"picky\",\n\"picnic\",\n\"pico\",\n\"picoid\",\n\"picot\",\n\"picotah\",\n\"picotee\",\n\"picra\",\n\"picrate\",\n\"picric\",\n\"picrite\",\n\"picrol\",\n\"picryl\",\n\"pict\",\n\"picture\",\n\"pictury\",\n\"picuda\",\n\"picudo\",\n\"picul\",\n\"piculet\",\n\"pidan\",\n\"piddle\",\n\"piddler\",\n\"piddock\",\n\"pidgin\",\n\"pie\",\n\"piebald\",\n\"piece\",\n\"piecen\",\n\"piecer\",\n\"piecing\",\n\"pied\",\n\"piedly\",\n\"pieless\",\n\"pielet\",\n\"pielum\",\n\"piemag\",\n\"pieman\",\n\"pien\",\n\"piend\",\n\"piepan\",\n\"pier\",\n\"pierage\",\n\"pierce\",\n\"pierced\",\n\"piercel\",\n\"piercer\",\n\"pierid\",\n\"pierine\",\n\"pierrot\",\n\"pieshop\",\n\"piet\",\n\"pietas\",\n\"pietic\",\n\"pietism\",\n\"pietist\",\n\"pietose\",\n\"piety\",\n\"piewife\",\n\"piewipe\",\n\"piezo\",\n\"piff\",\n\"piffle\",\n\"piffler\",\n\"pifine\",\n\"pig\",\n\"pigdan\",\n\"pigdom\",\n\"pigeon\",\n\"pigface\",\n\"pigfish\",\n\"pigfoot\",\n\"pigful\",\n\"piggery\",\n\"piggin\",\n\"pigging\",\n\"piggish\",\n\"piggle\",\n\"piggy\",\n\"pighead\",\n\"pigherd\",\n\"pightle\",\n\"pigless\",\n\"piglet\",\n\"pigling\",\n\"pigly\",\n\"pigman\",\n\"pigment\",\n\"pignon\",\n\"pignus\",\n\"pignut\",\n\"pigpen\",\n\"pigroot\",\n\"pigskin\",\n\"pigsney\",\n\"pigsty\",\n\"pigtail\",\n\"pigwash\",\n\"pigweed\",\n\"pigyard\",\n\"piitis\",\n\"pik\",\n\"pika\",\n\"pike\",\n\"piked\",\n\"pikel\",\n\"pikelet\",\n\"pikeman\",\n\"piker\",\n\"pikey\",\n\"piki\",\n\"piking\",\n\"pikle\",\n\"piky\",\n\"pilage\",\n\"pilapil\",\n\"pilar\",\n\"pilary\",\n\"pilau\",\n\"pilaued\",\n\"pilch\",\n\"pilcher\",\n\"pilcorn\",\n\"pilcrow\",\n\"pile\",\n\"pileata\",\n\"pileate\",\n\"piled\",\n\"pileous\",\n\"piler\",\n\"piles\",\n\"pileus\",\n\"pilfer\",\n\"pilger\",\n\"pilgrim\",\n\"pili\",\n\"pilifer\",\n\"piligan\",\n\"pilikai\",\n\"pilin\",\n\"piline\",\n\"piling\",\n\"pilkins\",\n\"pill\",\n\"pillage\",\n\"pillar\",\n\"pillary\",\n\"pillas\",\n\"pillbox\",\n\"pilled\",\n\"pillet\",\n\"pilleus\",\n\"pillion\",\n\"pillory\",\n\"pillow\",\n\"pillowy\",\n\"pilm\",\n\"pilmy\",\n\"pilon\",\n\"pilori\",\n\"pilose\",\n\"pilosis\",\n\"pilot\",\n\"pilotee\",\n\"pilotry\",\n\"pilous\",\n\"pilpul\",\n\"piltock\",\n\"pilula\",\n\"pilular\",\n\"pilule\",\n\"pilum\",\n\"pilus\",\n\"pily\",\n\"pimaric\",\n\"pimelic\",\n\"pimento\",\n\"pimlico\",\n\"pimola\",\n\"pimp\",\n\"pimpery\",\n\"pimping\",\n\"pimpish\",\n\"pimple\",\n\"pimpled\",\n\"pimplo\",\n\"pimploe\",\n\"pimply\",\n\"pin\",\n\"pina\",\n\"pinaces\",\n\"pinacle\",\n\"pinacol\",\n\"pinang\",\n\"pinax\",\n\"pinball\",\n\"pinbone\",\n\"pinbush\",\n\"pincase\",\n\"pincer\",\n\"pincers\",\n\"pinch\",\n\"pinche\",\n\"pinched\",\n\"pinchem\",\n\"pincher\",\n\"pind\",\n\"pinda\",\n\"pinder\",\n\"pindy\",\n\"pine\",\n\"pineal\",\n\"pined\",\n\"pinene\",\n\"piner\",\n\"pinery\",\n\"pinesap\",\n\"pinetum\",\n\"piney\",\n\"pinfall\",\n\"pinfish\",\n\"pinfold\",\n\"ping\",\n\"pingle\",\n\"pingler\",\n\"pingue\",\n\"pinguid\",\n\"pinguin\",\n\"pinhead\",\n\"pinhold\",\n\"pinhole\",\n\"pinhook\",\n\"pinic\",\n\"pining\",\n\"pinion\",\n\"pinite\",\n\"pinitol\",\n\"pinjane\",\n\"pinjra\",\n\"pink\",\n\"pinked\",\n\"pinkeen\",\n\"pinken\",\n\"pinker\",\n\"pinkeye\",\n\"pinkie\",\n\"pinkify\",\n\"pinkily\",\n\"pinking\",\n\"pinkish\",\n\"pinkly\",\n\"pinky\",\n\"pinless\",\n\"pinlock\",\n\"pinna\",\n\"pinnace\",\n\"pinnae\",\n\"pinnal\",\n\"pinnate\",\n\"pinned\",\n\"pinnel\",\n\"pinner\",\n\"pinnet\",\n\"pinning\",\n\"pinnock\",\n\"pinnula\",\n\"pinnule\",\n\"pinny\",\n\"pino\",\n\"pinole\",\n\"pinolia\",\n\"pinolin\",\n\"pinon\",\n\"pinonic\",\n\"pinrail\",\n\"pinsons\",\n\"pint\",\n\"pinta\",\n\"pintado\",\n\"pintail\",\n\"pintano\",\n\"pinte\",\n\"pintle\",\n\"pinto\",\n\"pintura\",\n\"pinulus\",\n\"pinweed\",\n\"pinwing\",\n\"pinwork\",\n\"pinworm\",\n\"piny\",\n\"pinyl\",\n\"pinyon\",\n\"pioneer\",\n\"pioted\",\n\"piotine\",\n\"piotty\",\n\"pioury\",\n\"pious\",\n\"piously\",\n\"pip\",\n\"pipa\",\n\"pipage\",\n\"pipal\",\n\"pipe\",\n\"pipeage\",\n\"piped\",\n\"pipeful\",\n\"pipeman\",\n\"piper\",\n\"piperic\",\n\"piperly\",\n\"piperno\",\n\"pipery\",\n\"pipet\",\n\"pipette\",\n\"pipi\",\n\"piping\",\n\"pipiri\",\n\"pipit\",\n\"pipkin\",\n\"pipless\",\n\"pipped\",\n\"pipper\",\n\"pippin\",\n\"pippy\",\n\"piprine\",\n\"piproid\",\n\"pipy\",\n\"piquant\",\n\"pique\",\n\"piquet\",\n\"piquia\",\n\"piqure\",\n\"pir\",\n\"piracy\",\n\"piragua\",\n\"piranha\",\n\"pirate\",\n\"piraty\",\n\"pirl\",\n\"pirn\",\n\"pirner\",\n\"pirnie\",\n\"pirny\",\n\"pirogue\",\n\"pirol\",\n\"pirr\",\n\"pirrmaw\",\n\"pisaca\",\n\"pisang\",\n\"pisay\",\n\"piscary\",\n\"piscian\",\n\"piscina\",\n\"piscine\",\n\"pisco\",\n\"pise\",\n\"pish\",\n\"pishaug\",\n\"pishu\",\n\"pisk\",\n\"pisky\",\n\"pismire\",\n\"piso\",\n\"piss\",\n\"pissant\",\n\"pist\",\n\"pistic\",\n\"pistil\",\n\"pistle\",\n\"pistol\",\n\"pistole\",\n\"piston\",\n\"pistrix\",\n\"pit\",\n\"pita\",\n\"pitanga\",\n\"pitapat\",\n\"pitarah\",\n\"pitau\",\n\"pitaya\",\n\"pitch\",\n\"pitcher\",\n\"pitchi\",\n\"pitchy\",\n\"piteous\",\n\"pitfall\",\n\"pith\",\n\"pithful\",\n\"pithily\",\n\"pithole\",\n\"pithos\",\n\"pithy\",\n\"pitier\",\n\"pitiful\",\n\"pitless\",\n\"pitlike\",\n\"pitman\",\n\"pitmark\",\n\"pitmirk\",\n\"pitpan\",\n\"pitpit\",\n\"pitside\",\n\"pitted\",\n\"pitter\",\n\"pittine\",\n\"pitting\",\n\"pittite\",\n\"pittoid\",\n\"pituite\",\n\"pituri\",\n\"pitwood\",\n\"pitwork\",\n\"pity\",\n\"pitying\",\n\"piuri\",\n\"pivalic\",\n\"pivot\",\n\"pivotal\",\n\"pivoter\",\n\"pix\",\n\"pixie\",\n\"pixy\",\n\"pize\",\n\"pizza\",\n\"pizzle\",\n\"placard\",\n\"placate\",\n\"place\",\n\"placebo\",\n\"placer\",\n\"placet\",\n\"placid\",\n\"plack\",\n\"placket\",\n\"placode\",\n\"placoid\",\n\"placula\",\n\"plaga\",\n\"plagal\",\n\"plagate\",\n\"plage\",\n\"plagium\",\n\"plagose\",\n\"plague\",\n\"plagued\",\n\"plaguer\",\n\"plaguy\",\n\"plaice\",\n\"plaid\",\n\"plaided\",\n\"plaidie\",\n\"plaidy\",\n\"plain\",\n\"plainer\",\n\"plainly\",\n\"plaint\",\n\"plait\",\n\"plaited\",\n\"plaiter\",\n\"plak\",\n\"plakat\",\n\"plan\",\n\"planaea\",\n\"planar\",\n\"planate\",\n\"planch\",\n\"plandok\",\n\"plane\",\n\"planer\",\n\"planet\",\n\"planeta\",\n\"planful\",\n\"plang\",\n\"plangor\",\n\"planish\",\n\"planity\",\n\"plank\",\n\"planker\",\n\"planky\",\n\"planner\",\n\"plant\",\n\"planta\",\n\"plantad\",\n\"plantal\",\n\"plantar\",\n\"planter\",\n\"planula\",\n\"planury\",\n\"planxty\",\n\"plap\",\n\"plaque\",\n\"plash\",\n\"plasher\",\n\"plashet\",\n\"plashy\",\n\"plasm\",\n\"plasma\",\n\"plasmic\",\n\"plasome\",\n\"plass\",\n\"plasson\",\n\"plaster\",\n\"plastic\",\n\"plastid\",\n\"plastin\",\n\"plat\",\n\"platan\",\n\"platane\",\n\"platano\",\n\"platch\",\n\"plate\",\n\"platea\",\n\"plateau\",\n\"plated\",\n\"platen\",\n\"plater\",\n\"platery\",\n\"platic\",\n\"platina\",\n\"plating\",\n\"platode\",\n\"platoid\",\n\"platoon\",\n\"platted\",\n\"platten\",\n\"platter\",\n\"platty\",\n\"platy\",\n\"plaud\",\n\"plaudit\",\n\"play\",\n\"playa\",\n\"playbox\",\n\"playboy\",\n\"playday\",\n\"player\",\n\"playful\",\n\"playlet\",\n\"playman\",\n\"playock\",\n\"playpen\",\n\"plaza\",\n\"plea\",\n\"pleach\",\n\"plead\",\n\"pleader\",\n\"please\",\n\"pleaser\",\n\"pleat\",\n\"pleater\",\n\"pleb\",\n\"plebe\",\n\"plebify\",\n\"plebs\",\n\"pleck\",\n\"plectre\",\n\"pled\",\n\"pledge\",\n\"pledgee\",\n\"pledger\",\n\"pledget\",\n\"pledgor\",\n\"pleion\",\n\"plenary\",\n\"plenipo\",\n\"plenish\",\n\"plenism\",\n\"plenist\",\n\"plenty\",\n\"plenum\",\n\"pleny\",\n\"pleon\",\n\"pleonal\",\n\"pleonic\",\n\"pleopod\",\n\"pleroma\",\n\"plerome\",\n\"plessor\",\n\"pleura\",\n\"pleural\",\n\"pleuric\",\n\"pleuron\",\n\"pleurum\",\n\"plew\",\n\"plex\",\n\"plexal\",\n\"plexor\",\n\"plexure\",\n\"plexus\",\n\"pliable\",\n\"pliably\",\n\"pliancy\",\n\"pliant\",\n\"plica\",\n\"plical\",\n\"plicate\",\n\"plied\",\n\"plier\",\n\"plies\",\n\"pliers\",\n\"plight\",\n\"plim\",\n\"plinth\",\n\"pliskie\",\n\"plisky\",\n\"ploat\",\n\"ploce\",\n\"plock\",\n\"plod\",\n\"plodder\",\n\"plodge\",\n\"plomb\",\n\"plook\",\n\"plop\",\n\"plosion\",\n\"plosive\",\n\"plot\",\n\"plote\",\n\"plotful\",\n\"plotted\",\n\"plotter\",\n\"plotty\",\n\"plough\",\n\"plouk\",\n\"plouked\",\n\"plouky\",\n\"plounce\",\n\"plout\",\n\"plouter\",\n\"plover\",\n\"plovery\",\n\"plow\",\n\"plowboy\",\n\"plower\",\n\"plowing\",\n\"plowman\",\n\"ploy\",\n\"pluck\",\n\"plucked\",\n\"plucker\",\n\"plucky\",\n\"plud\",\n\"pluff\",\n\"pluffer\",\n\"pluffy\",\n\"plug\",\n\"plugged\",\n\"plugger\",\n\"pluggy\",\n\"plugman\",\n\"plum\",\n\"pluma\",\n\"plumach\",\n\"plumade\",\n\"plumage\",\n\"plumate\",\n\"plumb\",\n\"plumber\",\n\"plumbet\",\n\"plumbic\",\n\"plumbog\",\n\"plumbum\",\n\"plumcot\",\n\"plume\",\n\"plumed\",\n\"plumer\",\n\"plumery\",\n\"plumet\",\n\"plumier\",\n\"plumify\",\n\"plumist\",\n\"plumlet\",\n\"plummer\",\n\"plummet\",\n\"plummy\",\n\"plumose\",\n\"plumous\",\n\"plump\",\n\"plumpen\",\n\"plumper\",\n\"plumply\",\n\"plumps\",\n\"plumpy\",\n\"plumula\",\n\"plumule\",\n\"plumy\",\n\"plunder\",\n\"plunge\",\n\"plunger\",\n\"plunk\",\n\"plup\",\n\"plural\",\n\"pluries\",\n\"plurify\",\n\"plus\",\n\"plush\",\n\"plushed\",\n\"plushy\",\n\"pluteal\",\n\"plutean\",\n\"pluteus\",\n\"pluvial\",\n\"pluvian\",\n\"pluvine\",\n\"ply\",\n\"plyer\",\n\"plying\",\n\"plywood\",\n\"pneuma\",\n\"po\",\n\"poach\",\n\"poacher\",\n\"poachy\",\n\"poalike\",\n\"pob\",\n\"pobby\",\n\"pobs\",\n\"pochade\",\n\"pochard\",\n\"pochay\",\n\"poche\",\n\"pock\",\n\"pocket\",\n\"pockety\",\n\"pockily\",\n\"pocky\",\n\"poco\",\n\"pocosin\",\n\"pod\",\n\"podagra\",\n\"podal\",\n\"podalic\",\n\"podatus\",\n\"podded\",\n\"podder\",\n\"poddish\",\n\"poddle\",\n\"poddy\",\n\"podeon\",\n\"podesta\",\n\"podex\",\n\"podge\",\n\"podger\",\n\"podgily\",\n\"podgy\",\n\"podial\",\n\"podical\",\n\"podices\",\n\"podite\",\n\"poditic\",\n\"poditti\",\n\"podium\",\n\"podler\",\n\"podley\",\n\"podlike\",\n\"podogyn\",\n\"podsol\",\n\"poduran\",\n\"podurid\",\n\"podware\",\n\"podzol\",\n\"poe\",\n\"poem\",\n\"poemet\",\n\"poemlet\",\n\"poesie\",\n\"poesis\",\n\"poesy\",\n\"poet\",\n\"poetdom\",\n\"poetess\",\n\"poetic\",\n\"poetics\",\n\"poetito\",\n\"poetize\",\n\"poetly\",\n\"poetry\",\n\"pogge\",\n\"poggy\",\n\"pogonip\",\n\"pogrom\",\n\"pogy\",\n\"poh\",\n\"poha\",\n\"pohna\",\n\"poi\",\n\"poietic\",\n\"poignet\",\n\"poil\",\n\"poilu\",\n\"poind\",\n\"poinder\",\n\"point\",\n\"pointed\",\n\"pointel\",\n\"pointer\",\n\"pointy\",\n\"poise\",\n\"poised\",\n\"poiser\",\n\"poison\",\n\"poitrel\",\n\"pokable\",\n\"poke\",\n\"poked\",\n\"pokeful\",\n\"pokeout\",\n\"poker\",\n\"pokey\",\n\"pokily\",\n\"poking\",\n\"pokomoo\",\n\"pokunt\",\n\"poky\",\n\"pol\",\n\"polacca\",\n\"polack\",\n\"polacre\",\n\"polar\",\n\"polaric\",\n\"polarly\",\n\"polaxis\",\n\"poldavy\",\n\"polder\",\n\"pole\",\n\"polearm\",\n\"poleax\",\n\"poleaxe\",\n\"polecat\",\n\"poleman\",\n\"polemic\",\n\"polenta\",\n\"poler\",\n\"poley\",\n\"poliad\",\n\"police\",\n\"policed\",\n\"policy\",\n\"poligar\",\n\"polio\",\n\"polis\",\n\"polish\",\n\"polite\",\n\"politic\",\n\"polity\",\n\"polk\",\n\"polka\",\n\"poll\",\n\"pollack\",\n\"polladz\",\n\"pollage\",\n\"pollam\",\n\"pollan\",\n\"pollard\",\n\"polled\",\n\"pollen\",\n\"pollent\",\n\"poller\",\n\"pollex\",\n\"polling\",\n\"pollock\",\n\"polloi\",\n\"pollute\",\n\"pollux\",\n\"polo\",\n\"poloist\",\n\"polony\",\n\"polos\",\n\"polska\",\n\"polt\",\n\"poltina\",\n\"poly\",\n\"polyact\",\n\"polyad\",\n\"polygam\",\n\"polygon\",\n\"polygyn\",\n\"polymer\",\n\"polyose\",\n\"polyp\",\n\"polyped\",\n\"polypi\",\n\"polypod\",\n\"polypus\",\n\"pom\",\n\"pomace\",\n\"pomade\",\n\"pomane\",\n\"pomate\",\n\"pomato\",\n\"pomatum\",\n\"pombe\",\n\"pombo\",\n\"pome\",\n\"pomelo\",\n\"pomey\",\n\"pomfret\",\n\"pomme\",\n\"pommee\",\n\"pommel\",\n\"pommet\",\n\"pommey\",\n\"pommy\",\n\"pomonal\",\n\"pomonic\",\n\"pomp\",\n\"pompa\",\n\"pompal\",\n\"pompano\",\n\"pompey\",\n\"pomphus\",\n\"pompier\",\n\"pompion\",\n\"pompist\",\n\"pompon\",\n\"pompous\",\n\"pomster\",\n\"pon\",\n\"ponce\",\n\"ponceau\",\n\"poncho\",\n\"pond\",\n\"pondage\",\n\"ponder\",\n\"pondful\",\n\"pondlet\",\n\"pondman\",\n\"pondok\",\n\"pondus\",\n\"pondy\",\n\"pone\",\n\"ponent\",\n\"ponerid\",\n\"poney\",\n\"pong\",\n\"ponga\",\n\"pongee\",\n\"poniard\",\n\"ponica\",\n\"ponier\",\n\"ponja\",\n\"pont\",\n\"pontage\",\n\"pontal\",\n\"pontee\",\n\"pontes\",\n\"pontic\",\n\"pontiff\",\n\"pontify\",\n\"pontil\",\n\"pontile\",\n\"pontin\",\n\"pontine\",\n\"pontist\",\n\"ponto\",\n\"ponton\",\n\"pontoon\",\n\"pony\",\n\"ponzite\",\n\"pooa\",\n\"pooch\",\n\"pooder\",\n\"poodle\",\n\"poof\",\n\"poogye\",\n\"pooh\",\n\"pook\",\n\"pooka\",\n\"pookaun\",\n\"pookoo\",\n\"pool\",\n\"pooler\",\n\"pooli\",\n\"pooly\",\n\"poon\",\n\"poonac\",\n\"poonga\",\n\"poop\",\n\"pooped\",\n\"poor\",\n\"poorish\",\n\"poorly\",\n\"poot\",\n\"pop\",\n\"popadam\",\n\"popal\",\n\"popcorn\",\n\"popdock\",\n\"pope\",\n\"popedom\",\n\"popeism\",\n\"popeler\",\n\"popely\",\n\"popery\",\n\"popess\",\n\"popeye\",\n\"popeyed\",\n\"popgun\",\n\"popify\",\n\"popinac\",\n\"popish\",\n\"popjoy\",\n\"poplar\",\n\"poplin\",\n\"popover\",\n\"poppa\",\n\"poppean\",\n\"poppel\",\n\"popper\",\n\"poppet\",\n\"poppied\",\n\"poppin\",\n\"popple\",\n\"popply\",\n\"poppy\",\n\"popshop\",\n\"popular\",\n\"populin\",\n\"popweed\",\n\"poral\",\n\"porcate\",\n\"porch\",\n\"porched\",\n\"porcine\",\n\"pore\",\n\"pored\",\n\"porer\",\n\"porge\",\n\"porger\",\n\"porgy\",\n\"poring\",\n\"porism\",\n\"porite\",\n\"pork\",\n\"porker\",\n\"porkery\",\n\"porket\",\n\"porkish\",\n\"porkman\",\n\"porkpie\",\n\"porky\",\n\"porogam\",\n\"poroma\",\n\"poros\",\n\"porose\",\n\"porosis\",\n\"porotic\",\n\"porous\",\n\"porr\",\n\"porrect\",\n\"porret\",\n\"porrigo\",\n\"porry\",\n\"port\",\n\"porta\",\n\"portage\",\n\"portail\",\n\"portal\",\n\"portass\",\n\"ported\",\n\"portend\",\n\"portent\",\n\"porter\",\n\"portia\",\n\"portico\",\n\"portify\",\n\"portio\",\n\"portion\",\n\"portlet\",\n\"portly\",\n\"portman\",\n\"porto\",\n\"portray\",\n\"portway\",\n\"porty\",\n\"porule\",\n\"porus\",\n\"pory\",\n\"posca\",\n\"pose\",\n\"poser\",\n\"poseur\",\n\"posey\",\n\"posh\",\n\"posing\",\n\"posit\",\n\"positor\",\n\"positum\",\n\"posnet\",\n\"posole\",\n\"poss\",\n\"posse\",\n\"possess\",\n\"posset\",\n\"possum\",\n\"post\",\n\"postage\",\n\"postal\",\n\"postbag\",\n\"postbox\",\n\"postboy\",\n\"posted\",\n\"posteen\",\n\"poster\",\n\"postern\",\n\"postfix\",\n\"postic\",\n\"postil\",\n\"posting\",\n\"postman\",\n\"posture\",\n\"postwar\",\n\"posy\",\n\"pot\",\n\"potable\",\n\"potamic\",\n\"potash\",\n\"potass\",\n\"potassa\",\n\"potate\",\n\"potato\",\n\"potator\",\n\"potbank\",\n\"potboil\",\n\"potboy\",\n\"potch\",\n\"potcher\",\n\"potdar\",\n\"pote\",\n\"poteen\",\n\"potence\",\n\"potency\",\n\"potent\",\n\"poter\",\n\"poteye\",\n\"potful\",\n\"potgirl\",\n\"potgun\",\n\"pothead\",\n\"potheen\",\n\"pother\",\n\"potherb\",\n\"pothery\",\n\"pothole\",\n\"pothook\",\n\"pothunt\",\n\"potifer\",\n\"potion\",\n\"potleg\",\n\"potlid\",\n\"potlike\",\n\"potluck\",\n\"potman\",\n\"potong\",\n\"potoo\",\n\"potoroo\",\n\"potpie\",\n\"potrack\",\n\"pott\",\n\"pottage\",\n\"pottagy\",\n\"pottah\",\n\"potted\",\n\"potter\",\n\"pottery\",\n\"potting\",\n\"pottle\",\n\"pottled\",\n\"potto\",\n\"potty\",\n\"potware\",\n\"potwork\",\n\"potwort\",\n\"pouce\",\n\"poucer\",\n\"poucey\",\n\"pouch\",\n\"pouched\",\n\"pouchy\",\n\"pouf\",\n\"poulard\",\n\"poulp\",\n\"poulpe\",\n\"poult\",\n\"poulter\",\n\"poultry\",\n\"pounamu\",\n\"pounce\",\n\"pounced\",\n\"pouncer\",\n\"pouncet\",\n\"pound\",\n\"poundal\",\n\"pounder\",\n\"pour\",\n\"pourer\",\n\"pourie\",\n\"pouring\",\n\"pouser\",\n\"pout\",\n\"pouter\",\n\"poutful\",\n\"pouting\",\n\"pouty\",\n\"poverty\",\n\"pow\",\n\"powder\",\n\"powdery\",\n\"powdike\",\n\"powdry\",\n\"power\",\n\"powered\",\n\"powitch\",\n\"pownie\",\n\"powwow\",\n\"pox\",\n\"poxy\",\n\"poy\",\n\"poyou\",\n\"praam\",\n\"prabble\",\n\"prabhu\",\n\"practic\",\n\"prad\",\n\"praecox\",\n\"praetor\",\n\"prairie\",\n\"praise\",\n\"praiser\",\n\"prajna\",\n\"praline\",\n\"pram\",\n\"prana\",\n\"prance\",\n\"prancer\",\n\"prancy\",\n\"prank\",\n\"pranked\",\n\"pranker\",\n\"prankle\",\n\"pranky\",\n\"prase\",\n\"prasine\",\n\"prasoid\",\n\"prastha\",\n\"prat\",\n\"pratal\",\n\"prate\",\n\"prater\",\n\"pratey\",\n\"prating\",\n\"prattle\",\n\"prattly\",\n\"prau\",\n\"pravity\",\n\"prawn\",\n\"prawner\",\n\"prawny\",\n\"praxis\",\n\"pray\",\n\"praya\",\n\"prayer\",\n\"prayful\",\n\"praying\",\n\"preach\",\n\"preachy\",\n\"preacid\",\n\"preact\",\n\"preaged\",\n\"preally\",\n\"preanal\",\n\"prearm\",\n\"preaver\",\n\"prebake\",\n\"prebend\",\n\"prebid\",\n\"prebill\",\n\"preboil\",\n\"preborn\",\n\"preburn\",\n\"precant\",\n\"precary\",\n\"precast\",\n\"precava\",\n\"precede\",\n\"precent\",\n\"precept\",\n\"preces\",\n\"precess\",\n\"precipe\",\n\"precis\",\n\"precise\",\n\"precite\",\n\"precoil\",\n\"precook\",\n\"precool\",\n\"precopy\",\n\"precox\",\n\"precure\",\n\"precut\",\n\"precyst\",\n\"predamn\",\n\"predark\",\n\"predata\",\n\"predate\",\n\"predawn\",\n\"preday\",\n\"predefy\",\n\"predeny\",\n\"predial\",\n\"predict\",\n\"prediet\",\n\"predine\",\n\"predoom\",\n\"predraw\",\n\"predry\",\n\"predusk\",\n\"preen\",\n\"preener\",\n\"preeze\",\n\"prefab\",\n\"preface\",\n\"prefect\",\n\"prefer\",\n\"prefine\",\n\"prefix\",\n\"prefool\",\n\"preform\",\n\"pregain\",\n\"pregust\",\n\"prehaps\",\n\"preheal\",\n\"preheat\",\n\"prehend\",\n\"preidea\",\n\"preknit\",\n\"preknow\",\n\"prelacy\",\n\"prelate\",\n\"prelect\",\n\"prelim\",\n\"preloan\",\n\"preloss\",\n\"prelude\",\n\"premake\",\n\"premate\",\n\"premial\",\n\"premier\",\n\"premise\",\n\"premiss\",\n\"premium\",\n\"premix\",\n\"premold\",\n\"premove\",\n\"prename\",\n\"prender\",\n\"prendre\",\n\"preomit\",\n\"preopen\",\n\"preoral\",\n\"prep\",\n\"prepare\",\n\"prepave\",\n\"prepay\",\n\"prepink\",\n\"preplan\",\n\"preplot\",\n\"prepose\",\n\"prepuce\",\n\"prepupa\",\n\"prerent\",\n\"prerich\",\n\"prerupt\",\n\"presage\",\n\"presay\",\n\"preseal\",\n\"presee\",\n\"presell\",\n\"present\",\n\"preses\",\n\"preset\",\n\"preship\",\n\"preshow\",\n\"preside\",\n\"presift\",\n\"presign\",\n\"prespur\",\n\"press\",\n\"pressel\",\n\"presser\",\n\"pressor\",\n\"prest\",\n\"prester\",\n\"presto\",\n\"presume\",\n\"pretan\",\n\"pretell\",\n\"pretend\",\n\"pretest\",\n\"pretext\",\n\"pretire\",\n\"pretone\",\n\"pretry\",\n\"pretty\",\n\"pretzel\",\n\"prevail\",\n\"prevene\",\n\"prevent\",\n\"preverb\",\n\"preveto\",\n\"previde\",\n\"preview\",\n\"previse\",\n\"prevoid\",\n\"prevote\",\n\"prevue\",\n\"prewar\",\n\"prewarn\",\n\"prewash\",\n\"prewhip\",\n\"prewire\",\n\"prewrap\",\n\"prexy\",\n\"prey\",\n\"preyer\",\n\"preyful\",\n\"prezone\",\n\"price\",\n\"priced\",\n\"pricer\",\n\"prich\",\n\"prick\",\n\"pricked\",\n\"pricker\",\n\"pricket\",\n\"prickle\",\n\"prickly\",\n\"pricks\",\n\"pricky\",\n\"pride\",\n\"pridian\",\n\"priding\",\n\"pridy\",\n\"pried\",\n\"prier\",\n\"priest\",\n\"prig\",\n\"prigdom\",\n\"prigger\",\n\"prigman\",\n\"prill\",\n\"prim\",\n\"prima\",\n\"primacy\",\n\"primage\",\n\"primal\",\n\"primar\",\n\"primary\",\n\"primate\",\n\"prime\",\n\"primely\",\n\"primer\",\n\"primero\",\n\"primine\",\n\"priming\",\n\"primly\",\n\"primost\",\n\"primp\",\n\"primsie\",\n\"primula\",\n\"primus\",\n\"primy\",\n\"prince\",\n\"princox\",\n\"prine\",\n\"pringle\",\n\"prink\",\n\"prinker\",\n\"prinkle\",\n\"prinky\",\n\"print\",\n\"printed\",\n\"printer\",\n\"prion\",\n\"prionid\",\n\"prior\",\n\"prioral\",\n\"priorly\",\n\"priory\",\n\"prisage\",\n\"prisal\",\n\"priscan\",\n\"prism\",\n\"prismal\",\n\"prismed\",\n\"prismy\",\n\"prison\",\n\"priss\",\n\"prissy\",\n\"pritch\",\n\"prithee\",\n\"prius\",\n\"privacy\",\n\"privant\",\n\"private\",\n\"privet\",\n\"privily\",\n\"privity\",\n\"privy\",\n\"prize\",\n\"prizer\",\n\"prizery\",\n\"pro\",\n\"proa\",\n\"proal\",\n\"proarmy\",\n\"prob\",\n\"probabl\",\n\"probal\",\n\"probang\",\n\"probant\",\n\"probate\",\n\"probe\",\n\"probeer\",\n\"prober\",\n\"probity\",\n\"problem\",\n\"procarp\",\n\"proceed\",\n\"process\",\n\"proctal\",\n\"proctor\",\n\"procure\",\n\"prod\",\n\"prodder\",\n\"proddle\",\n\"prodigy\",\n\"produce\",\n\"product\",\n\"proem\",\n\"proetid\",\n\"prof\",\n\"profane\",\n\"profert\",\n\"profess\",\n\"proffer\",\n\"profile\",\n\"profit\",\n\"profuse\",\n\"prog\",\n\"progeny\",\n\"progger\",\n\"progne\",\n\"program\",\n\"project\",\n\"proke\",\n\"proker\",\n\"prolan\",\n\"prolate\",\n\"proleg\",\n\"prolify\",\n\"proline\",\n\"prolix\",\n\"prolong\",\n\"prolyl\",\n\"promic\",\n\"promise\",\n\"promote\",\n\"prompt\",\n\"pronaos\",\n\"pronate\",\n\"pronavy\",\n\"prone\",\n\"pronely\",\n\"proneur\",\n\"prong\",\n\"pronged\",\n\"pronger\",\n\"pronic\",\n\"pronoun\",\n\"pronpl\",\n\"pronto\",\n\"pronuba\",\n\"proo\",\n\"proof\",\n\"proofer\",\n\"proofy\",\n\"prop\",\n\"propago\",\n\"propale\",\n\"propane\",\n\"propend\",\n\"propene\",\n\"proper\",\n\"prophet\",\n\"propine\",\n\"proplex\",\n\"propone\",\n\"propons\",\n\"propose\",\n\"propoxy\",\n\"propper\",\n\"props\",\n\"propupa\",\n\"propyl\",\n\"propyne\",\n\"prorata\",\n\"prorate\",\n\"prore\",\n\"prorean\",\n\"prorsad\",\n\"prorsal\",\n\"prosaic\",\n\"prosar\",\n\"prose\",\n\"prosect\",\n\"proser\",\n\"prosify\",\n\"prosily\",\n\"prosing\",\n\"prosish\",\n\"prosist\",\n\"proso\",\n\"prosode\",\n\"prosody\",\n\"prosoma\",\n\"prosper\",\n\"pross\",\n\"prossy\",\n\"prosy\",\n\"protax\",\n\"prote\",\n\"protea\",\n\"protead\",\n\"protean\",\n\"protect\",\n\"protege\",\n\"proteic\",\n\"protein\",\n\"protend\",\n\"protest\",\n\"protext\",\n\"prothyl\",\n\"protide\",\n\"protist\",\n\"protium\",\n\"proto\",\n\"protoma\",\n\"protome\",\n\"proton\",\n\"protone\",\n\"protore\",\n\"protyl\",\n\"protyle\",\n\"protype\",\n\"proudly\",\n\"provand\",\n\"provant\",\n\"prove\",\n\"provect\",\n\"proved\",\n\"proven\",\n\"prover\",\n\"proverb\",\n\"provide\",\n\"provine\",\n\"proving\",\n\"proviso\",\n\"provoke\",\n\"provost\",\n\"prow\",\n\"prowar\",\n\"prowed\",\n\"prowess\",\n\"prowl\",\n\"prowler\",\n\"proxeny\",\n\"proximo\",\n\"proxy\",\n\"proxysm\",\n\"prozone\",\n\"prude\",\n\"prudely\",\n\"prudent\",\n\"prudery\",\n\"prudish\",\n\"prudist\",\n\"prudity\",\n\"pruh\",\n\"prunase\",\n\"prune\",\n\"prunell\",\n\"pruner\",\n\"pruning\",\n\"prunt\",\n\"prunted\",\n\"prurigo\",\n\"prussic\",\n\"prut\",\n\"prutah\",\n\"pry\",\n\"pryer\",\n\"prying\",\n\"pryler\",\n\"pryse\",\n\"prytany\",\n\"psalis\",\n\"psalm\",\n\"psalmic\",\n\"psalmy\",\n\"psaloid\",\n\"psalter\",\n\"psaltes\",\n\"pschent\",\n\"pseudo\",\n\"psha\",\n\"pshaw\",\n\"psi\",\n\"psiloi\",\n\"psoadic\",\n\"psoas\",\n\"psoatic\",\n\"psocid\",\n\"psocine\",\n\"psoitis\",\n\"psora\",\n\"psoric\",\n\"psoroid\",\n\"psorous\",\n\"pst\",\n\"psych\",\n\"psychal\",\n\"psyche\",\n\"psychic\",\n\"psychid\",\n\"psychon\",\n\"psykter\",\n\"psylla\",\n\"psyllid\",\n\"ptarmic\",\n\"ptereal\",\n\"pteric\",\n\"pterion\",\n\"pteroid\",\n\"pteroma\",\n\"pteryla\",\n\"ptinid\",\n\"ptinoid\",\n\"ptisan\",\n\"ptomain\",\n\"ptosis\",\n\"ptotic\",\n\"ptyalin\",\n\"ptyxis\",\n\"pu\",\n\"pua\",\n\"puan\",\n\"pub\",\n\"pubal\",\n\"pubble\",\n\"puberal\",\n\"puberty\",\n\"pubes\",\n\"pubian\",\n\"pubic\",\n\"pubis\",\n\"public\",\n\"publish\",\n\"puccoon\",\n\"puce\",\n\"pucelle\",\n\"puchero\",\n\"puck\",\n\"pucka\",\n\"pucker\",\n\"puckery\",\n\"puckish\",\n\"puckle\",\n\"puckrel\",\n\"pud\",\n\"puddee\",\n\"pudder\",\n\"pudding\",\n\"puddle\",\n\"puddled\",\n\"puddler\",\n\"puddly\",\n\"puddock\",\n\"puddy\",\n\"pudency\",\n\"pudenda\",\n\"pudent\",\n\"pudge\",\n\"pudgily\",\n\"pudgy\",\n\"pudiano\",\n\"pudic\",\n\"pudical\",\n\"pudsey\",\n\"pudsy\",\n\"pudu\",\n\"pueblo\",\n\"puerer\",\n\"puerile\",\n\"puerman\",\n\"puff\",\n\"puffed\",\n\"puffer\",\n\"puffery\",\n\"puffily\",\n\"puffin\",\n\"puffing\",\n\"pufflet\",\n\"puffwig\",\n\"puffy\",\n\"pug\",\n\"pugged\",\n\"pugger\",\n\"puggi\",\n\"pugging\",\n\"puggish\",\n\"puggle\",\n\"puggree\",\n\"puggy\",\n\"pugh\",\n\"pugil\",\n\"pugman\",\n\"pugmill\",\n\"puisne\",\n\"puist\",\n\"puistie\",\n\"puja\",\n\"puka\",\n\"pukatea\",\n\"puke\",\n\"pukeko\",\n\"puker\",\n\"pukish\",\n\"pukras\",\n\"puku\",\n\"puky\",\n\"pul\",\n\"pulahan\",\n\"pulasan\",\n\"pule\",\n\"pulegol\",\n\"puler\",\n\"puli\",\n\"pulicat\",\n\"pulicid\",\n\"puling\",\n\"pulish\",\n\"pulk\",\n\"pulka\",\n\"pull\",\n\"pulldoo\",\n\"pullen\",\n\"puller\",\n\"pullery\",\n\"pullet\",\n\"pulley\",\n\"pulli\",\n\"pullus\",\n\"pulp\",\n\"pulpal\",\n\"pulper\",\n\"pulpify\",\n\"pulpily\",\n\"pulpit\",\n\"pulpous\",\n\"pulpy\",\n\"pulque\",\n\"pulsant\",\n\"pulsate\",\n\"pulse\",\n\"pulsion\",\n\"pulsive\",\n\"pulton\",\n\"pulu\",\n\"pulvic\",\n\"pulvil\",\n\"pulvino\",\n\"pulwar\",\n\"puly\",\n\"puma\",\n\"pumice\",\n\"pumiced\",\n\"pumicer\",\n\"pummel\",\n\"pummice\",\n\"pump\",\n\"pumpage\",\n\"pumper\",\n\"pumpkin\",\n\"pumple\",\n\"pumpman\",\n\"pun\",\n\"puna\",\n\"punaise\",\n\"punalua\",\n\"punatoo\",\n\"punch\",\n\"puncher\",\n\"punchy\",\n\"punct\",\n\"punctal\",\n\"punctum\",\n\"pundit\",\n\"pundita\",\n\"pundum\",\n\"puneca\",\n\"pung\",\n\"punga\",\n\"pungar\",\n\"pungent\",\n\"punger\",\n\"pungey\",\n\"pungi\",\n\"pungle\",\n\"pungled\",\n\"punicin\",\n\"punily\",\n\"punish\",\n\"punjum\",\n\"punk\",\n\"punkah\",\n\"punkie\",\n\"punky\",\n\"punless\",\n\"punlet\",\n\"punnage\",\n\"punner\",\n\"punnet\",\n\"punnic\",\n\"punster\",\n\"punt\",\n\"punta\",\n\"puntal\",\n\"puntel\",\n\"punter\",\n\"punti\",\n\"puntil\",\n\"puntist\",\n\"punto\",\n\"puntout\",\n\"punty\",\n\"puny\",\n\"punyish\",\n\"punyism\",\n\"pup\",\n\"pupa\",\n\"pupal\",\n\"pupate\",\n\"pupelo\",\n\"pupil\",\n\"pupilar\",\n\"pupiled\",\n\"pupoid\",\n\"puppet\",\n\"puppify\",\n\"puppily\",\n\"puppy\",\n\"pupulo\",\n\"pupunha\",\n\"pur\",\n\"purana\",\n\"puranic\",\n\"puraque\",\n\"purdah\",\n\"purdy\",\n\"pure\",\n\"pured\",\n\"puree\",\n\"purely\",\n\"purer\",\n\"purfle\",\n\"purfled\",\n\"purfler\",\n\"purfly\",\n\"purga\",\n\"purge\",\n\"purger\",\n\"purgery\",\n\"purging\",\n\"purify\",\n\"purine\",\n\"puriri\",\n\"purism\",\n\"purist\",\n\"purity\",\n\"purl\",\n\"purler\",\n\"purlieu\",\n\"purlin\",\n\"purlman\",\n\"purloin\",\n\"purpart\",\n\"purple\",\n\"purply\",\n\"purport\",\n\"purpose\",\n\"purpura\",\n\"purpure\",\n\"purr\",\n\"purre\",\n\"purree\",\n\"purreic\",\n\"purrel\",\n\"purrer\",\n\"purring\",\n\"purrone\",\n\"purry\",\n\"purse\",\n\"pursed\",\n\"purser\",\n\"pursily\",\n\"purslet\",\n\"pursley\",\n\"pursual\",\n\"pursue\",\n\"pursuer\",\n\"pursuit\",\n\"pursy\",\n\"purusha\",\n\"purvey\",\n\"purview\",\n\"purvoe\",\n\"pus\",\n\"push\",\n\"pusher\",\n\"pushful\",\n\"pushing\",\n\"pushpin\",\n\"puss\",\n\"pusscat\",\n\"pussley\",\n\"pussy\",\n\"pustule\",\n\"put\",\n\"putage\",\n\"putamen\",\n\"putback\",\n\"putchen\",\n\"putcher\",\n\"puteal\",\n\"putelee\",\n\"puther\",\n\"puthery\",\n\"putid\",\n\"putidly\",\n\"putlog\",\n\"putois\",\n\"putrefy\",\n\"putrid\",\n\"putt\",\n\"puttee\",\n\"putter\",\n\"puttier\",\n\"puttock\",\n\"putty\",\n\"puture\",\n\"puxy\",\n\"puzzle\",\n\"puzzled\",\n\"puzzler\",\n\"pya\",\n\"pyal\",\n\"pyche\",\n\"pycnia\",\n\"pycnial\",\n\"pycnid\",\n\"pycnite\",\n\"pycnium\",\n\"pyelic\",\n\"pyemia\",\n\"pyemic\",\n\"pygal\",\n\"pygarg\",\n\"pygidid\",\n\"pygmoid\",\n\"pygmy\",\n\"pygofer\",\n\"pygopod\",\n\"pyic\",\n\"pyin\",\n\"pyjama\",\n\"pyke\",\n\"pyknic\",\n\"pyla\",\n\"pylar\",\n\"pylic\",\n\"pylon\",\n\"pyloric\",\n\"pylorus\",\n\"pyocele\",\n\"pyocyst\",\n\"pyocyte\",\n\"pyoid\",\n\"pyosis\",\n\"pyr\",\n\"pyral\",\n\"pyralid\",\n\"pyralis\",\n\"pyramid\",\n\"pyran\",\n\"pyranyl\",\n\"pyre\",\n\"pyrena\",\n\"pyrene\",\n\"pyrenic\",\n\"pyrenin\",\n\"pyretic\",\n\"pyrex\",\n\"pyrexia\",\n\"pyrexic\",\n\"pyrgom\",\n\"pyridic\",\n\"pyridyl\",\n\"pyrite\",\n\"pyrites\",\n\"pyritic\",\n\"pyro\",\n\"pyrogen\",\n\"pyroid\",\n\"pyrone\",\n\"pyrope\",\n\"pyropen\",\n\"pyropus\",\n\"pyrosis\",\n\"pyrotic\",\n\"pyrrhic\",\n\"pyrrol\",\n\"pyrrole\",\n\"pyrroyl\",\n\"pyrryl\",\n\"pyruvic\",\n\"pyruvil\",\n\"pyruvyl\",\n\"python\",\n\"pyuria\",\n\"pyvuril\",\n\"pyx\",\n\"pyxides\",\n\"pyxie\",\n\"pyxis\",\n\"q\",\n\"qasida\",\n\"qere\",\n\"qeri\",\n\"qintar\",\n\"qoph\",\n\"qua\",\n\"quab\",\n\"quabird\",\n\"quachil\",\n\"quack\",\n\"quackle\",\n\"quacky\",\n\"quad\",\n\"quadded\",\n\"quaddle\",\n\"quadra\",\n\"quadral\",\n\"quadrat\",\n\"quadric\",\n\"quadrum\",\n\"quaedam\",\n\"quaff\",\n\"quaffer\",\n\"quag\",\n\"quagga\",\n\"quaggle\",\n\"quaggy\",\n\"quahog\",\n\"quail\",\n\"quaily\",\n\"quaint\",\n\"quake\",\n\"quaker\",\n\"quaking\",\n\"quaky\",\n\"quale\",\n\"qualify\",\n\"quality\",\n\"qualm\",\n\"qualmy\",\n\"quan\",\n\"quandy\",\n\"quannet\",\n\"quant\",\n\"quanta\",\n\"quantic\",\n\"quantum\",\n\"quar\",\n\"quare\",\n\"quark\",\n\"quarl\",\n\"quarle\",\n\"quarred\",\n\"quarrel\",\n\"quarry\",\n\"quart\",\n\"quartan\",\n\"quarter\",\n\"quartet\",\n\"quartic\",\n\"quarto\",\n\"quartz\",\n\"quartzy\",\n\"quash\",\n\"quashey\",\n\"quashy\",\n\"quasi\",\n\"quasky\",\n\"quassin\",\n\"quat\",\n\"quata\",\n\"quatch\",\n\"quatern\",\n\"quaters\",\n\"quatral\",\n\"quatre\",\n\"quatrin\",\n\"quattie\",\n\"quatuor\",\n\"quauk\",\n\"quave\",\n\"quaver\",\n\"quavery\",\n\"quaw\",\n\"quawk\",\n\"quay\",\n\"quayage\",\n\"quayful\",\n\"quayman\",\n\"qubba\",\n\"queach\",\n\"queachy\",\n\"queak\",\n\"queal\",\n\"quean\",\n\"queasom\",\n\"queasy\",\n\"quedful\",\n\"queechy\",\n\"queen\",\n\"queenly\",\n\"queer\",\n\"queerer\",\n\"queerly\",\n\"queery\",\n\"queest\",\n\"queet\",\n\"queeve\",\n\"quegh\",\n\"quei\",\n\"quelch\",\n\"quell\",\n\"queller\",\n\"quemado\",\n\"queme\",\n\"quemely\",\n\"quench\",\n\"quercic\",\n\"quercin\",\n\"querent\",\n\"querier\",\n\"querist\",\n\"querken\",\n\"querl\",\n\"quern\",\n\"quernal\",\n\"query\",\n\"quest\",\n\"quester\",\n\"questor\",\n\"quet\",\n\"quetch\",\n\"quetzal\",\n\"queue\",\n\"quey\",\n\"quiapo\",\n\"quib\",\n\"quibble\",\n\"quiblet\",\n\"quica\",\n\"quick\",\n\"quicken\",\n\"quickie\",\n\"quickly\",\n\"quid\",\n\"quidder\",\n\"quiddit\",\n\"quiddle\",\n\"quiesce\",\n\"quiet\",\n\"quieten\",\n\"quieter\",\n\"quietly\",\n\"quietus\",\n\"quiff\",\n\"quila\",\n\"quiles\",\n\"quilkin\",\n\"quill\",\n\"quillai\",\n\"quilled\",\n\"quiller\",\n\"quillet\",\n\"quilly\",\n\"quilt\",\n\"quilted\",\n\"quilter\",\n\"quin\",\n\"quina\",\n\"quinary\",\n\"quinate\",\n\"quince\",\n\"quinch\",\n\"quinia\",\n\"quinic\",\n\"quinin\",\n\"quinina\",\n\"quinine\",\n\"quinism\",\n\"quinite\",\n\"quinize\",\n\"quink\",\n\"quinnat\",\n\"quinnet\",\n\"quinoa\",\n\"quinoid\",\n\"quinol\",\n\"quinone\",\n\"quinova\",\n\"quinoyl\",\n\"quinse\",\n\"quinsy\",\n\"quint\",\n\"quintad\",\n\"quintal\",\n\"quintan\",\n\"quinte\",\n\"quintet\",\n\"quintic\",\n\"quintin\",\n\"quinto\",\n\"quinton\",\n\"quintus\",\n\"quinyl\",\n\"quinze\",\n\"quip\",\n\"quipful\",\n\"quipo\",\n\"quipper\",\n\"quippy\",\n\"quipu\",\n\"quira\",\n\"quire\",\n\"quirk\",\n\"quirky\",\n\"quirl\",\n\"quirt\",\n\"quis\",\n\"quisby\",\n\"quiscos\",\n\"quisle\",\n\"quit\",\n\"quitch\",\n\"quite\",\n\"quits\",\n\"quitted\",\n\"quitter\",\n\"quittor\",\n\"quiver\",\n\"quivery\",\n\"quiz\",\n\"quizzee\",\n\"quizzer\",\n\"quizzy\",\n\"quo\",\n\"quod\",\n\"quoin\",\n\"quoined\",\n\"quoit\",\n\"quoiter\",\n\"quoits\",\n\"quondam\",\n\"quoniam\",\n\"quop\",\n\"quorum\",\n\"quot\",\n\"quota\",\n\"quote\",\n\"quotee\",\n\"quoter\",\n\"quoth\",\n\"quotha\",\n\"quotity\",\n\"quotum\",\n\"r\",\n\"ra\",\n\"raad\",\n\"raash\",\n\"rab\",\n\"raband\",\n\"rabanna\",\n\"rabat\",\n\"rabatte\",\n\"rabbet\",\n\"rabbi\",\n\"rabbin\",\n\"rabbit\",\n\"rabbity\",\n\"rabble\",\n\"rabbler\",\n\"rabboni\",\n\"rabic\",\n\"rabid\",\n\"rabidly\",\n\"rabies\",\n\"rabific\",\n\"rabinet\",\n\"rabitic\",\n\"raccoon\",\n\"raccroc\",\n\"race\",\n\"raceme\",\n\"racemed\",\n\"racemic\",\n\"racer\",\n\"raceway\",\n\"rach\",\n\"rache\",\n\"rachial\",\n\"rachis\",\n\"racial\",\n\"racily\",\n\"racing\",\n\"racism\",\n\"racist\",\n\"rack\",\n\"rackan\",\n\"racker\",\n\"racket\",\n\"rackett\",\n\"rackety\",\n\"rackful\",\n\"racking\",\n\"rackle\",\n\"rackway\",\n\"racloir\",\n\"racon\",\n\"racoon\",\n\"racy\",\n\"rad\",\n\"rada\",\n\"radar\",\n\"raddle\",\n\"radial\",\n\"radiale\",\n\"radian\",\n\"radiant\",\n\"radiate\",\n\"radical\",\n\"radicel\",\n\"radices\",\n\"radicle\",\n\"radii\",\n\"radio\",\n\"radiode\",\n\"radish\",\n\"radium\",\n\"radius\",\n\"radix\",\n\"radman\",\n\"radome\",\n\"radon\",\n\"radula\",\n\"raff\",\n\"raffe\",\n\"raffee\",\n\"raffery\",\n\"raffia\",\n\"raffing\",\n\"raffish\",\n\"raffle\",\n\"raffler\",\n\"raft\",\n\"raftage\",\n\"rafter\",\n\"raftman\",\n\"rafty\",\n\"rag\",\n\"raga\",\n\"rage\",\n\"rageful\",\n\"rageous\",\n\"rager\",\n\"ragfish\",\n\"ragged\",\n\"raggedy\",\n\"raggee\",\n\"ragger\",\n\"raggery\",\n\"raggety\",\n\"raggil\",\n\"raggily\",\n\"ragging\",\n\"raggle\",\n\"raggled\",\n\"raggy\",\n\"raging\",\n\"raglan\",\n\"raglet\",\n\"raglin\",\n\"ragman\",\n\"ragout\",\n\"ragshag\",\n\"ragtag\",\n\"ragtime\",\n\"ragule\",\n\"raguly\",\n\"ragweed\",\n\"ragwort\",\n\"rah\",\n\"rahdar\",\n\"raia\",\n\"raid\",\n\"raider\",\n\"rail\",\n\"railage\",\n\"railer\",\n\"railing\",\n\"railly\",\n\"railman\",\n\"railway\",\n\"raiment\",\n\"rain\",\n\"rainbow\",\n\"rainer\",\n\"rainful\",\n\"rainily\",\n\"rainy\",\n\"raioid\",\n\"rais\",\n\"raise\",\n\"raised\",\n\"raiser\",\n\"raisin\",\n\"raising\",\n\"raisiny\",\n\"raj\",\n\"raja\",\n\"rajah\",\n\"rakan\",\n\"rake\",\n\"rakeage\",\n\"rakeful\",\n\"raker\",\n\"rakery\",\n\"rakh\",\n\"raki\",\n\"rakily\",\n\"raking\",\n\"rakish\",\n\"rakit\",\n\"raku\",\n\"rallier\",\n\"ralline\",\n\"rally\",\n\"ralph\",\n\"ram\",\n\"ramada\",\n\"ramage\",\n\"ramal\",\n\"ramanas\",\n\"ramass\",\n\"ramate\",\n\"rambeh\",\n\"ramble\",\n\"rambler\",\n\"rambong\",\n\"rame\",\n\"rameal\",\n\"ramed\",\n\"ramekin\",\n\"rament\",\n\"rameous\",\n\"ramet\",\n\"ramex\",\n\"ramhead\",\n\"ramhood\",\n\"rami\",\n\"ramie\",\n\"ramify\",\n\"ramlike\",\n\"ramline\",\n\"rammack\",\n\"rammel\",\n\"rammer\",\n\"rammish\",\n\"rammy\",\n\"ramose\",\n\"ramous\",\n\"ramp\",\n\"rampage\",\n\"rampant\",\n\"rampart\",\n\"ramped\",\n\"ramper\",\n\"rampick\",\n\"rampike\",\n\"ramping\",\n\"rampion\",\n\"rampire\",\n\"rampler\",\n\"ramplor\",\n\"ramrace\",\n\"ramrod\",\n\"ramsch\",\n\"ramson\",\n\"ramstam\",\n\"ramtil\",\n\"ramular\",\n\"ramule\",\n\"ramulus\",\n\"ramus\",\n\"ran\",\n\"rana\",\n\"ranal\",\n\"rance\",\n\"rancel\",\n\"rancer\",\n\"ranch\",\n\"ranche\",\n\"rancher\",\n\"rancho\",\n\"rancid\",\n\"rancor\",\n\"rand\",\n\"randan\",\n\"randem\",\n\"rander\",\n\"randing\",\n\"randir\",\n\"randle\",\n\"random\",\n\"randy\",\n\"rane\",\n\"rang\",\n\"range\",\n\"ranged\",\n\"ranger\",\n\"rangey\",\n\"ranging\",\n\"rangle\",\n\"rangler\",\n\"rangy\",\n\"rani\",\n\"ranid\",\n\"ranine\",\n\"rank\",\n\"ranked\",\n\"ranker\",\n\"rankish\",\n\"rankle\",\n\"rankly\",\n\"rann\",\n\"rannel\",\n\"ranny\",\n\"ransack\",\n\"ransel\",\n\"ransom\",\n\"rant\",\n\"rantan\",\n\"ranter\",\n\"ranting\",\n\"rantock\",\n\"ranty\",\n\"ranula\",\n\"ranular\",\n\"rap\",\n\"rape\",\n\"rapeful\",\n\"raper\",\n\"raphany\",\n\"raphe\",\n\"raphide\",\n\"raphis\",\n\"rapic\",\n\"rapid\",\n\"rapidly\",\n\"rapier\",\n\"rapillo\",\n\"rapine\",\n\"rapiner\",\n\"raping\",\n\"rapinic\",\n\"rapist\",\n\"raploch\",\n\"rappage\",\n\"rappe\",\n\"rappel\",\n\"rapper\",\n\"rapping\",\n\"rappist\",\n\"rapport\",\n\"rapt\",\n\"raptly\",\n\"raptor\",\n\"raptril\",\n\"rapture\",\n\"raptury\",\n\"raptus\",\n\"rare\",\n\"rarebit\",\n\"rarefy\",\n\"rarely\",\n\"rarish\",\n\"rarity\",\n\"ras\",\n\"rasa\",\n\"rasant\",\n\"rascal\",\n\"rasceta\",\n\"rase\",\n\"rasen\",\n\"raser\",\n\"rasgado\",\n\"rash\",\n\"rasher\",\n\"rashful\",\n\"rashing\",\n\"rashly\",\n\"rasion\",\n\"rasp\",\n\"rasped\",\n\"rasper\",\n\"rasping\",\n\"raspish\",\n\"raspite\",\n\"raspy\",\n\"rasse\",\n\"rassle\",\n\"raster\",\n\"rastik\",\n\"rastle\",\n\"rasure\",\n\"rat\",\n\"rata\",\n\"ratable\",\n\"ratably\",\n\"ratafee\",\n\"ratafia\",\n\"ratal\",\n\"ratbite\",\n\"ratch\",\n\"ratchel\",\n\"ratcher\",\n\"ratchet\",\n\"rate\",\n\"rated\",\n\"ratel\",\n\"rater\",\n\"ratfish\",\n\"rath\",\n\"rathe\",\n\"rathed\",\n\"rathely\",\n\"rather\",\n\"rathest\",\n\"rathite\",\n\"rathole\",\n\"ratify\",\n\"ratine\",\n\"rating\",\n\"ratio\",\n\"ration\",\n\"ratite\",\n\"ratlike\",\n\"ratline\",\n\"ratoon\",\n\"rattage\",\n\"rattail\",\n\"rattan\",\n\"ratteen\",\n\"ratten\",\n\"ratter\",\n\"rattery\",\n\"ratti\",\n\"rattish\",\n\"rattle\",\n\"rattled\",\n\"rattler\",\n\"rattles\",\n\"rattly\",\n\"ratton\",\n\"rattrap\",\n\"ratty\",\n\"ratwa\",\n\"ratwood\",\n\"raucid\",\n\"raucity\",\n\"raucous\",\n\"raught\",\n\"rauk\",\n\"raukle\",\n\"rauli\",\n\"raun\",\n\"raunge\",\n\"raupo\",\n\"rauque\",\n\"ravage\",\n\"ravager\",\n\"rave\",\n\"ravel\",\n\"raveler\",\n\"ravelin\",\n\"ravelly\",\n\"raven\",\n\"ravener\",\n\"ravenry\",\n\"ravens\",\n\"raver\",\n\"ravin\",\n\"ravine\",\n\"ravined\",\n\"raviney\",\n\"raving\",\n\"ravioli\",\n\"ravish\",\n\"ravison\",\n\"raw\",\n\"rawhead\",\n\"rawhide\",\n\"rawish\",\n\"rawness\",\n\"rax\",\n\"ray\",\n\"raya\",\n\"rayage\",\n\"rayed\",\n\"rayful\",\n\"rayless\",\n\"raylet\",\n\"rayon\",\n\"raze\",\n\"razee\",\n\"razer\",\n\"razoo\",\n\"razor\",\n\"razz\",\n\"razzia\",\n\"razzly\",\n\"re\",\n\"rea\",\n\"reaal\",\n\"reabuse\",\n\"reach\",\n\"reacher\",\n\"reachy\",\n\"react\",\n\"reactor\",\n\"read\",\n\"readapt\",\n\"readd\",\n\"reader\",\n\"readily\",\n\"reading\",\n\"readmit\",\n\"readopt\",\n\"readorn\",\n\"ready\",\n\"reagent\",\n\"reagin\",\n\"reagree\",\n\"reak\",\n\"real\",\n\"realarm\",\n\"reales\",\n\"realest\",\n\"realgar\",\n\"realign\",\n\"realism\",\n\"realist\",\n\"reality\",\n\"realive\",\n\"realize\",\n\"reallot\",\n\"reallow\",\n\"really\",\n\"realm\",\n\"realter\",\n\"realtor\",\n\"realty\",\n\"ream\",\n\"reamage\",\n\"reamass\",\n\"reamend\",\n\"reamer\",\n\"reamuse\",\n\"reamy\",\n\"reannex\",\n\"reannoy\",\n\"reanvil\",\n\"reap\",\n\"reaper\",\n\"reapply\",\n\"rear\",\n\"rearer\",\n\"reargue\",\n\"rearise\",\n\"rearm\",\n\"rearray\",\n\"reask\",\n\"reason\",\n\"reassay\",\n\"reasty\",\n\"reasy\",\n\"reatus\",\n\"reaudit\",\n\"reavail\",\n\"reave\",\n\"reaver\",\n\"reavoid\",\n\"reavow\",\n\"reawait\",\n\"reawake\",\n\"reaward\",\n\"reaware\",\n\"reb\",\n\"rebab\",\n\"reback\",\n\"rebag\",\n\"rebait\",\n\"rebake\",\n\"rebale\",\n\"reban\",\n\"rebar\",\n\"rebase\",\n\"rebasis\",\n\"rebate\",\n\"rebater\",\n\"rebathe\",\n\"rebato\",\n\"rebawl\",\n\"rebear\",\n\"rebeat\",\n\"rebec\",\n\"rebeck\",\n\"rebed\",\n\"rebeg\",\n\"rebeget\",\n\"rebegin\",\n\"rebel\",\n\"rebelly\",\n\"rebend\",\n\"rebeset\",\n\"rebia\",\n\"rebias\",\n\"rebid\",\n\"rebill\",\n\"rebind\",\n\"rebirth\",\n\"rebite\",\n\"reblade\",\n\"reblame\",\n\"reblast\",\n\"reblend\",\n\"rebless\",\n\"reblock\",\n\"rebloom\",\n\"reblot\",\n\"reblow\",\n\"reblue\",\n\"rebluff\",\n\"reboant\",\n\"reboard\",\n\"reboast\",\n\"rebob\",\n\"reboil\",\n\"reboise\",\n\"rebold\",\n\"rebolt\",\n\"rebone\",\n\"rebook\",\n\"rebop\",\n\"rebore\",\n\"reborn\",\n\"rebound\",\n\"rebox\",\n\"rebrace\",\n\"rebraid\",\n\"rebrand\",\n\"rebreed\",\n\"rebrew\",\n\"rebribe\",\n\"rebrick\",\n\"rebring\",\n\"rebrown\",\n\"rebrush\",\n\"rebud\",\n\"rebuff\",\n\"rebuild\",\n\"rebuilt\",\n\"rebuke\",\n\"rebuker\",\n\"rebulk\",\n\"rebunch\",\n\"rebuoy\",\n\"reburn\",\n\"reburst\",\n\"rebury\",\n\"rebus\",\n\"rebush\",\n\"rebusy\",\n\"rebut\",\n\"rebute\",\n\"rebuy\",\n\"recable\",\n\"recage\",\n\"recalk\",\n\"recall\",\n\"recant\",\n\"recap\",\n\"recarry\",\n\"recart\",\n\"recarve\",\n\"recase\",\n\"recash\",\n\"recast\",\n\"recatch\",\n\"recce\",\n\"recco\",\n\"reccy\",\n\"recede\",\n\"receder\",\n\"receipt\",\n\"receive\",\n\"recency\",\n\"recense\",\n\"recent\",\n\"recept\",\n\"recess\",\n\"rechafe\",\n\"rechain\",\n\"rechal\",\n\"rechant\",\n\"rechaos\",\n\"rechar\",\n\"rechase\",\n\"rechaw\",\n\"recheat\",\n\"recheck\",\n\"recheer\",\n\"rechew\",\n\"rechip\",\n\"rechuck\",\n\"rechurn\",\n\"recipe\",\n\"recital\",\n\"recite\",\n\"reciter\",\n\"reck\",\n\"reckla\",\n\"reckon\",\n\"reclaim\",\n\"reclama\",\n\"reclang\",\n\"reclasp\",\n\"reclass\",\n\"reclean\",\n\"reclear\",\n\"reclimb\",\n\"recline\",\n\"reclose\",\n\"recluse\",\n\"recoach\",\n\"recoal\",\n\"recoast\",\n\"recoat\",\n\"recock\",\n\"recoct\",\n\"recode\",\n\"recoil\",\n\"recoin\",\n\"recoke\",\n\"recolor\",\n\"recomb\",\n\"recon\",\n\"recook\",\n\"recool\",\n\"recopy\",\n\"record\",\n\"recork\",\n\"recount\",\n\"recoup\",\n\"recover\",\n\"recramp\",\n\"recrank\",\n\"recrate\",\n\"recrew\",\n\"recroon\",\n\"recrop\",\n\"recross\",\n\"recrowd\",\n\"recrown\",\n\"recruit\",\n\"recrush\",\n\"rect\",\n\"recta\",\n\"rectal\",\n\"recti\",\n\"rectify\",\n\"rection\",\n\"recto\",\n\"rector\",\n\"rectory\",\n\"rectrix\",\n\"rectum\",\n\"rectus\",\n\"recur\",\n\"recure\",\n\"recurl\",\n\"recurse\",\n\"recurve\",\n\"recuse\",\n\"recut\",\n\"recycle\",\n\"red\",\n\"redact\",\n\"redan\",\n\"redare\",\n\"redarn\",\n\"redart\",\n\"redate\",\n\"redaub\",\n\"redawn\",\n\"redback\",\n\"redbait\",\n\"redbill\",\n\"redbird\",\n\"redbone\",\n\"redbuck\",\n\"redbud\",\n\"redcap\",\n\"redcoat\",\n\"redd\",\n\"redden\",\n\"redder\",\n\"redding\",\n\"reddish\",\n\"reddock\",\n\"reddy\",\n\"rede\",\n\"redeal\",\n\"redebit\",\n\"redeck\",\n\"redeed\",\n\"redeem\",\n\"redefer\",\n\"redefy\",\n\"redeify\",\n\"redelay\",\n\"redeny\",\n\"redeye\",\n\"redfin\",\n\"redfish\",\n\"redfoot\",\n\"redhead\",\n\"redhoop\",\n\"redia\",\n\"redient\",\n\"redig\",\n\"redip\",\n\"redive\",\n\"redleg\",\n\"redlegs\",\n\"redly\",\n\"redness\",\n\"redo\",\n\"redock\",\n\"redoom\",\n\"redoubt\",\n\"redound\",\n\"redowa\",\n\"redox\",\n\"redpoll\",\n\"redraft\",\n\"redrag\",\n\"redrape\",\n\"redraw\",\n\"redream\",\n\"redress\",\n\"redrill\",\n\"redrive\",\n\"redroot\",\n\"redry\",\n\"redsear\",\n\"redskin\",\n\"redtab\",\n\"redtail\",\n\"redtop\",\n\"redub\",\n\"reduce\",\n\"reduced\",\n\"reducer\",\n\"reduct\",\n\"redue\",\n\"redux\",\n\"redward\",\n\"redware\",\n\"redweed\",\n\"redwing\",\n\"redwood\",\n\"redye\",\n\"ree\",\n\"reechy\",\n\"reed\",\n\"reeded\",\n\"reeden\",\n\"reeder\",\n\"reedily\",\n\"reeding\",\n\"reedish\",\n\"reedman\",\n\"reedy\",\n\"reef\",\n\"reefer\",\n\"reefing\",\n\"reefy\",\n\"reek\",\n\"reeker\",\n\"reeky\",\n\"reel\",\n\"reeled\",\n\"reeler\",\n\"reem\",\n\"reeming\",\n\"reemish\",\n\"reen\",\n\"reenge\",\n\"reeper\",\n\"reese\",\n\"reeshle\",\n\"reesk\",\n\"reesle\",\n\"reest\",\n\"reester\",\n\"reestle\",\n\"reesty\",\n\"reet\",\n\"reetam\",\n\"reetle\",\n\"reeve\",\n\"ref\",\n\"reface\",\n\"refall\",\n\"refan\",\n\"refavor\",\n\"refect\",\n\"refeed\",\n\"refeel\",\n\"refeign\",\n\"refel\",\n\"refence\",\n\"refer\",\n\"referee\",\n\"refetch\",\n\"refight\",\n\"refill\",\n\"refilm\",\n\"refind\",\n\"refine\",\n\"refined\",\n\"refiner\",\n\"refire\",\n\"refit\",\n\"refix\",\n\"reflag\",\n\"reflame\",\n\"reflash\",\n\"reflate\",\n\"reflect\",\n\"reflee\",\n\"reflex\",\n\"refling\",\n\"refloat\",\n\"reflog\",\n\"reflood\",\n\"refloor\",\n\"reflow\",\n\"reflush\",\n\"reflux\",\n\"refly\",\n\"refocus\",\n\"refold\",\n\"refont\",\n\"refool\",\n\"refoot\",\n\"reforce\",\n\"reford\",\n\"reforge\",\n\"reform\",\n\"refound\",\n\"refract\",\n\"refrain\",\n\"reframe\",\n\"refresh\",\n\"refront\",\n\"reft\",\n\"refuel\",\n\"refuge\",\n\"refugee\",\n\"refulge\",\n\"refund\",\n\"refurl\",\n\"refusal\",\n\"refuse\",\n\"refuser\",\n\"refutal\",\n\"refute\",\n\"refuter\",\n\"reg\",\n\"regain\",\n\"regal\",\n\"regale\",\n\"regaler\",\n\"regalia\",\n\"regally\",\n\"regard\",\n\"regatta\",\n\"regauge\",\n\"regency\",\n\"regent\",\n\"reges\",\n\"reget\",\n\"regia\",\n\"regift\",\n\"regild\",\n\"regill\",\n\"regime\",\n\"regimen\",\n\"regin\",\n\"reginal\",\n\"region\",\n\"regive\",\n\"reglair\",\n\"reglaze\",\n\"regle\",\n\"reglet\",\n\"regloss\",\n\"reglove\",\n\"reglow\",\n\"reglue\",\n\"regma\",\n\"regnal\",\n\"regnant\",\n\"regorge\",\n\"regrade\",\n\"regraft\",\n\"regrant\",\n\"regrasp\",\n\"regrass\",\n\"regrate\",\n\"regrede\",\n\"regreen\",\n\"regreet\",\n\"regress\",\n\"regret\",\n\"regrind\",\n\"regrip\",\n\"regroup\",\n\"regrow\",\n\"reguard\",\n\"reguide\",\n\"regula\",\n\"regular\",\n\"reguli\",\n\"regulus\",\n\"regur\",\n\"regurge\",\n\"regush\",\n\"reh\",\n\"rehair\",\n\"rehale\",\n\"rehang\",\n\"reharm\",\n\"rehash\",\n\"rehaul\",\n\"rehead\",\n\"reheal\",\n\"reheap\",\n\"rehear\",\n\"reheat\",\n\"rehedge\",\n\"reheel\",\n\"rehoe\",\n\"rehoist\",\n\"rehonor\",\n\"rehood\",\n\"rehook\",\n\"rehoop\",\n\"rehouse\",\n\"rehung\",\n\"reif\",\n\"reify\",\n\"reign\",\n\"reim\",\n\"reimage\",\n\"reimpel\",\n\"reimply\",\n\"rein\",\n\"reina\",\n\"reincur\",\n\"reindue\",\n\"reinfer\",\n\"reins\",\n\"reinter\",\n\"reis\",\n\"reissue\",\n\"reit\",\n\"reitbok\",\n\"reiter\",\n\"reiver\",\n\"rejail\",\n\"reject\",\n\"rejerk\",\n\"rejoice\",\n\"rejoin\",\n\"rejolt\",\n\"rejudge\",\n\"rekick\",\n\"rekill\",\n\"reking\",\n\"rekiss\",\n\"reknit\",\n\"reknow\",\n\"rel\",\n\"relabel\",\n\"relace\",\n\"relade\",\n\"reladen\",\n\"relais\",\n\"relamp\",\n\"reland\",\n\"relap\",\n\"relapse\",\n\"relast\",\n\"relata\",\n\"relatch\",\n\"relate\",\n\"related\",\n\"relater\",\n\"relator\",\n\"relatum\",\n\"relax\",\n\"relaxed\",\n\"relaxer\",\n\"relay\",\n\"relbun\",\n\"relead\",\n\"releap\",\n\"relearn\",\n\"release\",\n\"relend\",\n\"relent\",\n\"relet\",\n\"relevel\",\n\"relevy\",\n\"reliant\",\n\"relic\",\n\"relick\",\n\"relict\",\n\"relief\",\n\"relier\",\n\"relieve\",\n\"relievo\",\n\"relift\",\n\"relight\",\n\"relime\",\n\"relimit\",\n\"reline\",\n\"reliner\",\n\"relink\",\n\"relish\",\n\"relishy\",\n\"relist\",\n\"relive\",\n\"reload\",\n\"reloan\",\n\"relock\",\n\"relodge\",\n\"relook\",\n\"relose\",\n\"relost\",\n\"relot\",\n\"relove\",\n\"relower\",\n\"reluct\",\n\"relume\",\n\"rely\",\n\"remade\",\n\"remail\",\n\"remain\",\n\"remains\",\n\"remake\",\n\"remaker\",\n\"reman\",\n\"remand\",\n\"remanet\",\n\"remap\",\n\"remarch\",\n\"remark\",\n\"remarry\",\n\"remask\",\n\"remass\",\n\"remast\",\n\"rematch\",\n\"remble\",\n\"remeant\",\n\"remede\",\n\"remedy\",\n\"remeet\",\n\"remelt\",\n\"remend\",\n\"remerge\",\n\"remetal\",\n\"remex\",\n\"remica\",\n\"remicle\",\n\"remiges\",\n\"remill\",\n\"remimic\",\n\"remind\",\n\"remint\",\n\"remiped\",\n\"remise\",\n\"remiss\",\n\"remit\",\n\"remix\",\n\"remnant\",\n\"remock\",\n\"remodel\",\n\"remold\",\n\"remop\",\n\"remora\",\n\"remord\",\n\"remorse\",\n\"remote\",\n\"remould\",\n\"remount\",\n\"removal\",\n\"remove\",\n\"removed\",\n\"remover\",\n\"renable\",\n\"renably\",\n\"renail\",\n\"renal\",\n\"rename\",\n\"rend\",\n\"render\",\n\"reneg\",\n\"renege\",\n\"reneger\",\n\"renegue\",\n\"renerve\",\n\"renes\",\n\"renet\",\n\"renew\",\n\"renewal\",\n\"renewer\",\n\"renin\",\n\"renish\",\n\"renk\",\n\"renky\",\n\"renne\",\n\"rennet\",\n\"rennin\",\n\"renown\",\n\"rent\",\n\"rentage\",\n\"rental\",\n\"rented\",\n\"rentee\",\n\"renter\",\n\"renvoi\",\n\"renvoy\",\n\"reoccur\",\n\"reoffer\",\n\"reoil\",\n\"reomit\",\n\"reopen\",\n\"reorder\",\n\"reown\",\n\"rep\",\n\"repace\",\n\"repack\",\n\"repage\",\n\"repaint\",\n\"repair\",\n\"repale\",\n\"repand\",\n\"repanel\",\n\"repaper\",\n\"repark\",\n\"repass\",\n\"repast\",\n\"repaste\",\n\"repatch\",\n\"repave\",\n\"repawn\",\n\"repay\",\n\"repayal\",\n\"repeal\",\n\"repeat\",\n\"repeg\",\n\"repel\",\n\"repen\",\n\"repent\",\n\"repew\",\n\"rephase\",\n\"repic\",\n\"repick\",\n\"repiece\",\n\"repile\",\n\"repin\",\n\"repine\",\n\"repiner\",\n\"repipe\",\n\"repique\",\n\"repitch\",\n\"repkie\",\n\"replace\",\n\"replait\",\n\"replan\",\n\"replane\",\n\"replant\",\n\"replate\",\n\"replay\",\n\"replead\",\n\"repleat\",\n\"replete\",\n\"replevy\",\n\"replica\",\n\"replier\",\n\"replod\",\n\"replot\",\n\"replow\",\n\"replum\",\n\"replume\",\n\"reply\",\n\"repoint\",\n\"repoll\",\n\"repolon\",\n\"repone\",\n\"repope\",\n\"report\",\n\"reposal\",\n\"repose\",\n\"reposed\",\n\"reposer\",\n\"reposit\",\n\"repost\",\n\"repot\",\n\"repound\",\n\"repour\",\n\"repp\",\n\"repped\",\n\"repray\",\n\"repress\",\n\"reprice\",\n\"reprime\",\n\"reprint\",\n\"reprise\",\n\"reproof\",\n\"reprove\",\n\"reprune\",\n\"reps\",\n\"reptant\",\n\"reptile\",\n\"repuff\",\n\"repugn\",\n\"repulse\",\n\"repump\",\n\"repurge\",\n\"repute\",\n\"reputed\",\n\"requeen\",\n\"request\",\n\"requiem\",\n\"requin\",\n\"require\",\n\"requit\",\n\"requite\",\n\"requiz\",\n\"requote\",\n\"rerack\",\n\"rerail\",\n\"reraise\",\n\"rerake\",\n\"rerank\",\n\"rerate\",\n\"reread\",\n\"reredos\",\n\"reree\",\n\"rereel\",\n\"rereeve\",\n\"rereign\",\n\"rerent\",\n\"rerig\",\n\"rering\",\n\"rerise\",\n\"rerival\",\n\"rerivet\",\n\"rerob\",\n\"rerobe\",\n\"reroll\",\n\"reroof\",\n\"reroot\",\n\"rerope\",\n\"reroute\",\n\"rerow\",\n\"rerub\",\n\"rerun\",\n\"resaca\",\n\"resack\",\n\"resail\",\n\"resale\",\n\"resalt\",\n\"resaw\",\n\"resawer\",\n\"resay\",\n\"rescan\",\n\"rescind\",\n\"rescore\",\n\"rescrub\",\n\"rescue\",\n\"rescuer\",\n\"reseal\",\n\"reseam\",\n\"reseat\",\n\"resect\",\n\"reseda\",\n\"resee\",\n\"reseed\",\n\"reseek\",\n\"reseise\",\n\"reseize\",\n\"reself\",\n\"resell\",\n\"resend\",\n\"resene\",\n\"resent\",\n\"reserve\",\n\"reset\",\n\"resever\",\n\"resew\",\n\"resex\",\n\"resh\",\n\"reshake\",\n\"reshape\",\n\"reshare\",\n\"reshave\",\n\"reshear\",\n\"reshift\",\n\"reshine\",\n\"reship\",\n\"reshoe\",\n\"reshoot\",\n\"reshun\",\n\"reshunt\",\n\"reshut\",\n\"reside\",\n\"resider\",\n\"residua\",\n\"residue\",\n\"resift\",\n\"resigh\",\n\"resign\",\n\"resile\",\n\"resin\",\n\"resina\",\n\"resiner\",\n\"resing\",\n\"resinic\",\n\"resink\",\n\"resinol\",\n\"resiny\",\n\"resist\",\n\"resize\",\n\"resizer\",\n\"reskin\",\n\"reslash\",\n\"reslate\",\n\"reslay\",\n\"reslide\",\n\"reslot\",\n\"resmell\",\n\"resmelt\",\n\"resmile\",\n\"resnap\",\n\"resnub\",\n\"resoak\",\n\"resoap\",\n\"resoil\",\n\"resole\",\n\"resolve\",\n\"resorb\",\n\"resort\",\n\"resound\",\n\"resow\",\n\"resp\",\n\"respace\",\n\"respade\",\n\"respan\",\n\"respeak\",\n\"respect\",\n\"respell\",\n\"respin\",\n\"respire\",\n\"respite\",\n\"resplit\",\n\"respoke\",\n\"respond\",\n\"respot\",\n\"respray\",\n\"respue\",\n\"ressala\",\n\"ressaut\",\n\"rest\",\n\"restack\",\n\"restaff\",\n\"restain\",\n\"restake\",\n\"restamp\",\n\"restant\",\n\"restart\",\n\"restate\",\n\"restaur\",\n\"resteal\",\n\"resteel\",\n\"resteep\",\n\"restem\",\n\"restep\",\n\"rester\",\n\"restes\",\n\"restful\",\n\"restiad\",\n\"restiff\",\n\"resting\",\n\"restir\",\n\"restis\",\n\"restive\",\n\"restock\",\n\"restore\",\n\"restow\",\n\"restrap\",\n\"restrip\",\n\"restudy\",\n\"restuff\",\n\"resty\",\n\"restyle\",\n\"resuck\",\n\"resue\",\n\"resuing\",\n\"resuit\",\n\"result\",\n\"resume\",\n\"resumer\",\n\"resun\",\n\"resup\",\n\"resurge\",\n\"reswage\",\n\"resward\",\n\"reswarm\",\n\"reswear\",\n\"resweat\",\n\"resweep\",\n\"reswell\",\n\"reswill\",\n\"reswim\",\n\"ret\",\n\"retable\",\n\"retack\",\n\"retag\",\n\"retail\",\n\"retain\",\n\"retake\",\n\"retaker\",\n\"retalk\",\n\"retama\",\n\"retame\",\n\"retan\",\n\"retape\",\n\"retard\",\n\"retare\",\n\"retaste\",\n\"retax\",\n\"retch\",\n\"reteach\",\n\"retell\",\n\"retem\",\n\"retempt\",\n\"retene\",\n\"retent\",\n\"retest\",\n\"rethank\",\n\"rethaw\",\n\"rethe\",\n\"rethink\",\n\"rethrow\",\n\"retia\",\n\"retial\",\n\"retiary\",\n\"reticle\",\n\"retie\",\n\"retier\",\n\"retile\",\n\"retill\",\n\"retime\",\n\"retin\",\n\"retina\",\n\"retinal\",\n\"retinol\",\n\"retinue\",\n\"retip\",\n\"retiral\",\n\"retire\",\n\"retired\",\n\"retirer\",\n\"retoast\",\n\"retold\",\n\"retomb\",\n\"retook\",\n\"retool\",\n\"retooth\",\n\"retort\",\n\"retoss\",\n\"retotal\",\n\"retouch\",\n\"retour\",\n\"retrace\",\n\"retrack\",\n\"retract\",\n\"retrad\",\n\"retrade\",\n\"retrain\",\n\"retral\",\n\"retramp\",\n\"retread\",\n\"retreat\",\n\"retree\",\n\"retrial\",\n\"retrim\",\n\"retrip\",\n\"retrot\",\n\"retrude\",\n\"retrue\",\n\"retrust\",\n\"retry\",\n\"retted\",\n\"retter\",\n\"rettery\",\n\"retting\",\n\"rettory\",\n\"retube\",\n\"retuck\",\n\"retune\",\n\"returf\",\n\"return\",\n\"retuse\",\n\"retwine\",\n\"retwist\",\n\"retying\",\n\"retype\",\n\"retzian\",\n\"reune\",\n\"reunify\",\n\"reunion\",\n\"reunite\",\n\"reurge\",\n\"reuse\",\n\"reutter\",\n\"rev\",\n\"revalue\",\n\"revamp\",\n\"revary\",\n\"reve\",\n\"reveal\",\n\"reveil\",\n\"revel\",\n\"reveler\",\n\"revelly\",\n\"revelry\",\n\"revend\",\n\"revenge\",\n\"revent\",\n\"revenue\",\n\"rever\",\n\"reverb\",\n\"revere\",\n\"revered\",\n\"reverer\",\n\"reverie\",\n\"revers\",\n\"reverse\",\n\"reversi\",\n\"reverso\",\n\"revert\",\n\"revery\",\n\"revest\",\n\"revet\",\n\"revete\",\n\"revie\",\n\"review\",\n\"revile\",\n\"reviler\",\n\"revisal\",\n\"revise\",\n\"revisee\",\n\"reviser\",\n\"revisit\",\n\"revisor\",\n\"revival\",\n\"revive\",\n\"reviver\",\n\"revivor\",\n\"revoice\",\n\"revoke\",\n\"revoker\",\n\"revolt\",\n\"revolve\",\n\"revomit\",\n\"revote\",\n\"revue\",\n\"revuist\",\n\"rewade\",\n\"rewager\",\n\"rewake\",\n\"rewaken\",\n\"rewall\",\n\"reward\",\n\"rewarm\",\n\"rewarn\",\n\"rewash\",\n\"rewater\",\n\"rewave\",\n\"rewax\",\n\"rewayle\",\n\"rewear\",\n\"reweave\",\n\"rewed\",\n\"reweigh\",\n\"reweld\",\n\"rewend\",\n\"rewet\",\n\"rewhelp\",\n\"rewhirl\",\n\"rewiden\",\n\"rewin\",\n\"rewind\",\n\"rewire\",\n\"rewish\",\n\"rewood\",\n\"reword\",\n\"rework\",\n\"rewound\",\n\"rewove\",\n\"rewoven\",\n\"rewrap\",\n\"rewrite\",\n\"rex\",\n\"rexen\",\n\"reyield\",\n\"reyoke\",\n\"reyouth\",\n\"rhabdom\",\n\"rhabdos\",\n\"rhabdus\",\n\"rhagite\",\n\"rhagon\",\n\"rhagose\",\n\"rhamn\",\n\"rhamnal\",\n\"rhason\",\n\"rhatany\",\n\"rhe\",\n\"rhea\",\n\"rhebok\",\n\"rheeboc\",\n\"rheebok\",\n\"rheen\",\n\"rheic\",\n\"rhein\",\n\"rheinic\",\n\"rhema\",\n\"rheme\",\n\"rhenium\",\n\"rheotan\",\n\"rhesian\",\n\"rhesus\",\n\"rhetor\",\n\"rheum\",\n\"rheumed\",\n\"rheumic\",\n\"rheumy\",\n\"rhexis\",\n\"rhinal\",\n\"rhine\",\n\"rhinion\",\n\"rhino\",\n\"rhizine\",\n\"rhizoid\",\n\"rhizoma\",\n\"rhizome\",\n\"rhizote\",\n\"rho\",\n\"rhodic\",\n\"rhoding\",\n\"rhodite\",\n\"rhodium\",\n\"rhomb\",\n\"rhombic\",\n\"rhombos\",\n\"rhombus\",\n\"rhubarb\",\n\"rhumb\",\n\"rhumba\",\n\"rhyme\",\n\"rhymer\",\n\"rhymery\",\n\"rhymic\",\n\"rhymist\",\n\"rhymy\",\n\"rhyptic\",\n\"rhythm\",\n\"rhyton\",\n\"ria\",\n\"rial\",\n\"riancy\",\n\"riant\",\n\"riantly\",\n\"riata\",\n\"rib\",\n\"ribald\",\n\"riband\",\n\"ribat\",\n\"ribband\",\n\"ribbed\",\n\"ribber\",\n\"ribbet\",\n\"ribbing\",\n\"ribble\",\n\"ribbon\",\n\"ribbony\",\n\"ribby\",\n\"ribe\",\n\"ribless\",\n\"riblet\",\n\"riblike\",\n\"ribonic\",\n\"ribose\",\n\"ribskin\",\n\"ribwork\",\n\"ribwort\",\n\"rice\",\n\"ricer\",\n\"ricey\",\n\"rich\",\n\"richdom\",\n\"richen\",\n\"riches\",\n\"richly\",\n\"richt\",\n\"ricin\",\n\"ricine\",\n\"ricinic\",\n\"ricinus\",\n\"rick\",\n\"ricker\",\n\"rickets\",\n\"rickety\",\n\"rickey\",\n\"rickle\",\n\"ricksha\",\n\"ricrac\",\n\"rictal\",\n\"rictus\",\n\"rid\",\n\"ridable\",\n\"ridably\",\n\"riddam\",\n\"riddel\",\n\"ridden\",\n\"ridder\",\n\"ridding\",\n\"riddle\",\n\"riddler\",\n\"ride\",\n\"rideau\",\n\"riden\",\n\"rident\",\n\"rider\",\n\"ridered\",\n\"ridge\",\n\"ridged\",\n\"ridgel\",\n\"ridger\",\n\"ridgil\",\n\"ridging\",\n\"ridgy\",\n\"riding\",\n\"ridotto\",\n\"rie\",\n\"riem\",\n\"riempie\",\n\"rier\",\n\"rife\",\n\"rifely\",\n\"riff\",\n\"riffle\",\n\"riffler\",\n\"rifle\",\n\"rifler\",\n\"riflery\",\n\"rifling\",\n\"rift\",\n\"rifter\",\n\"rifty\",\n\"rig\",\n\"rigbane\",\n\"riggald\",\n\"rigger\",\n\"rigging\",\n\"riggish\",\n\"riggite\",\n\"riggot\",\n\"right\",\n\"righten\",\n\"righter\",\n\"rightle\",\n\"rightly\",\n\"righto\",\n\"righty\",\n\"rigid\",\n\"rigidly\",\n\"rigling\",\n\"rignum\",\n\"rigol\",\n\"rigor\",\n\"rigsby\",\n\"rikisha\",\n\"rikk\",\n\"riksha\",\n\"rikshaw\",\n\"rilawa\",\n\"rile\",\n\"riley\",\n\"rill\",\n\"rillet\",\n\"rillett\",\n\"rillock\",\n\"rilly\",\n\"rim\",\n\"rima\",\n\"rimal\",\n\"rimate\",\n\"rimbase\",\n\"rime\",\n\"rimer\",\n\"rimfire\",\n\"rimland\",\n\"rimless\",\n\"rimmed\",\n\"rimmer\",\n\"rimose\",\n\"rimous\",\n\"rimpi\",\n\"rimple\",\n\"rimrock\",\n\"rimu\",\n\"rimula\",\n\"rimy\",\n\"rinceau\",\n\"rinch\",\n\"rincon\",\n\"rind\",\n\"rinded\",\n\"rindle\",\n\"rindy\",\n\"rine\",\n\"ring\",\n\"ringe\",\n\"ringed\",\n\"ringent\",\n\"ringer\",\n\"ringeye\",\n\"ringing\",\n\"ringite\",\n\"ringle\",\n\"ringlet\",\n\"ringman\",\n\"ringtaw\",\n\"ringy\",\n\"rink\",\n\"rinka\",\n\"rinker\",\n\"rinkite\",\n\"rinner\",\n\"rinse\",\n\"rinser\",\n\"rinsing\",\n\"rio\",\n\"riot\",\n\"rioter\",\n\"rioting\",\n\"riotist\",\n\"riotous\",\n\"riotry\",\n\"rip\",\n\"ripa\",\n\"ripal\",\n\"ripcord\",\n\"ripe\",\n\"ripely\",\n\"ripen\",\n\"ripener\",\n\"riper\",\n\"ripgut\",\n\"ripieno\",\n\"ripier\",\n\"ripost\",\n\"riposte\",\n\"ripper\",\n\"rippet\",\n\"rippier\",\n\"ripping\",\n\"rippit\",\n\"ripple\",\n\"rippler\",\n\"ripplet\",\n\"ripply\",\n\"rippon\",\n\"riprap\",\n\"ripsack\",\n\"ripsaw\",\n\"ripup\",\n\"risala\",\n\"risberm\",\n\"rise\",\n\"risen\",\n\"riser\",\n\"rishi\",\n\"risible\",\n\"risibly\",\n\"rising\",\n\"risk\",\n\"risker\",\n\"riskful\",\n\"riskily\",\n\"riskish\",\n\"risky\",\n\"risp\",\n\"risper\",\n\"risque\",\n\"risquee\",\n\"rissel\",\n\"risser\",\n\"rissle\",\n\"rissoid\",\n\"rist\",\n\"ristori\",\n\"rit\",\n\"rita\",\n\"rite\",\n\"ritling\",\n\"ritual\",\n\"ritzy\",\n\"riva\",\n\"rivage\",\n\"rival\",\n\"rivalry\",\n\"rive\",\n\"rivel\",\n\"rivell\",\n\"riven\",\n\"river\",\n\"rivered\",\n\"riverly\",\n\"rivery\",\n\"rivet\",\n\"riveter\",\n\"riving\",\n\"rivose\",\n\"rivulet\",\n\"rix\",\n\"rixy\",\n\"riyal\",\n\"rizzar\",\n\"rizzle\",\n\"rizzom\",\n\"roach\",\n\"road\",\n\"roadbed\",\n\"roaded\",\n\"roader\",\n\"roading\",\n\"roadite\",\n\"roadman\",\n\"roadway\",\n\"roam\",\n\"roamage\",\n\"roamer\",\n\"roaming\",\n\"roan\",\n\"roanoke\",\n\"roar\",\n\"roarer\",\n\"roaring\",\n\"roast\",\n\"roaster\",\n\"rob\",\n\"robalo\",\n\"roband\",\n\"robber\",\n\"robbery\",\n\"robbin\",\n\"robbing\",\n\"robe\",\n\"rober\",\n\"roberd\",\n\"robin\",\n\"robinet\",\n\"robing\",\n\"robinin\",\n\"roble\",\n\"robomb\",\n\"robot\",\n\"robotry\",\n\"robur\",\n\"robust\",\n\"roc\",\n\"rocher\",\n\"rochet\",\n\"rock\",\n\"rockaby\",\n\"rocker\",\n\"rockery\",\n\"rocket\",\n\"rockety\",\n\"rocking\",\n\"rockish\",\n\"rocklay\",\n\"rocklet\",\n\"rockman\",\n\"rocky\",\n\"rococo\",\n\"rocta\",\n\"rod\",\n\"rodd\",\n\"roddin\",\n\"rodding\",\n\"rode\",\n\"rodent\",\n\"rodeo\",\n\"rodge\",\n\"rodham\",\n\"roding\",\n\"rodless\",\n\"rodlet\",\n\"rodlike\",\n\"rodman\",\n\"rodney\",\n\"rodsman\",\n\"rodster\",\n\"rodwood\",\n\"roe\",\n\"roebuck\",\n\"roed\",\n\"roelike\",\n\"roer\",\n\"roey\",\n\"rog\",\n\"rogan\",\n\"roger\",\n\"roggle\",\n\"rogue\",\n\"roguery\",\n\"roguing\",\n\"roguish\",\n\"rohan\",\n\"rohob\",\n\"rohun\",\n\"rohuna\",\n\"roi\",\n\"roid\",\n\"roil\",\n\"roily\",\n\"roister\",\n\"roit\",\n\"roka\",\n\"roke\",\n\"rokeage\",\n\"rokee\",\n\"rokelay\",\n\"roker\",\n\"rokey\",\n\"roky\",\n\"role\",\n\"roleo\",\n\"roll\",\n\"rolled\",\n\"roller\",\n\"rolley\",\n\"rollick\",\n\"rolling\",\n\"rollix\",\n\"rollmop\",\n\"rollock\",\n\"rollway\",\n\"roloway\",\n\"romaika\",\n\"romaine\",\n\"romal\",\n\"romance\",\n\"romancy\",\n\"romanza\",\n\"romaunt\",\n\"rombos\",\n\"romeite\",\n\"romero\",\n\"rommack\",\n\"romp\",\n\"romper\",\n\"romping\",\n\"rompish\",\n\"rompu\",\n\"rompy\",\n\"roncet\",\n\"ronco\",\n\"rond\",\n\"ronde\",\n\"rondeau\",\n\"rondel\",\n\"rondino\",\n\"rondle\",\n\"rondo\",\n\"rondure\",\n\"rone\",\n\"rongeur\",\n\"ronquil\",\n\"rontgen\",\n\"ronyon\",\n\"rood\",\n\"roodle\",\n\"roof\",\n\"roofage\",\n\"roofer\",\n\"roofing\",\n\"rooflet\",\n\"roofman\",\n\"roofy\",\n\"rooibok\",\n\"rooinek\",\n\"rook\",\n\"rooker\",\n\"rookery\",\n\"rookie\",\n\"rookish\",\n\"rooklet\",\n\"rooky\",\n\"rool\",\n\"room\",\n\"roomage\",\n\"roomed\",\n\"roomer\",\n\"roomful\",\n\"roomie\",\n\"roomily\",\n\"roomlet\",\n\"roomth\",\n\"roomthy\",\n\"roomy\",\n\"roon\",\n\"roosa\",\n\"roost\",\n\"roosted\",\n\"rooster\",\n\"root\",\n\"rootage\",\n\"rootcap\",\n\"rooted\",\n\"rooter\",\n\"rootery\",\n\"rootle\",\n\"rootlet\",\n\"rooty\",\n\"roove\",\n\"ropable\",\n\"rope\",\n\"ropeman\",\n\"roper\",\n\"ropery\",\n\"ropes\",\n\"ropeway\",\n\"ropily\",\n\"roping\",\n\"ropish\",\n\"ropp\",\n\"ropy\",\n\"roque\",\n\"roquer\",\n\"roquet\",\n\"roquist\",\n\"roral\",\n\"roric\",\n\"rorqual\",\n\"rorty\",\n\"rory\",\n\"rosal\",\n\"rosario\",\n\"rosary\",\n\"rosated\",\n\"roscid\",\n\"rose\",\n\"roseal\",\n\"roseate\",\n\"rosebay\",\n\"rosebud\",\n\"rosed\",\n\"roseine\",\n\"rosel\",\n\"roselet\",\n\"rosella\",\n\"roselle\",\n\"roseola\",\n\"roseous\",\n\"rosery\",\n\"roset\",\n\"rosetan\",\n\"rosette\",\n\"rosetty\",\n\"rosetum\",\n\"rosety\",\n\"rosied\",\n\"rosier\",\n\"rosilla\",\n\"rosillo\",\n\"rosily\",\n\"rosin\",\n\"rosiny\",\n\"rosland\",\n\"rosoli\",\n\"rosolic\",\n\"rosolio\",\n\"ross\",\n\"rosser\",\n\"rossite\",\n\"rostel\",\n\"roster\",\n\"rostra\",\n\"rostral\",\n\"rostrum\",\n\"rosular\",\n\"rosy\",\n\"rot\",\n\"rota\",\n\"rotal\",\n\"rotaman\",\n\"rotan\",\n\"rotang\",\n\"rotary\",\n\"rotate\",\n\"rotated\",\n\"rotator\",\n\"rotch\",\n\"rote\",\n\"rotella\",\n\"roter\",\n\"rotge\",\n\"rotgut\",\n\"rother\",\n\"rotifer\",\n\"roto\",\n\"rotor\",\n\"rottan\",\n\"rotten\",\n\"rotter\",\n\"rotting\",\n\"rottle\",\n\"rottock\",\n\"rottolo\",\n\"rotula\",\n\"rotulad\",\n\"rotular\",\n\"rotulet\",\n\"rotulus\",\n\"rotund\",\n\"rotunda\",\n\"rotundo\",\n\"roub\",\n\"roucou\",\n\"roud\",\n\"roue\",\n\"rouelle\",\n\"rouge\",\n\"rougeau\",\n\"rougeot\",\n\"rough\",\n\"roughen\",\n\"rougher\",\n\"roughet\",\n\"roughie\",\n\"roughly\",\n\"roughy\",\n\"rougy\",\n\"rouille\",\n\"rouky\",\n\"roulade\",\n\"rouleau\",\n\"roun\",\n\"rounce\",\n\"rouncy\",\n\"round\",\n\"rounded\",\n\"roundel\",\n\"rounder\",\n\"roundly\",\n\"roundup\",\n\"roundy\",\n\"roup\",\n\"rouper\",\n\"roupet\",\n\"roupily\",\n\"roupit\",\n\"roupy\",\n\"rouse\",\n\"rouser\",\n\"rousing\",\n\"roust\",\n\"rouster\",\n\"rout\",\n\"route\",\n\"router\",\n\"routh\",\n\"routhie\",\n\"routhy\",\n\"routine\",\n\"routing\",\n\"routous\",\n\"rove\",\n\"rover\",\n\"rovet\",\n\"rovetto\",\n\"roving\",\n\"row\",\n\"rowable\",\n\"rowan\",\n\"rowboat\",\n\"rowdily\",\n\"rowdy\",\n\"rowed\",\n\"rowel\",\n\"rowen\",\n\"rower\",\n\"rowet\",\n\"rowing\",\n\"rowlet\",\n\"rowlock\",\n\"rowport\",\n\"rowty\",\n\"rowy\",\n\"rox\",\n\"roxy\",\n\"royal\",\n\"royale\",\n\"royalet\",\n\"royally\",\n\"royalty\",\n\"royet\",\n\"royt\",\n\"rozum\",\n\"ruach\",\n\"ruana\",\n\"rub\",\n\"rubasse\",\n\"rubato\",\n\"rubbed\",\n\"rubber\",\n\"rubbers\",\n\"rubbery\",\n\"rubbing\",\n\"rubbish\",\n\"rubble\",\n\"rubbler\",\n\"rubbly\",\n\"rubdown\",\n\"rubelet\",\n\"rubella\",\n\"rubelle\",\n\"rubeola\",\n\"rubiate\",\n\"rubican\",\n\"rubidic\",\n\"rubied\",\n\"rubific\",\n\"rubify\",\n\"rubine\",\n\"rubious\",\n\"ruble\",\n\"rublis\",\n\"rubor\",\n\"rubric\",\n\"rubrica\",\n\"rubrify\",\n\"ruby\",\n\"ruche\",\n\"ruching\",\n\"ruck\",\n\"rucker\",\n\"ruckle\",\n\"rucksey\",\n\"ruckus\",\n\"rucky\",\n\"ruction\",\n\"rud\",\n\"rudas\",\n\"rudd\",\n\"rudder\",\n\"ruddied\",\n\"ruddily\",\n\"ruddle\",\n\"ruddock\",\n\"ruddy\",\n\"rude\",\n\"rudely\",\n\"ruderal\",\n\"rudesby\",\n\"rudge\",\n\"rudish\",\n\"rudity\",\n\"rue\",\n\"rueful\",\n\"ruelike\",\n\"ruelle\",\n\"ruen\",\n\"ruer\",\n\"ruesome\",\n\"ruewort\",\n\"ruff\",\n\"ruffed\",\n\"ruffer\",\n\"ruffian\",\n\"ruffin\",\n\"ruffle\",\n\"ruffled\",\n\"ruffler\",\n\"ruffly\",\n\"rufous\",\n\"rufter\",\n\"rufus\",\n\"rug\",\n\"ruga\",\n\"rugate\",\n\"rugged\",\n\"rugging\",\n\"ruggle\",\n\"ruggy\",\n\"ruglike\",\n\"rugosa\",\n\"rugose\",\n\"rugous\",\n\"ruin\",\n\"ruinate\",\n\"ruined\",\n\"ruiner\",\n\"ruing\",\n\"ruinous\",\n\"rukh\",\n\"rulable\",\n\"rule\",\n\"ruledom\",\n\"ruler\",\n\"ruling\",\n\"rull\",\n\"ruller\",\n\"rullion\",\n\"rum\",\n\"rumal\",\n\"rumble\",\n\"rumbler\",\n\"rumbly\",\n\"rumbo\",\n\"rumen\",\n\"ruminal\",\n\"rumkin\",\n\"rumless\",\n\"rumly\",\n\"rummage\",\n\"rummagy\",\n\"rummer\",\n\"rummily\",\n\"rummish\",\n\"rummy\",\n\"rumness\",\n\"rumney\",\n\"rumor\",\n\"rumorer\",\n\"rump\",\n\"rumpad\",\n\"rumpade\",\n\"rumple\",\n\"rumply\",\n\"rumpus\",\n\"rumshop\",\n\"run\",\n\"runaway\",\n\"runback\",\n\"runby\",\n\"runch\",\n\"rundale\",\n\"rundle\",\n\"rundlet\",\n\"rune\",\n\"runed\",\n\"runer\",\n\"runfish\",\n\"rung\",\n\"runic\",\n\"runite\",\n\"runkle\",\n\"runkly\",\n\"runless\",\n\"runlet\",\n\"runman\",\n\"runnel\",\n\"runner\",\n\"runnet\",\n\"running\",\n\"runny\",\n\"runoff\",\n\"runout\",\n\"runover\",\n\"runrig\",\n\"runt\",\n\"runted\",\n\"runtee\",\n\"runtish\",\n\"runty\",\n\"runway\",\n\"rupa\",\n\"rupee\",\n\"rupia\",\n\"rupiah\",\n\"rupial\",\n\"rupie\",\n\"rupitic\",\n\"ruptile\",\n\"ruption\",\n\"ruptive\",\n\"rupture\",\n\"rural\",\n\"rurally\",\n\"rurban\",\n\"ruru\",\n\"ruse\",\n\"rush\",\n\"rushed\",\n\"rushen\",\n\"rusher\",\n\"rushing\",\n\"rushlit\",\n\"rushy\",\n\"rusine\",\n\"rusk\",\n\"ruskin\",\n\"rusky\",\n\"rusma\",\n\"rusot\",\n\"ruspone\",\n\"russel\",\n\"russet\",\n\"russety\",\n\"russia\",\n\"russud\",\n\"rust\",\n\"rustful\",\n\"rustic\",\n\"rustily\",\n\"rustle\",\n\"rustler\",\n\"rustly\",\n\"rustre\",\n\"rustred\",\n\"rusty\",\n\"ruswut\",\n\"rut\",\n\"rutate\",\n\"rutch\",\n\"ruth\",\n\"ruther\",\n\"ruthful\",\n\"rutic\",\n\"rutile\",\n\"rutin\",\n\"ruttee\",\n\"rutter\",\n\"ruttish\",\n\"rutty\",\n\"rutyl\",\n\"ruvid\",\n\"rux\",\n\"ryal\",\n\"ryania\",\n\"rybat\",\n\"ryder\",\n\"rye\",\n\"ryen\",\n\"ryme\",\n\"rynd\",\n\"rynt\",\n\"ryot\",\n\"ryotwar\",\n\"rype\",\n\"rypeck\",\n\"s\",\n\"sa\",\n\"saa\",\n\"sab\",\n\"sabalo\",\n\"sabanut\",\n\"sabbat\",\n\"sabbath\",\n\"sabe\",\n\"sabeca\",\n\"sabella\",\n\"saber\",\n\"sabered\",\n\"sabicu\",\n\"sabina\",\n\"sabine\",\n\"sabino\",\n\"sable\",\n\"sably\",\n\"sabora\",\n\"sabot\",\n\"saboted\",\n\"sabra\",\n\"sabulum\",\n\"saburra\",\n\"sabutan\",\n\"sabzi\",\n\"sac\",\n\"sacaton\",\n\"sacatra\",\n\"saccade\",\n\"saccate\",\n\"saccos\",\n\"saccule\",\n\"saccus\",\n\"sachem\",\n\"sachet\",\n\"sack\",\n\"sackage\",\n\"sackbag\",\n\"sackbut\",\n\"sacked\",\n\"sacken\",\n\"sacker\",\n\"sackful\",\n\"sacking\",\n\"sackman\",\n\"saclike\",\n\"saco\",\n\"sacope\",\n\"sacque\",\n\"sacra\",\n\"sacrad\",\n\"sacral\",\n\"sacred\",\n\"sacring\",\n\"sacrist\",\n\"sacro\",\n\"sacrum\",\n\"sad\",\n\"sadden\",\n\"saddik\",\n\"saddish\",\n\"saddle\",\n\"saddled\",\n\"saddler\",\n\"sade\",\n\"sadh\",\n\"sadhe\",\n\"sadhu\",\n\"sadic\",\n\"sadiron\",\n\"sadism\",\n\"sadist\",\n\"sadly\",\n\"sadness\",\n\"sado\",\n\"sadr\",\n\"saecula\",\n\"saeter\",\n\"saeume\",\n\"safari\",\n\"safe\",\n\"safely\",\n\"safen\",\n\"safener\",\n\"safety\",\n\"saffian\",\n\"safflor\",\n\"safflow\",\n\"saffron\",\n\"safrole\",\n\"saft\",\n\"sag\",\n\"saga\",\n\"sagaie\",\n\"sagaman\",\n\"sagathy\",\n\"sage\",\n\"sagely\",\n\"sagene\",\n\"sagger\",\n\"sagging\",\n\"saggon\",\n\"saggy\",\n\"saging\",\n\"sagitta\",\n\"sagless\",\n\"sago\",\n\"sagoin\",\n\"saguaro\",\n\"sagum\",\n\"saguran\",\n\"sagwire\",\n\"sagy\",\n\"sah\",\n\"sahh\",\n\"sahib\",\n\"sahme\",\n\"sahukar\",\n\"sai\",\n\"saic\",\n\"said\",\n\"saiga\",\n\"sail\",\n\"sailage\",\n\"sailed\",\n\"sailer\",\n\"sailing\",\n\"sailor\",\n\"saily\",\n\"saim\",\n\"saimiri\",\n\"saimy\",\n\"sain\",\n\"saint\",\n\"sainted\",\n\"saintly\",\n\"saip\",\n\"sair\",\n\"sairly\",\n\"sairve\",\n\"sairy\",\n\"saithe\",\n\"saj\",\n\"sajou\",\n\"sake\",\n\"sakeber\",\n\"sakeen\",\n\"saker\",\n\"sakeret\",\n\"saki\",\n\"sakieh\",\n\"sakulya\",\n\"sal\",\n\"salaam\",\n\"salable\",\n\"salably\",\n\"salacot\",\n\"salad\",\n\"salago\",\n\"salal\",\n\"salamo\",\n\"salar\",\n\"salary\",\n\"salat\",\n\"salay\",\n\"sale\",\n\"salele\",\n\"salema\",\n\"salep\",\n\"salfern\",\n\"salic\",\n\"salicin\",\n\"salicyl\",\n\"salient\",\n\"salify\",\n\"saligot\",\n\"salina\",\n\"saline\",\n\"salite\",\n\"salited\",\n\"saliva\",\n\"salival\",\n\"salix\",\n\"salle\",\n\"sallee\",\n\"sallet\",\n\"sallier\",\n\"salloo\",\n\"sallow\",\n\"sallowy\",\n\"sally\",\n\"salma\",\n\"salmiac\",\n\"salmine\",\n\"salmis\",\n\"salmon\",\n\"salol\",\n\"salomon\",\n\"salon\",\n\"saloon\",\n\"saloop\",\n\"salp\",\n\"salpa\",\n\"salpian\",\n\"salpinx\",\n\"salpoid\",\n\"salse\",\n\"salsify\",\n\"salt\",\n\"salta\",\n\"saltant\",\n\"saltary\",\n\"saltate\",\n\"saltcat\",\n\"salted\",\n\"saltee\",\n\"salten\",\n\"salter\",\n\"saltern\",\n\"saltery\",\n\"saltfat\",\n\"saltier\",\n\"saltine\",\n\"salting\",\n\"saltish\",\n\"saltly\",\n\"saltman\",\n\"saltpan\",\n\"saltus\",\n\"salty\",\n\"saluki\",\n\"salung\",\n\"salute\",\n\"saluter\",\n\"salvage\",\n\"salve\",\n\"salver\",\n\"salviol\",\n\"salvo\",\n\"salvor\",\n\"salvy\",\n\"sam\",\n\"samadh\",\n\"samadhi\",\n\"samaj\",\n\"saman\",\n\"samara\",\n\"samaria\",\n\"samarra\",\n\"samba\",\n\"sambal\",\n\"sambar\",\n\"sambo\",\n\"sambuk\",\n\"sambuke\",\n\"same\",\n\"samekh\",\n\"samel\",\n\"samely\",\n\"samen\",\n\"samh\",\n\"samhita\",\n\"samiel\",\n\"samiri\",\n\"samisen\",\n\"samite\",\n\"samkara\",\n\"samlet\",\n\"sammel\",\n\"sammer\",\n\"sammier\",\n\"sammy\",\n\"samovar\",\n\"samp\",\n\"sampan\",\n\"sampi\",\n\"sample\",\n\"sampler\",\n\"samsara\",\n\"samshu\",\n\"samson\",\n\"samurai\",\n\"san\",\n\"sanable\",\n\"sanai\",\n\"sancho\",\n\"sanct\",\n\"sancta\",\n\"sanctum\",\n\"sand\",\n\"sandak\",\n\"sandal\",\n\"sandan\",\n\"sandbag\",\n\"sandbin\",\n\"sandbox\",\n\"sandboy\",\n\"sandbur\",\n\"sanded\",\n\"sander\",\n\"sanders\",\n\"sandhi\",\n\"sanding\",\n\"sandix\",\n\"sandman\",\n\"sandust\",\n\"sandy\",\n\"sane\",\n\"sanely\",\n\"sang\",\n\"sanga\",\n\"sangar\",\n\"sangei\",\n\"sanger\",\n\"sangha\",\n\"sangley\",\n\"sangrel\",\n\"sangsue\",\n\"sanicle\",\n\"sanies\",\n\"sanify\",\n\"sanious\",\n\"sanity\",\n\"sanjak\",\n\"sank\",\n\"sankha\",\n\"sannup\",\n\"sans\",\n\"sansei\",\n\"sansi\",\n\"sant\",\n\"santal\",\n\"santene\",\n\"santimi\",\n\"santims\",\n\"santir\",\n\"santon\",\n\"sao\",\n\"sap\",\n\"sapa\",\n\"sapajou\",\n\"sapan\",\n\"sapbush\",\n\"sapek\",\n\"sapful\",\n\"saphead\",\n\"saphena\",\n\"saphie\",\n\"sapid\",\n\"sapient\",\n\"sapin\",\n\"sapinda\",\n\"saple\",\n\"sapless\",\n\"sapling\",\n\"sapo\",\n\"saponin\",\n\"sapor\",\n\"sapota\",\n\"sapote\",\n\"sappare\",\n\"sapper\",\n\"sapphic\",\n\"sapping\",\n\"sapples\",\n\"sappy\",\n\"saprine\",\n\"sapsago\",\n\"sapsuck\",\n\"sapwood\",\n\"sapwort\",\n\"sar\",\n\"saraad\",\n\"saraf\",\n\"sarangi\",\n\"sarcasm\",\n\"sarcast\",\n\"sarcine\",\n\"sarcle\",\n\"sarcler\",\n\"sarcode\",\n\"sarcoid\",\n\"sarcoma\",\n\"sarcous\",\n\"sard\",\n\"sardel\",\n\"sardine\",\n\"sardius\",\n\"sare\",\n\"sargo\",\n\"sargus\",\n\"sari\",\n\"sarif\",\n\"sarigue\",\n\"sarinda\",\n\"sarip\",\n\"sark\",\n\"sarkar\",\n\"sarkful\",\n\"sarkine\",\n\"sarking\",\n\"sarkit\",\n\"sarlak\",\n\"sarlyk\",\n\"sarment\",\n\"sarna\",\n\"sarod\",\n\"saron\",\n\"sarong\",\n\"saronic\",\n\"saros\",\n\"sarpler\",\n\"sarpo\",\n\"sarra\",\n\"sarraf\",\n\"sarsa\",\n\"sarsen\",\n\"sart\",\n\"sartage\",\n\"sartain\",\n\"sartor\",\n\"sarus\",\n\"sarwan\",\n\"sasa\",\n\"sasan\",\n\"sasani\",\n\"sash\",\n\"sashay\",\n\"sashery\",\n\"sashing\",\n\"sasin\",\n\"sasine\",\n\"sassaby\",\n\"sassy\",\n\"sat\",\n\"satable\",\n\"satan\",\n\"satang\",\n\"satanic\",\n\"satara\",\n\"satchel\",\n\"sate\",\n\"sateen\",\n\"satiate\",\n\"satient\",\n\"satiety\",\n\"satin\",\n\"satine\",\n\"satined\",\n\"satiny\",\n\"satire\",\n\"satiric\",\n\"satisfy\",\n\"satlijk\",\n\"satrap\",\n\"satrapy\",\n\"satron\",\n\"sattle\",\n\"sattva\",\n\"satura\",\n\"satyr\",\n\"satyric\",\n\"sauce\",\n\"saucer\",\n\"saucily\",\n\"saucy\",\n\"sauf\",\n\"sauger\",\n\"saugh\",\n\"saughen\",\n\"sauld\",\n\"saulie\",\n\"sault\",\n\"saulter\",\n\"saum\",\n\"saumon\",\n\"saumont\",\n\"sauna\",\n\"saunter\",\n\"sauqui\",\n\"saur\",\n\"saurel\",\n\"saurian\",\n\"saury\",\n\"sausage\",\n\"saut\",\n\"saute\",\n\"sauteur\",\n\"sauty\",\n\"sauve\",\n\"savable\",\n\"savacu\",\n\"savage\",\n\"savanna\",\n\"savant\",\n\"savarin\",\n\"save\",\n\"saved\",\n\"saveloy\",\n\"saver\",\n\"savin\",\n\"saving\",\n\"savior\",\n\"savola\",\n\"savor\",\n\"savored\",\n\"savorer\",\n\"savory\",\n\"savour\",\n\"savoy\",\n\"savoyed\",\n\"savssat\",\n\"savvy\",\n\"saw\",\n\"sawah\",\n\"sawali\",\n\"sawarra\",\n\"sawback\",\n\"sawbill\",\n\"sawbuck\",\n\"sawbwa\",\n\"sawder\",\n\"sawdust\",\n\"sawed\",\n\"sawer\",\n\"sawfish\",\n\"sawfly\",\n\"sawing\",\n\"sawish\",\n\"sawlike\",\n\"sawman\",\n\"sawmill\",\n\"sawmon\",\n\"sawmont\",\n\"sawn\",\n\"sawney\",\n\"sawt\",\n\"sawway\",\n\"sawwort\",\n\"sawyer\",\n\"sax\",\n\"saxhorn\",\n\"saxten\",\n\"saxtie\",\n\"saxtuba\",\n\"say\",\n\"saya\",\n\"sayable\",\n\"sayer\",\n\"sayette\",\n\"sayid\",\n\"saying\",\n\"sazen\",\n\"sblood\",\n\"scab\",\n\"scabbed\",\n\"scabble\",\n\"scabby\",\n\"scabid\",\n\"scabies\",\n\"scabish\",\n\"scabrid\",\n\"scad\",\n\"scaddle\",\n\"scads\",\n\"scaff\",\n\"scaffer\",\n\"scaffie\",\n\"scaffle\",\n\"scaglia\",\n\"scala\",\n\"scalage\",\n\"scalar\",\n\"scalare\",\n\"scald\",\n\"scalded\",\n\"scalder\",\n\"scaldic\",\n\"scaldy\",\n\"scale\",\n\"scaled\",\n\"scalena\",\n\"scalene\",\n\"scaler\",\n\"scales\",\n\"scaling\",\n\"scall\",\n\"scalled\",\n\"scallom\",\n\"scallop\",\n\"scalma\",\n\"scaloni\",\n\"scalp\",\n\"scalpel\",\n\"scalper\",\n\"scalt\",\n\"scaly\",\n\"scam\",\n\"scamble\",\n\"scamell\",\n\"scamler\",\n\"scamles\",\n\"scamp\",\n\"scamper\",\n\"scan\",\n\"scandal\",\n\"scandia\",\n\"scandic\",\n\"scanmag\",\n\"scanner\",\n\"scant\",\n\"scantle\",\n\"scantly\",\n\"scanty\",\n\"scap\",\n\"scape\",\n\"scapel\",\n\"scapha\",\n\"scapoid\",\n\"scapose\",\n\"scapple\",\n\"scapula\",\n\"scapus\",\n\"scar\",\n\"scarab\",\n\"scarce\",\n\"scarcen\",\n\"scare\",\n\"scarer\",\n\"scarf\",\n\"scarfed\",\n\"scarfer\",\n\"scarfy\",\n\"scarid\",\n\"scarify\",\n\"scarily\",\n\"scarlet\",\n\"scarman\",\n\"scarn\",\n\"scaroid\",\n\"scarp\",\n\"scarred\",\n\"scarrer\",\n\"scarry\",\n\"scart\",\n\"scarth\",\n\"scarus\",\n\"scarved\",\n\"scary\",\n\"scase\",\n\"scasely\",\n\"scat\",\n\"scatch\",\n\"scathe\",\n\"scatter\",\n\"scatty\",\n\"scatula\",\n\"scaul\",\n\"scaum\",\n\"scaup\",\n\"scauper\",\n\"scaur\",\n\"scaurie\",\n\"scaut\",\n\"scavage\",\n\"scavel\",\n\"scaw\",\n\"scawd\",\n\"scawl\",\n\"scazon\",\n\"sceat\",\n\"scena\",\n\"scenary\",\n\"scend\",\n\"scene\",\n\"scenery\",\n\"scenic\",\n\"scenist\",\n\"scenite\",\n\"scent\",\n\"scented\",\n\"scenter\",\n\"scepsis\",\n\"scepter\",\n\"sceptic\",\n\"sceptry\",\n\"scerne\",\n\"schanz\",\n\"schappe\",\n\"scharf\",\n\"schelly\",\n\"schema\",\n\"scheme\",\n\"schemer\",\n\"schemy\",\n\"schene\",\n\"schepel\",\n\"schepen\",\n\"scherm\",\n\"scherzi\",\n\"scherzo\",\n\"schesis\",\n\"schism\",\n\"schisma\",\n\"schist\",\n\"schloop\",\n\"schmelz\",\n\"scho\",\n\"schola\",\n\"scholae\",\n\"scholar\",\n\"scholia\",\n\"schone\",\n\"school\",\n\"schoon\",\n\"schorl\",\n\"schorly\",\n\"schout\",\n\"schtoff\",\n\"schuh\",\n\"schuhe\",\n\"schuit\",\n\"schule\",\n\"schuss\",\n\"schute\",\n\"schwa\",\n\"schwarz\",\n\"sciapod\",\n\"sciarid\",\n\"sciatic\",\n\"scibile\",\n\"science\",\n\"scient\",\n\"scincid\",\n\"scind\",\n\"sciniph\",\n\"scintle\",\n\"scion\",\n\"scious\",\n\"scirrhi\",\n\"scissel\",\n\"scissor\",\n\"sciurid\",\n\"sclaff\",\n\"sclate\",\n\"sclater\",\n\"sclaw\",\n\"scler\",\n\"sclera\",\n\"scleral\",\n\"sclere\",\n\"scliff\",\n\"sclim\",\n\"sclimb\",\n\"scoad\",\n\"scob\",\n\"scobby\",\n\"scobs\",\n\"scoff\",\n\"scoffer\",\n\"scog\",\n\"scoggan\",\n\"scogger\",\n\"scoggin\",\n\"scoke\",\n\"scolb\",\n\"scold\",\n\"scolder\",\n\"scolex\",\n\"scolia\",\n\"scoliid\",\n\"scolion\",\n\"scolite\",\n\"scollop\",\n\"scolog\",\n\"sconce\",\n\"sconcer\",\n\"scone\",\n\"scoon\",\n\"scoop\",\n\"scooped\",\n\"scooper\",\n\"scoot\",\n\"scooter\",\n\"scopa\",\n\"scopate\",\n\"scope\",\n\"scopet\",\n\"scopic\",\n\"scopine\",\n\"scopola\",\n\"scops\",\n\"scopula\",\n\"scorch\",\n\"score\",\n\"scored\",\n\"scorer\",\n\"scoria\",\n\"scoriac\",\n\"scoriae\",\n\"scorify\",\n\"scoring\",\n\"scorn\",\n\"scorned\",\n\"scorner\",\n\"scorny\",\n\"scorper\",\n\"scorse\",\n\"scot\",\n\"scotale\",\n\"scotch\",\n\"scote\",\n\"scoter\",\n\"scotia\",\n\"scotino\",\n\"scotoma\",\n\"scotomy\",\n\"scouch\",\n\"scouk\",\n\"scoup\",\n\"scour\",\n\"scoured\",\n\"scourer\",\n\"scourge\",\n\"scoury\",\n\"scouse\",\n\"scout\",\n\"scouter\",\n\"scouth\",\n\"scove\",\n\"scovel\",\n\"scovy\",\n\"scow\",\n\"scowder\",\n\"scowl\",\n\"scowler\",\n\"scowman\",\n\"scrab\",\n\"scrabe\",\n\"scrae\",\n\"scrag\",\n\"scraggy\",\n\"scraily\",\n\"scram\",\n\"scran\",\n\"scranch\",\n\"scrank\",\n\"scranky\",\n\"scranny\",\n\"scrap\",\n\"scrape\",\n\"scraped\",\n\"scraper\",\n\"scrapie\",\n\"scrappy\",\n\"scrapy\",\n\"scrat\",\n\"scratch\",\n\"scrath\",\n\"scrauch\",\n\"scraw\",\n\"scrawk\",\n\"scrawl\",\n\"scrawly\",\n\"scrawm\",\n\"scrawny\",\n\"scray\",\n\"scraze\",\n\"screak\",\n\"screaky\",\n\"scream\",\n\"screamy\",\n\"scree\",\n\"screech\",\n\"screed\",\n\"screek\",\n\"screel\",\n\"screen\",\n\"screeny\",\n\"screet\",\n\"screeve\",\n\"screich\",\n\"screigh\",\n\"screve\",\n\"screver\",\n\"screw\",\n\"screwed\",\n\"screwer\",\n\"screwy\",\n\"scribal\",\n\"scribe\",\n\"scriber\",\n\"scride\",\n\"scrieve\",\n\"scrike\",\n\"scrim\",\n\"scrime\",\n\"scrimer\",\n\"scrimp\",\n\"scrimpy\",\n\"scrin\",\n\"scrinch\",\n\"scrine\",\n\"scringe\",\n\"scrip\",\n\"scripee\",\n\"script\",\n\"scritch\",\n\"scrive\",\n\"scriven\",\n\"scriver\",\n\"scrob\",\n\"scrobe\",\n\"scrobis\",\n\"scrod\",\n\"scroff\",\n\"scrog\",\n\"scroggy\",\n\"scrolar\",\n\"scroll\",\n\"scrolly\",\n\"scroo\",\n\"scrooch\",\n\"scrooge\",\n\"scroop\",\n\"scrota\",\n\"scrotal\",\n\"scrotum\",\n\"scrouge\",\n\"scrout\",\n\"scrow\",\n\"scroyle\",\n\"scrub\",\n\"scrubby\",\n\"scruf\",\n\"scruff\",\n\"scruffy\",\n\"scruft\",\n\"scrum\",\n\"scrump\",\n\"scrunch\",\n\"scrunge\",\n\"scrunt\",\n\"scruple\",\n\"scrush\",\n\"scruto\",\n\"scruze\",\n\"scry\",\n\"scryer\",\n\"scud\",\n\"scudder\",\n\"scuddle\",\n\"scuddy\",\n\"scudi\",\n\"scudler\",\n\"scudo\",\n\"scuff\",\n\"scuffed\",\n\"scuffer\",\n\"scuffle\",\n\"scuffly\",\n\"scuffy\",\n\"scuft\",\n\"scufter\",\n\"scug\",\n\"sculch\",\n\"scull\",\n\"sculler\",\n\"scullog\",\n\"sculp\",\n\"sculper\",\n\"sculpin\",\n\"sculpt\",\n\"sculsh\",\n\"scum\",\n\"scumber\",\n\"scumble\",\n\"scummed\",\n\"scummer\",\n\"scummy\",\n\"scun\",\n\"scunder\",\n\"scunner\",\n\"scup\",\n\"scupful\",\n\"scupper\",\n\"scuppet\",\n\"scur\",\n\"scurdy\",\n\"scurf\",\n\"scurfer\",\n\"scurfy\",\n\"scurry\",\n\"scurvy\",\n\"scuse\",\n\"scut\",\n\"scuta\",\n\"scutage\",\n\"scutal\",\n\"scutate\",\n\"scutch\",\n\"scute\",\n\"scutel\",\n\"scutter\",\n\"scuttle\",\n\"scutty\",\n\"scutula\",\n\"scutum\",\n\"scybala\",\n\"scye\",\n\"scypha\",\n\"scyphae\",\n\"scyphi\",\n\"scyphoi\",\n\"scyphus\",\n\"scyt\",\n\"scytale\",\n\"scythe\",\n\"sdeath\",\n\"se\",\n\"sea\",\n\"seadog\",\n\"seafare\",\n\"seafolk\",\n\"seafowl\",\n\"seagirt\",\n\"seagoer\",\n\"seah\",\n\"seak\",\n\"seal\",\n\"sealant\",\n\"sealch\",\n\"sealed\",\n\"sealer\",\n\"sealery\",\n\"sealess\",\n\"sealet\",\n\"sealike\",\n\"sealine\",\n\"sealing\",\n\"seam\",\n\"seaman\",\n\"seamark\",\n\"seamed\",\n\"seamer\",\n\"seaming\",\n\"seamlet\",\n\"seamost\",\n\"seamrog\",\n\"seamy\",\n\"seance\",\n\"seaport\",\n\"sear\",\n\"searce\",\n\"searcer\",\n\"search\",\n\"seared\",\n\"searer\",\n\"searing\",\n\"seary\",\n\"seasick\",\n\"seaside\",\n\"season\",\n\"seat\",\n\"seatang\",\n\"seated\",\n\"seater\",\n\"seathe\",\n\"seating\",\n\"seatron\",\n\"seave\",\n\"seavy\",\n\"seawant\",\n\"seaward\",\n\"seaware\",\n\"seaway\",\n\"seaweed\",\n\"seawife\",\n\"seaworn\",\n\"seax\",\n\"sebacic\",\n\"sebait\",\n\"sebate\",\n\"sebific\",\n\"sebilla\",\n\"sebkha\",\n\"sebum\",\n\"sebundy\",\n\"sec\",\n\"secable\",\n\"secalin\",\n\"secancy\",\n\"secant\",\n\"secede\",\n\"seceder\",\n\"secern\",\n\"secesh\",\n\"sech\",\n\"seck\",\n\"seclude\",\n\"secluse\",\n\"secohm\",\n\"second\",\n\"seconde\",\n\"secos\",\n\"secpar\",\n\"secque\",\n\"secre\",\n\"secrecy\",\n\"secret\",\n\"secreta\",\n\"secrete\",\n\"secreto\",\n\"sect\",\n\"sectary\",\n\"sectile\",\n\"section\",\n\"sectism\",\n\"sectist\",\n\"sective\",\n\"sector\",\n\"secular\",\n\"secund\",\n\"secure\",\n\"securer\",\n\"sedan\",\n\"sedate\",\n\"sedent\",\n\"sedge\",\n\"sedged\",\n\"sedging\",\n\"sedgy\",\n\"sedile\",\n\"sedilia\",\n\"seduce\",\n\"seducee\",\n\"seducer\",\n\"seduct\",\n\"sedum\",\n\"see\",\n\"seeable\",\n\"seech\",\n\"seed\",\n\"seedage\",\n\"seedbed\",\n\"seedbox\",\n\"seeded\",\n\"seeder\",\n\"seedful\",\n\"seedily\",\n\"seedkin\",\n\"seedlet\",\n\"seedlip\",\n\"seedman\",\n\"seedy\",\n\"seege\",\n\"seeing\",\n\"seek\",\n\"seeker\",\n\"seeking\",\n\"seel\",\n\"seelful\",\n\"seely\",\n\"seem\",\n\"seemer\",\n\"seeming\",\n\"seemly\",\n\"seen\",\n\"seenie\",\n\"seep\",\n\"seepage\",\n\"seeped\",\n\"seepy\",\n\"seer\",\n\"seeress\",\n\"seerpaw\",\n\"seesaw\",\n\"seesee\",\n\"seethe\",\n\"seg\",\n\"seggar\",\n\"seggard\",\n\"segged\",\n\"seggrom\",\n\"segment\",\n\"sego\",\n\"segol\",\n\"seiche\",\n\"seidel\",\n\"seine\",\n\"seiner\",\n\"seise\",\n\"seism\",\n\"seismal\",\n\"seismic\",\n\"seit\",\n\"seity\",\n\"seize\",\n\"seizer\",\n\"seizin\",\n\"seizing\",\n\"seizor\",\n\"seizure\",\n\"sejant\",\n\"sejoin\",\n\"sejunct\",\n\"sekos\",\n\"selah\",\n\"selamin\",\n\"seldom\",\n\"seldor\",\n\"sele\",\n\"select\",\n\"selenic\",\n\"self\",\n\"selfdom\",\n\"selfful\",\n\"selfish\",\n\"selfism\",\n\"selfist\",\n\"selfly\",\n\"selion\",\n\"sell\",\n\"sella\",\n\"sellar\",\n\"sellate\",\n\"seller\",\n\"sellie\",\n\"selling\",\n\"sellout\",\n\"selly\",\n\"selsyn\",\n\"selt\",\n\"selva\",\n\"selvage\",\n\"semarum\",\n\"sematic\",\n\"semball\",\n\"semble\",\n\"seme\",\n\"semeed\",\n\"semeia\",\n\"semeion\",\n\"semen\",\n\"semence\",\n\"semese\",\n\"semi\",\n\"semiape\",\n\"semiarc\",\n\"semibay\",\n\"semic\",\n\"semicup\",\n\"semidry\",\n\"semiegg\",\n\"semifib\",\n\"semifit\",\n\"semify\",\n\"semigod\",\n\"semihot\",\n\"seminal\",\n\"seminar\",\n\"semiorb\",\n\"semiped\",\n\"semipro\",\n\"semiraw\",\n\"semis\",\n\"semita\",\n\"semitae\",\n\"semital\",\n\"semiurn\",\n\"semmet\",\n\"semmit\",\n\"semola\",\n\"semsem\",\n\"sen\",\n\"senaite\",\n\"senam\",\n\"senary\",\n\"senate\",\n\"senator\",\n\"sence\",\n\"sencion\",\n\"send\",\n\"sendal\",\n\"sendee\",\n\"sender\",\n\"sending\",\n\"senega\",\n\"senegin\",\n\"senesce\",\n\"senile\",\n\"senior\",\n\"senna\",\n\"sennet\",\n\"sennit\",\n\"sennite\",\n\"sensa\",\n\"sensal\",\n\"sensate\",\n\"sense\",\n\"sensed\",\n\"sensify\",\n\"sensile\",\n\"sension\",\n\"sensism\",\n\"sensist\",\n\"sensive\",\n\"sensize\",\n\"senso\",\n\"sensor\",\n\"sensory\",\n\"sensual\",\n\"sensum\",\n\"sensyne\",\n\"sent\",\n\"sentry\",\n\"sepad\",\n\"sepal\",\n\"sepaled\",\n\"sephen\",\n\"sepia\",\n\"sepian\",\n\"sepiary\",\n\"sepic\",\n\"sepioid\",\n\"sepion\",\n\"sepiost\",\n\"sepium\",\n\"sepone\",\n\"sepoy\",\n\"seppuku\",\n\"seps\",\n\"sepsine\",\n\"sepsis\",\n\"sept\",\n\"septa\",\n\"septal\",\n\"septan\",\n\"septane\",\n\"septate\",\n\"septave\",\n\"septet\",\n\"septic\",\n\"septier\",\n\"septile\",\n\"septime\",\n\"septoic\",\n\"septole\",\n\"septum\",\n\"septuor\",\n\"sequa\",\n\"sequel\",\n\"sequela\",\n\"sequent\",\n\"sequest\",\n\"sequin\",\n\"ser\",\n\"sera\",\n\"serab\",\n\"seragli\",\n\"serai\",\n\"serail\",\n\"seral\",\n\"serang\",\n\"serape\",\n\"seraph\",\n\"serau\",\n\"seraw\",\n\"sercial\",\n\"serdab\",\n\"sere\",\n\"sereh\",\n\"serene\",\n\"serf\",\n\"serfage\",\n\"serfdom\",\n\"serfish\",\n\"serfism\",\n\"serge\",\n\"serger\",\n\"serging\",\n\"serial\",\n\"seriary\",\n\"seriate\",\n\"sericea\",\n\"sericin\",\n\"seriema\",\n\"series\",\n\"serif\",\n\"serific\",\n\"serin\",\n\"serine\",\n\"seringa\",\n\"serio\",\n\"serious\",\n\"serment\",\n\"sermo\",\n\"sermon\",\n\"sero\",\n\"serolin\",\n\"seron\",\n\"seroon\",\n\"seroot\",\n\"seropus\",\n\"serosa\",\n\"serous\",\n\"serow\",\n\"serpent\",\n\"serphid\",\n\"serpigo\",\n\"serpula\",\n\"serra\",\n\"serrage\",\n\"serran\",\n\"serrana\",\n\"serrano\",\n\"serrate\",\n\"serried\",\n\"serry\",\n\"sert\",\n\"serta\",\n\"sertule\",\n\"sertum\",\n\"serum\",\n\"serumal\",\n\"serut\",\n\"servage\",\n\"serval\",\n\"servant\",\n\"serve\",\n\"server\",\n\"servery\",\n\"servet\",\n\"service\",\n\"servile\",\n\"serving\",\n\"servist\",\n\"servo\",\n\"sesame\",\n\"sesma\",\n\"sesqui\",\n\"sess\",\n\"sessile\",\n\"session\",\n\"sestet\",\n\"sesti\",\n\"sestiad\",\n\"sestina\",\n\"sestine\",\n\"sestole\",\n\"sestuor\",\n\"set\",\n\"seta\",\n\"setae\",\n\"setal\",\n\"setback\",\n\"setbolt\",\n\"setdown\",\n\"setfast\",\n\"seth\",\n\"sethead\",\n\"setier\",\n\"setline\",\n\"setness\",\n\"setoff\",\n\"seton\",\n\"setose\",\n\"setous\",\n\"setout\",\n\"setover\",\n\"setsman\",\n\"sett\",\n\"settee\",\n\"setter\",\n\"setting\",\n\"settle\",\n\"settled\",\n\"settler\",\n\"settlor\",\n\"setula\",\n\"setule\",\n\"setup\",\n\"setwall\",\n\"setwise\",\n\"setwork\",\n\"seugh\",\n\"seven\",\n\"sevener\",\n\"seventh\",\n\"seventy\",\n\"sever\",\n\"several\",\n\"severe\",\n\"severer\",\n\"severy\",\n\"sew\",\n\"sewable\",\n\"sewage\",\n\"sewan\",\n\"sewed\",\n\"sewen\",\n\"sewer\",\n\"sewered\",\n\"sewery\",\n\"sewing\",\n\"sewless\",\n\"sewn\",\n\"sex\",\n\"sexed\",\n\"sexern\",\n\"sexfid\",\n\"sexfoil\",\n\"sexhood\",\n\"sexifid\",\n\"sexiped\",\n\"sexless\",\n\"sexlike\",\n\"sexly\",\n\"sext\",\n\"sextain\",\n\"sextan\",\n\"sextans\",\n\"sextant\",\n\"sextar\",\n\"sextary\",\n\"sextern\",\n\"sextet\",\n\"sextic\",\n\"sextile\",\n\"sexto\",\n\"sextole\",\n\"sexton\",\n\"sextry\",\n\"sextula\",\n\"sexual\",\n\"sexuale\",\n\"sexuous\",\n\"sexy\",\n\"sey\",\n\"sfoot\",\n\"sh\",\n\"sha\",\n\"shab\",\n\"shabash\",\n\"shabbed\",\n\"shabble\",\n\"shabby\",\n\"shachle\",\n\"shachly\",\n\"shack\",\n\"shackle\",\n\"shackly\",\n\"shacky\",\n\"shad\",\n\"shade\",\n\"shaded\",\n\"shader\",\n\"shadily\",\n\"shadine\",\n\"shading\",\n\"shadkan\",\n\"shadoof\",\n\"shadow\",\n\"shadowy\",\n\"shady\",\n\"shaffle\",\n\"shaft\",\n\"shafted\",\n\"shafter\",\n\"shafty\",\n\"shag\",\n\"shagbag\",\n\"shagged\",\n\"shaggy\",\n\"shaglet\",\n\"shagrag\",\n\"shah\",\n\"shahdom\",\n\"shahi\",\n\"shahin\",\n\"shaikh\",\n\"shaitan\",\n\"shake\",\n\"shaken\",\n\"shaker\",\n\"shakers\",\n\"shakha\",\n\"shakily\",\n\"shaking\",\n\"shako\",\n\"shakti\",\n\"shaku\",\n\"shaky\",\n\"shale\",\n\"shall\",\n\"shallal\",\n\"shallon\",\n\"shallop\",\n\"shallot\",\n\"shallow\",\n\"shallu\",\n\"shalom\",\n\"shalt\",\n\"shalwar\",\n\"shaly\",\n\"sham\",\n\"shama\",\n\"shamal\",\n\"shamalo\",\n\"shaman\",\n\"shamba\",\n\"shamble\",\n\"shame\",\n\"shamed\",\n\"shamer\",\n\"shamir\",\n\"shammed\",\n\"shammer\",\n\"shammy\",\n\"shampoo\",\n\"shan\",\n\"shandry\",\n\"shandy\",\n\"shangan\",\n\"shank\",\n\"shanked\",\n\"shanker\",\n\"shanna\",\n\"shanny\",\n\"shansa\",\n\"shant\",\n\"shanty\",\n\"shap\",\n\"shape\",\n\"shaped\",\n\"shapely\",\n\"shapen\",\n\"shaper\",\n\"shaping\",\n\"shaps\",\n\"shapy\",\n\"shard\",\n\"sharded\",\n\"shardy\",\n\"share\",\n\"sharer\",\n\"shargar\",\n\"shark\",\n\"sharky\",\n\"sharn\",\n\"sharny\",\n\"sharp\",\n\"sharpen\",\n\"sharper\",\n\"sharpie\",\n\"sharply\",\n\"sharps\",\n\"sharpy\",\n\"sharrag\",\n\"sharry\",\n\"shaster\",\n\"shastra\",\n\"shastri\",\n\"shat\",\n\"shatan\",\n\"shatter\",\n\"shaugh\",\n\"shaul\",\n\"shaup\",\n\"shauri\",\n\"shauwe\",\n\"shave\",\n\"shaved\",\n\"shavee\",\n\"shaven\",\n\"shaver\",\n\"shavery\",\n\"shaving\",\n\"shaw\",\n\"shawl\",\n\"shawled\",\n\"shawm\",\n\"shawny\",\n\"shawy\",\n\"shay\",\n\"she\",\n\"shea\",\n\"sheaf\",\n\"sheafy\",\n\"sheal\",\n\"shear\",\n\"sheard\",\n\"shearer\",\n\"shears\",\n\"sheat\",\n\"sheath\",\n\"sheathe\",\n\"sheathy\",\n\"sheave\",\n\"sheaved\",\n\"shebang\",\n\"shebeen\",\n\"shed\",\n\"shedded\",\n\"shedder\",\n\"sheder\",\n\"shedman\",\n\"shee\",\n\"sheely\",\n\"sheen\",\n\"sheenly\",\n\"sheeny\",\n\"sheep\",\n\"sheepy\",\n\"sheer\",\n\"sheered\",\n\"sheerly\",\n\"sheet\",\n\"sheeted\",\n\"sheeter\",\n\"sheety\",\n\"sheik\",\n\"sheikly\",\n\"shekel\",\n\"shela\",\n\"sheld\",\n\"shelder\",\n\"shelf\",\n\"shelfy\",\n\"shell\",\n\"shellac\",\n\"shelled\",\n\"sheller\",\n\"shellum\",\n\"shelly\",\n\"shelta\",\n\"shelter\",\n\"shelty\",\n\"shelve\",\n\"shelver\",\n\"shelvy\",\n\"shend\",\n\"sheng\",\n\"sheolic\",\n\"sheppey\",\n\"sher\",\n\"sherbet\",\n\"sheriat\",\n\"sherif\",\n\"sherifa\",\n\"sheriff\",\n\"sherifi\",\n\"sherify\",\n\"sherry\",\n\"sheth\",\n\"sheugh\",\n\"sheva\",\n\"shevel\",\n\"shevri\",\n\"shewa\",\n\"shewel\",\n\"sheyle\",\n\"shi\",\n\"shibah\",\n\"shibar\",\n\"shice\",\n\"shicer\",\n\"shicker\",\n\"shide\",\n\"shied\",\n\"shiel\",\n\"shield\",\n\"shier\",\n\"shies\",\n\"shiest\",\n\"shift\",\n\"shifter\",\n\"shifty\",\n\"shigram\",\n\"shih\",\n\"shikar\",\n\"shikara\",\n\"shikari\",\n\"shikimi\",\n\"shikken\",\n\"shiko\",\n\"shikra\",\n\"shilf\",\n\"shilfa\",\n\"shill\",\n\"shilla\",\n\"shillet\",\n\"shilloo\",\n\"shilpit\",\n\"shim\",\n\"shimal\",\n\"shimmer\",\n\"shimmy\",\n\"shimose\",\n\"shimper\",\n\"shin\",\n\"shindig\",\n\"shindle\",\n\"shindy\",\n\"shine\",\n\"shiner\",\n\"shingle\",\n\"shingly\",\n\"shinily\",\n\"shining\",\n\"shinner\",\n\"shinny\",\n\"shinty\",\n\"shiny\",\n\"shinza\",\n\"ship\",\n\"shipboy\",\n\"shipful\",\n\"shiplap\",\n\"shiplet\",\n\"shipman\",\n\"shipped\",\n\"shipper\",\n\"shippo\",\n\"shippon\",\n\"shippy\",\n\"shipway\",\n\"shire\",\n\"shirk\",\n\"shirker\",\n\"shirky\",\n\"shirl\",\n\"shirpit\",\n\"shirr\",\n\"shirt\",\n\"shirty\",\n\"shish\",\n\"shisham\",\n\"shisn\",\n\"shita\",\n\"shither\",\n\"shittah\",\n\"shittim\",\n\"shiv\",\n\"shive\",\n\"shiver\",\n\"shivery\",\n\"shivey\",\n\"shivoo\",\n\"shivy\",\n\"sho\",\n\"shoad\",\n\"shoader\",\n\"shoal\",\n\"shoaler\",\n\"shoaly\",\n\"shoat\",\n\"shock\",\n\"shocker\",\n\"shod\",\n\"shodden\",\n\"shoddy\",\n\"shode\",\n\"shoder\",\n\"shoe\",\n\"shoeboy\",\n\"shoeing\",\n\"shoeman\",\n\"shoer\",\n\"shoful\",\n\"shog\",\n\"shogaol\",\n\"shoggie\",\n\"shoggle\",\n\"shoggly\",\n\"shogi\",\n\"shogun\",\n\"shohet\",\n\"shoji\",\n\"shola\",\n\"shole\",\n\"shone\",\n\"shoneen\",\n\"shoo\",\n\"shood\",\n\"shoofa\",\n\"shoofly\",\n\"shooi\",\n\"shook\",\n\"shool\",\n\"shooler\",\n\"shoop\",\n\"shoor\",\n\"shoot\",\n\"shootee\",\n\"shooter\",\n\"shop\",\n\"shopboy\",\n\"shopful\",\n\"shophar\",\n\"shoplet\",\n\"shopman\",\n\"shoppe\",\n\"shopper\",\n\"shoppy\",\n\"shoq\",\n\"shor\",\n\"shoran\",\n\"shore\",\n\"shored\",\n\"shorer\",\n\"shoring\",\n\"shorn\",\n\"short\",\n\"shorten\",\n\"shorter\",\n\"shortly\",\n\"shorts\",\n\"shot\",\n\"shote\",\n\"shotgun\",\n\"shotman\",\n\"shott\",\n\"shotted\",\n\"shotten\",\n\"shotter\",\n\"shotty\",\n\"shou\",\n\"should\",\n\"shout\",\n\"shouter\",\n\"shoval\",\n\"shove\",\n\"shovel\",\n\"shover\",\n\"show\",\n\"showdom\",\n\"shower\",\n\"showery\",\n\"showily\",\n\"showing\",\n\"showish\",\n\"showman\",\n\"shown\",\n\"showup\",\n\"showy\",\n\"shoya\",\n\"shrab\",\n\"shradh\",\n\"shraf\",\n\"shrag\",\n\"shram\",\n\"shrank\",\n\"shrap\",\n\"shrave\",\n\"shravey\",\n\"shred\",\n\"shreddy\",\n\"shree\",\n\"shreeve\",\n\"shrend\",\n\"shrew\",\n\"shrewd\",\n\"shrewdy\",\n\"shrewly\",\n\"shriek\",\n\"shrieky\",\n\"shrift\",\n\"shrike\",\n\"shrill\",\n\"shrilly\",\n\"shrimp\",\n\"shrimpi\",\n\"shrimpy\",\n\"shrinal\",\n\"shrine\",\n\"shrink\",\n\"shrinky\",\n\"shrip\",\n\"shrite\",\n\"shrive\",\n\"shrivel\",\n\"shriven\",\n\"shriver\",\n\"shroff\",\n\"shrog\",\n\"shroud\",\n\"shroudy\",\n\"shrove\",\n\"shrover\",\n\"shrub\",\n\"shrubby\",\n\"shruff\",\n\"shrug\",\n\"shrunk\",\n\"shrups\",\n\"shuba\",\n\"shuck\",\n\"shucker\",\n\"shucks\",\n\"shudder\",\n\"shuff\",\n\"shuffle\",\n\"shug\",\n\"shul\",\n\"shuler\",\n\"shumac\",\n\"shun\",\n\"shune\",\n\"shunner\",\n\"shunt\",\n\"shunter\",\n\"shure\",\n\"shurf\",\n\"shush\",\n\"shusher\",\n\"shut\",\n\"shutoff\",\n\"shutout\",\n\"shutten\",\n\"shutter\",\n\"shuttle\",\n\"shy\",\n\"shyer\",\n\"shyish\",\n\"shyly\",\n\"shyness\",\n\"shyster\",\n\"si\",\n\"siak\",\n\"sial\",\n\"sialic\",\n\"sialid\",\n\"sialoid\",\n\"siamang\",\n\"sib\",\n\"sibbed\",\n\"sibbens\",\n\"sibber\",\n\"sibby\",\n\"sibilus\",\n\"sibling\",\n\"sibness\",\n\"sibrede\",\n\"sibship\",\n\"sibyl\",\n\"sibylic\",\n\"sibylla\",\n\"sic\",\n\"sicca\",\n\"siccant\",\n\"siccate\",\n\"siccity\",\n\"sice\",\n\"sick\",\n\"sickbed\",\n\"sicken\",\n\"sicker\",\n\"sickish\",\n\"sickle\",\n\"sickled\",\n\"sickler\",\n\"sickly\",\n\"sicsac\",\n\"sicula\",\n\"sicular\",\n\"sidder\",\n\"siddur\",\n\"side\",\n\"sideage\",\n\"sidearm\",\n\"sidecar\",\n\"sided\",\n\"sider\",\n\"sideral\",\n\"siderin\",\n\"sides\",\n\"sideway\",\n\"sidhe\",\n\"sidi\",\n\"siding\",\n\"sidle\",\n\"sidler\",\n\"sidling\",\n\"sidth\",\n\"sidy\",\n\"sie\",\n\"siege\",\n\"sieger\",\n\"sienna\",\n\"sier\",\n\"siering\",\n\"sierra\",\n\"sierran\",\n\"siesta\",\n\"sieve\",\n\"siever\",\n\"sievy\",\n\"sifac\",\n\"sifaka\",\n\"sife\",\n\"siffle\",\n\"sifflet\",\n\"sifflot\",\n\"sift\",\n\"siftage\",\n\"sifted\",\n\"sifter\",\n\"sifting\",\n\"sig\",\n\"sigger\",\n\"sigh\",\n\"sigher\",\n\"sighful\",\n\"sighing\",\n\"sight\",\n\"sighted\",\n\"sighten\",\n\"sighter\",\n\"sightly\",\n\"sighty\",\n\"sigil\",\n\"sigla\",\n\"siglos\",\n\"sigma\",\n\"sigmate\",\n\"sigmoid\",\n\"sign\",\n\"signal\",\n\"signary\",\n\"signate\",\n\"signee\",\n\"signer\",\n\"signet\",\n\"signify\",\n\"signior\",\n\"signist\",\n\"signman\",\n\"signory\",\n\"signum\",\n\"sika\",\n\"sikar\",\n\"sikatch\",\n\"sike\",\n\"sikerly\",\n\"siket\",\n\"sikhara\",\n\"sikhra\",\n\"sil\",\n\"silage\",\n\"silane\",\n\"sile\",\n\"silen\",\n\"silence\",\n\"silency\",\n\"sileni\",\n\"silenic\",\n\"silent\",\n\"silenus\",\n\"silesia\",\n\"silex\",\n\"silica\",\n\"silicam\",\n\"silicic\",\n\"silicle\",\n\"silico\",\n\"silicon\",\n\"silicyl\",\n\"siliqua\",\n\"silique\",\n\"silk\",\n\"silked\",\n\"silken\",\n\"silker\",\n\"silkie\",\n\"silkily\",\n\"silkman\",\n\"silky\",\n\"sill\",\n\"sillar\",\n\"siller\",\n\"sillily\",\n\"sillock\",\n\"sillon\",\n\"silly\",\n\"silo\",\n\"siloist\",\n\"silphid\",\n\"silt\",\n\"siltage\",\n\"silting\",\n\"silty\",\n\"silurid\",\n\"silva\",\n\"silvan\",\n\"silver\",\n\"silvern\",\n\"silvery\",\n\"silvics\",\n\"silyl\",\n\"sima\",\n\"simal\",\n\"simar\",\n\"simball\",\n\"simbil\",\n\"simblin\",\n\"simblot\",\n\"sime\",\n\"simiad\",\n\"simial\",\n\"simian\",\n\"similar\",\n\"simile\",\n\"similor\",\n\"simioid\",\n\"simious\",\n\"simity\",\n\"simkin\",\n\"simlin\",\n\"simling\",\n\"simmer\",\n\"simmon\",\n\"simnel\",\n\"simony\",\n\"simool\",\n\"simoom\",\n\"simoon\",\n\"simous\",\n\"simp\",\n\"simpai\",\n\"simper\",\n\"simple\",\n\"simpler\",\n\"simplex\",\n\"simply\",\n\"simsim\",\n\"simson\",\n\"simular\",\n\"simuler\",\n\"sin\",\n\"sina\",\n\"sinaite\",\n\"sinal\",\n\"sinamay\",\n\"sinapic\",\n\"sinapis\",\n\"sinawa\",\n\"since\",\n\"sincere\",\n\"sind\",\n\"sinder\",\n\"sindle\",\n\"sindoc\",\n\"sindon\",\n\"sindry\",\n\"sine\",\n\"sinew\",\n\"sinewed\",\n\"sinewy\",\n\"sinful\",\n\"sing\",\n\"singe\",\n\"singed\",\n\"singer\",\n\"singey\",\n\"singh\",\n\"singing\",\n\"single\",\n\"singled\",\n\"singler\",\n\"singles\",\n\"singlet\",\n\"singly\",\n\"singult\",\n\"sinh\",\n\"sink\",\n\"sinkage\",\n\"sinker\",\n\"sinking\",\n\"sinky\",\n\"sinless\",\n\"sinlike\",\n\"sinnen\",\n\"sinner\",\n\"sinnet\",\n\"sinopia\",\n\"sinople\",\n\"sinsion\",\n\"sinsyne\",\n\"sinter\",\n\"sintoc\",\n\"sinuate\",\n\"sinuose\",\n\"sinuous\",\n\"sinus\",\n\"sinusal\",\n\"sinward\",\n\"siol\",\n\"sion\",\n\"sip\",\n\"sipage\",\n\"sipe\",\n\"siper\",\n\"siphoid\",\n\"siphon\",\n\"sipid\",\n\"siping\",\n\"sipling\",\n\"sipper\",\n\"sippet\",\n\"sippio\",\n\"sir\",\n\"sircar\",\n\"sirdar\",\n\"sire\",\n\"siren\",\n\"sirene\",\n\"sirenic\",\n\"sireny\",\n\"siress\",\n\"sirgang\",\n\"sirian\",\n\"siricid\",\n\"sirih\",\n\"siris\",\n\"sirkeer\",\n\"sirki\",\n\"sirky\",\n\"sirloin\",\n\"siroc\",\n\"sirocco\",\n\"sirpea\",\n\"sirple\",\n\"sirpoon\",\n\"sirrah\",\n\"sirree\",\n\"sirship\",\n\"sirup\",\n\"siruped\",\n\"siruper\",\n\"sirupy\",\n\"sis\",\n\"sisal\",\n\"sise\",\n\"sisel\",\n\"sish\",\n\"sisham\",\n\"sisi\",\n\"siskin\",\n\"siss\",\n\"sissify\",\n\"sissoo\",\n\"sissy\",\n\"sist\",\n\"sister\",\n\"sistern\",\n\"sistle\",\n\"sistrum\",\n\"sit\",\n\"sitao\",\n\"sitar\",\n\"sitch\",\n\"site\",\n\"sitfast\",\n\"sith\",\n\"sithe\",\n\"sithens\",\n\"sitient\",\n\"sitio\",\n\"sittee\",\n\"sitten\",\n\"sitter\",\n\"sittine\",\n\"sitting\",\n\"situal\",\n\"situate\",\n\"situla\",\n\"situlae\",\n\"situs\",\n\"siva\",\n\"siver\",\n\"sivvens\",\n\"siwash\",\n\"six\",\n\"sixain\",\n\"sixer\",\n\"sixfoil\",\n\"sixfold\",\n\"sixsome\",\n\"sixte\",\n\"sixteen\",\n\"sixth\",\n\"sixthet\",\n\"sixthly\",\n\"sixty\",\n\"sizable\",\n\"sizably\",\n\"sizal\",\n\"sizar\",\n\"size\",\n\"sized\",\n\"sizeman\",\n\"sizer\",\n\"sizes\",\n\"sizing\",\n\"sizy\",\n\"sizygia\",\n\"sizz\",\n\"sizzard\",\n\"sizzing\",\n\"sizzle\",\n\"sjambok\",\n\"skaddle\",\n\"skaff\",\n\"skaffie\",\n\"skag\",\n\"skair\",\n\"skal\",\n\"skance\",\n\"skart\",\n\"skasely\",\n\"skat\",\n\"skate\",\n\"skater\",\n\"skatiku\",\n\"skating\",\n\"skatist\",\n\"skatole\",\n\"skaw\",\n\"skean\",\n\"skedge\",\n\"skee\",\n\"skeed\",\n\"skeeg\",\n\"skeel\",\n\"skeely\",\n\"skeen\",\n\"skeer\",\n\"skeered\",\n\"skeery\",\n\"skeet\",\n\"skeeter\",\n\"skeezix\",\n\"skeg\",\n\"skegger\",\n\"skeif\",\n\"skeigh\",\n\"skeily\",\n\"skein\",\n\"skeiner\",\n\"skeipp\",\n\"skel\",\n\"skelder\",\n\"skelf\",\n\"skelic\",\n\"skell\",\n\"skellat\",\n\"skeller\",\n\"skellum\",\n\"skelly\",\n\"skelp\",\n\"skelper\",\n\"skelpin\",\n\"skelter\",\n\"skemmel\",\n\"skemp\",\n\"sken\",\n\"skene\",\n\"skeo\",\n\"skeough\",\n\"skep\",\n\"skepful\",\n\"skeptic\",\n\"sker\",\n\"skere\",\n\"skerret\",\n\"skerry\",\n\"sketch\",\n\"sketchy\",\n\"skete\",\n\"skevish\",\n\"skew\",\n\"skewed\",\n\"skewer\",\n\"skewl\",\n\"skewly\",\n\"skewy\",\n\"skey\",\n\"ski\",\n\"skiapod\",\n\"skibby\",\n\"skice\",\n\"skid\",\n\"skidded\",\n\"skidder\",\n\"skiddoo\",\n\"skiddy\",\n\"skidpan\",\n\"skidway\",\n\"skied\",\n\"skieppe\",\n\"skier\",\n\"skies\",\n\"skiff\",\n\"skift\",\n\"skiing\",\n\"skijore\",\n\"skil\",\n\"skilder\",\n\"skill\",\n\"skilled\",\n\"skillet\",\n\"skilly\",\n\"skilpot\",\n\"skilts\",\n\"skim\",\n\"skime\",\n\"skimmed\",\n\"skimmer\",\n\"skimp\",\n\"skimpy\",\n\"skin\",\n\"skinch\",\n\"skinful\",\n\"skink\",\n\"skinker\",\n\"skinkle\",\n\"skinned\",\n\"skinner\",\n\"skinny\",\n\"skip\",\n\"skipman\",\n\"skippel\",\n\"skipper\",\n\"skippet\",\n\"skipple\",\n\"skippy\",\n\"skirl\",\n\"skirp\",\n\"skirr\",\n\"skirreh\",\n\"skirret\",\n\"skirt\",\n\"skirted\",\n\"skirter\",\n\"skirty\",\n\"skit\",\n\"skite\",\n\"skiter\",\n\"skither\",\n\"skitter\",\n\"skittle\",\n\"skitty\",\n\"skiv\",\n\"skive\",\n\"skiver\",\n\"skiving\",\n\"sklate\",\n\"sklater\",\n\"sklent\",\n\"skoal\",\n\"skoo\",\n\"skookum\",\n\"skoptsy\",\n\"skout\",\n\"skraigh\",\n\"skrike\",\n\"skrupul\",\n\"skua\",\n\"skulk\",\n\"skulker\",\n\"skull\",\n\"skulled\",\n\"skully\",\n\"skulp\",\n\"skun\",\n\"skunk\",\n\"skunky\",\n\"skuse\",\n\"sky\",\n\"skybal\",\n\"skyey\",\n\"skyful\",\n\"skyish\",\n\"skylark\",\n\"skyless\",\n\"skylike\",\n\"skylook\",\n\"skyman\",\n\"skyphoi\",\n\"skyphos\",\n\"skyre\",\n\"skysail\",\n\"skyugle\",\n\"skyward\",\n\"skyway\",\n\"sla\",\n\"slab\",\n\"slabbed\",\n\"slabber\",\n\"slabby\",\n\"slabman\",\n\"slack\",\n\"slacked\",\n\"slacken\",\n\"slacker\",\n\"slackly\",\n\"slad\",\n\"sladang\",\n\"slade\",\n\"slae\",\n\"slag\",\n\"slagger\",\n\"slaggy\",\n\"slagman\",\n\"slain\",\n\"slainte\",\n\"slait\",\n\"slake\",\n\"slaker\",\n\"slaking\",\n\"slaky\",\n\"slam\",\n\"slamp\",\n\"slander\",\n\"slane\",\n\"slang\",\n\"slangy\",\n\"slank\",\n\"slant\",\n\"slantly\",\n\"slap\",\n\"slape\",\n\"slapper\",\n\"slare\",\n\"slart\",\n\"slarth\",\n\"slash\",\n\"slashed\",\n\"slasher\",\n\"slashy\",\n\"slat\",\n\"slatch\",\n\"slate\",\n\"slater\",\n\"slath\",\n\"slather\",\n\"slatify\",\n\"slating\",\n\"slatish\",\n\"slatted\",\n\"slatter\",\n\"slaty\",\n\"slaum\",\n\"slave\",\n\"slaved\",\n\"slaver\",\n\"slavery\",\n\"slavey\",\n\"slaving\",\n\"slavish\",\n\"slaw\",\n\"slay\",\n\"slayer\",\n\"slaying\",\n\"sleathy\",\n\"sleave\",\n\"sleaved\",\n\"sleazy\",\n\"sleck\",\n\"sled\",\n\"sledded\",\n\"sledder\",\n\"sledful\",\n\"sledge\",\n\"sledger\",\n\"slee\",\n\"sleech\",\n\"sleechy\",\n\"sleek\",\n\"sleeken\",\n\"sleeker\",\n\"sleekit\",\n\"sleekly\",\n\"sleeky\",\n\"sleep\",\n\"sleeper\",\n\"sleepry\",\n\"sleepy\",\n\"sleer\",\n\"sleet\",\n\"sleety\",\n\"sleeve\",\n\"sleeved\",\n\"sleever\",\n\"sleigh\",\n\"sleight\",\n\"slender\",\n\"slent\",\n\"slepez\",\n\"slept\",\n\"slete\",\n\"sleuth\",\n\"slew\",\n\"slewed\",\n\"slewer\",\n\"slewing\",\n\"sley\",\n\"sleyer\",\n\"slice\",\n\"sliced\",\n\"slicer\",\n\"slich\",\n\"slicht\",\n\"slicing\",\n\"slick\",\n\"slicken\",\n\"slicker\",\n\"slickly\",\n\"slid\",\n\"slidage\",\n\"slidden\",\n\"slidder\",\n\"slide\",\n\"slided\",\n\"slider\",\n\"sliding\",\n\"slifter\",\n\"slight\",\n\"slighty\",\n\"slim\",\n\"slime\",\n\"slimer\",\n\"slimily\",\n\"slimish\",\n\"slimly\",\n\"slimpsy\",\n\"slimsy\",\n\"slimy\",\n\"sline\",\n\"sling\",\n\"slinge\",\n\"slinger\",\n\"slink\",\n\"slinker\",\n\"slinky\",\n\"slip\",\n\"slipe\",\n\"slipman\",\n\"slipped\",\n\"slipper\",\n\"slippy\",\n\"slipway\",\n\"slirt\",\n\"slish\",\n\"slit\",\n\"slitch\",\n\"slite\",\n\"slither\",\n\"slithy\",\n\"slitted\",\n\"slitter\",\n\"slitty\",\n\"slive\",\n\"sliver\",\n\"slivery\",\n\"sliving\",\n\"sloan\",\n\"slob\",\n\"slobber\",\n\"slobby\",\n\"slock\",\n\"slocken\",\n\"slod\",\n\"slodder\",\n\"slodge\",\n\"slodger\",\n\"sloe\",\n\"slog\",\n\"slogan\",\n\"slogger\",\n\"sloka\",\n\"sloke\",\n\"slon\",\n\"slone\",\n\"slonk\",\n\"sloo\",\n\"sloom\",\n\"sloomy\",\n\"sloop\",\n\"sloosh\",\n\"slop\",\n\"slope\",\n\"sloped\",\n\"slopely\",\n\"sloper\",\n\"sloping\",\n\"slopped\",\n\"sloppy\",\n\"slops\",\n\"slopy\",\n\"slorp\",\n\"slosh\",\n\"slosher\",\n\"sloshy\",\n\"slot\",\n\"slote\",\n\"sloted\",\n\"sloth\",\n\"slotted\",\n\"slotter\",\n\"slouch\",\n\"slouchy\",\n\"slough\",\n\"sloughy\",\n\"slour\",\n\"sloush\",\n\"sloven\",\n\"slow\",\n\"slowish\",\n\"slowly\",\n\"slowrie\",\n\"slows\",\n\"sloyd\",\n\"slub\",\n\"slubber\",\n\"slubby\",\n\"slud\",\n\"sludder\",\n\"sludge\",\n\"sludged\",\n\"sludger\",\n\"sludgy\",\n\"slue\",\n\"sluer\",\n\"slug\",\n\"slugged\",\n\"slugger\",\n\"sluggy\",\n\"sluice\",\n\"sluicer\",\n\"sluicy\",\n\"sluig\",\n\"sluit\",\n\"slum\",\n\"slumber\",\n\"slumdom\",\n\"slumgum\",\n\"slummer\",\n\"slummy\",\n\"slump\",\n\"slumpy\",\n\"slung\",\n\"slunge\",\n\"slunk\",\n\"slunken\",\n\"slur\",\n\"slurbow\",\n\"slurp\",\n\"slurry\",\n\"slush\",\n\"slusher\",\n\"slushy\",\n\"slut\",\n\"slutch\",\n\"slutchy\",\n\"sluther\",\n\"slutter\",\n\"slutty\",\n\"sly\",\n\"slyish\",\n\"slyly\",\n\"slyness\",\n\"slype\",\n\"sma\",\n\"smack\",\n\"smackee\",\n\"smacker\",\n\"smaik\",\n\"small\",\n\"smallen\",\n\"smaller\",\n\"smalls\",\n\"smally\",\n\"smalm\",\n\"smalt\",\n\"smalter\",\n\"smalts\",\n\"smaragd\",\n\"smarm\",\n\"smarmy\",\n\"smart\",\n\"smarten\",\n\"smartly\",\n\"smarty\",\n\"smash\",\n\"smasher\",\n\"smashup\",\n\"smatter\",\n\"smaze\",\n\"smear\",\n\"smeared\",\n\"smearer\",\n\"smeary\",\n\"smectic\",\n\"smectis\",\n\"smeddum\",\n\"smee\",\n\"smeech\",\n\"smeek\",\n\"smeeky\",\n\"smeer\",\n\"smeeth\",\n\"smegma\",\n\"smell\",\n\"smelled\",\n\"smeller\",\n\"smelly\",\n\"smelt\",\n\"smelter\",\n\"smeth\",\n\"smethe\",\n\"smeuse\",\n\"smew\",\n\"smich\",\n\"smicker\",\n\"smicket\",\n\"smiddie\",\n\"smiddum\",\n\"smidge\",\n\"smidgen\",\n\"smilax\",\n\"smile\",\n\"smiler\",\n\"smilet\",\n\"smiling\",\n\"smily\",\n\"smirch\",\n\"smirchy\",\n\"smiris\",\n\"smirk\",\n\"smirker\",\n\"smirkle\",\n\"smirkly\",\n\"smirky\",\n\"smirtle\",\n\"smit\",\n\"smitch\",\n\"smite\",\n\"smiter\",\n\"smith\",\n\"smitham\",\n\"smither\",\n\"smithy\",\n\"smiting\",\n\"smitten\",\n\"smock\",\n\"smocker\",\n\"smog\",\n\"smoke\",\n\"smoked\",\n\"smoker\",\n\"smokery\",\n\"smokily\",\n\"smoking\",\n\"smokish\",\n\"smoky\",\n\"smolder\",\n\"smolt\",\n\"smooch\",\n\"smoochy\",\n\"smoodge\",\n\"smook\",\n\"smoot\",\n\"smooth\",\n\"smopple\",\n\"smore\",\n\"smote\",\n\"smother\",\n\"smotter\",\n\"smouch\",\n\"smous\",\n\"smouse\",\n\"smouser\",\n\"smout\",\n\"smriti\",\n\"smudge\",\n\"smudged\",\n\"smudger\",\n\"smudgy\",\n\"smug\",\n\"smuggle\",\n\"smugism\",\n\"smugly\",\n\"smuisty\",\n\"smur\",\n\"smurr\",\n\"smurry\",\n\"smuse\",\n\"smush\",\n\"smut\",\n\"smutch\",\n\"smutchy\",\n\"smutted\",\n\"smutter\",\n\"smutty\",\n\"smyth\",\n\"smytrie\",\n\"snab\",\n\"snabbie\",\n\"snabble\",\n\"snack\",\n\"snackle\",\n\"snaff\",\n\"snaffle\",\n\"snafu\",\n\"snag\",\n\"snagged\",\n\"snagger\",\n\"snaggy\",\n\"snagrel\",\n\"snail\",\n\"snails\",\n\"snaily\",\n\"snaith\",\n\"snake\",\n\"snaker\",\n\"snakery\",\n\"snakily\",\n\"snaking\",\n\"snakish\",\n\"snaky\",\n\"snap\",\n\"snapbag\",\n\"snape\",\n\"snaper\",\n\"snapped\",\n\"snapper\",\n\"snapps\",\n\"snappy\",\n\"snaps\",\n\"snapy\",\n\"snare\",\n\"snarer\",\n\"snark\",\n\"snarl\",\n\"snarler\",\n\"snarly\",\n\"snary\",\n\"snaste\",\n\"snatch\",\n\"snatchy\",\n\"snath\",\n\"snathe\",\n\"snavel\",\n\"snavvle\",\n\"snaw\",\n\"snead\",\n\"sneak\",\n\"sneaker\",\n\"sneaky\",\n\"sneap\",\n\"sneath\",\n\"sneathe\",\n\"sneb\",\n\"sneck\",\n\"snecker\",\n\"snecket\",\n\"sned\",\n\"snee\",\n\"sneer\",\n\"sneerer\",\n\"sneery\",\n\"sneesh\",\n\"sneest\",\n\"sneesty\",\n\"sneeze\",\n\"sneezer\",\n\"sneezy\",\n\"snell\",\n\"snelly\",\n\"snerp\",\n\"snew\",\n\"snib\",\n\"snibble\",\n\"snibel\",\n\"snicher\",\n\"snick\",\n\"snicker\",\n\"snicket\",\n\"snickey\",\n\"snickle\",\n\"sniddle\",\n\"snide\",\n\"sniff\",\n\"sniffer\",\n\"sniffle\",\n\"sniffly\",\n\"sniffy\",\n\"snift\",\n\"snifter\",\n\"snifty\",\n\"snig\",\n\"snigger\",\n\"sniggle\",\n\"snip\",\n\"snipe\",\n\"sniper\",\n\"sniping\",\n\"snipish\",\n\"snipper\",\n\"snippet\",\n\"snippy\",\n\"snipy\",\n\"snirl\",\n\"snirt\",\n\"snirtle\",\n\"snitch\",\n\"snite\",\n\"snithe\",\n\"snithy\",\n\"snittle\",\n\"snivel\",\n\"snively\",\n\"snivy\",\n\"snob\",\n\"snobber\",\n\"snobby\",\n\"snobdom\",\n\"snocher\",\n\"snock\",\n\"snocker\",\n\"snod\",\n\"snodly\",\n\"snoek\",\n\"snog\",\n\"snoga\",\n\"snoke\",\n\"snood\",\n\"snooded\",\n\"snook\",\n\"snooker\",\n\"snoop\",\n\"snooper\",\n\"snoopy\",\n\"snoose\",\n\"snoot\",\n\"snooty\",\n\"snoove\",\n\"snooze\",\n\"snoozer\",\n\"snoozle\",\n\"snoozy\",\n\"snop\",\n\"snore\",\n\"snorer\",\n\"snoring\",\n\"snork\",\n\"snorkel\",\n\"snorker\",\n\"snort\",\n\"snorter\",\n\"snortle\",\n\"snorty\",\n\"snot\",\n\"snotter\",\n\"snotty\",\n\"snouch\",\n\"snout\",\n\"snouted\",\n\"snouter\",\n\"snouty\",\n\"snow\",\n\"snowcap\",\n\"snowie\",\n\"snowily\",\n\"snowish\",\n\"snowk\",\n\"snowl\",\n\"snowy\",\n\"snozzle\",\n\"snub\",\n\"snubbed\",\n\"snubbee\",\n\"snubber\",\n\"snubby\",\n\"snuck\",\n\"snudge\",\n\"snuff\",\n\"snuffer\",\n\"snuffle\",\n\"snuffly\",\n\"snuffy\",\n\"snug\",\n\"snugger\",\n\"snuggle\",\n\"snugify\",\n\"snugly\",\n\"snum\",\n\"snup\",\n\"snupper\",\n\"snur\",\n\"snurl\",\n\"snurly\",\n\"snurp\",\n\"snurt\",\n\"snuzzle\",\n\"sny\",\n\"snying\",\n\"so\",\n\"soak\",\n\"soakage\",\n\"soaked\",\n\"soaken\",\n\"soaker\",\n\"soaking\",\n\"soakman\",\n\"soaky\",\n\"soally\",\n\"soam\",\n\"soap\",\n\"soapbox\",\n\"soaper\",\n\"soapery\",\n\"soapily\",\n\"soapsud\",\n\"soapy\",\n\"soar\",\n\"soarer\",\n\"soaring\",\n\"soary\",\n\"sob\",\n\"sobber\",\n\"sobbing\",\n\"sobby\",\n\"sobeit\",\n\"sober\",\n\"soberer\",\n\"soberly\",\n\"sobful\",\n\"soboles\",\n\"soc\",\n\"socage\",\n\"socager\",\n\"soccer\",\n\"soce\",\n\"socht\",\n\"social\",\n\"society\",\n\"socii\",\n\"socius\",\n\"sock\",\n\"socker\",\n\"socket\",\n\"sockeye\",\n\"socky\",\n\"socle\",\n\"socman\",\n\"soco\",\n\"sod\",\n\"soda\",\n\"sodaic\",\n\"sodded\",\n\"sodden\",\n\"sodding\",\n\"soddite\",\n\"soddy\",\n\"sodic\",\n\"sodio\",\n\"sodium\",\n\"sodless\",\n\"sodoku\",\n\"sodomic\",\n\"sodomy\",\n\"sodwork\",\n\"sody\",\n\"soe\",\n\"soekoe\",\n\"soever\",\n\"sofa\",\n\"sofane\",\n\"sofar\",\n\"soffit\",\n\"soft\",\n\"softa\",\n\"soften\",\n\"softish\",\n\"softly\",\n\"softner\",\n\"softy\",\n\"sog\",\n\"soger\",\n\"soget\",\n\"soggily\",\n\"sogging\",\n\"soggy\",\n\"soh\",\n\"soho\",\n\"soil\",\n\"soilage\",\n\"soiled\",\n\"soiling\",\n\"soilure\",\n\"soily\",\n\"soiree\",\n\"soja\",\n\"sojourn\",\n\"sok\",\n\"soka\",\n\"soke\",\n\"sokeman\",\n\"soken\",\n\"sol\",\n\"sola\",\n\"solace\",\n\"solacer\",\n\"solan\",\n\"solanal\",\n\"solanum\",\n\"solar\",\n\"solate\",\n\"solatia\",\n\"solay\",\n\"sold\",\n\"soldado\",\n\"soldan\",\n\"solder\",\n\"soldi\",\n\"soldier\",\n\"soldo\",\n\"sole\",\n\"solea\",\n\"soleas\",\n\"soleil\",\n\"solely\",\n\"solemn\",\n\"solen\",\n\"solent\",\n\"soler\",\n\"soles\",\n\"soleus\",\n\"soleyn\",\n\"soli\",\n\"solicit\",\n\"solid\",\n\"solidi\",\n\"solidly\",\n\"solidum\",\n\"solidus\",\n\"solio\",\n\"soliped\",\n\"solist\",\n\"sollar\",\n\"solo\",\n\"solod\",\n\"solodi\",\n\"soloist\",\n\"solon\",\n\"soloth\",\n\"soluble\",\n\"solubly\",\n\"solum\",\n\"solute\",\n\"solvate\",\n\"solve\",\n\"solvend\",\n\"solvent\",\n\"solver\",\n\"soma\",\n\"somal\",\n\"somata\",\n\"somatic\",\n\"somber\",\n\"sombre\",\n\"some\",\n\"someday\",\n\"somehow\",\n\"someone\",\n\"somers\",\n\"someway\",\n\"somewhy\",\n\"somital\",\n\"somite\",\n\"somitic\",\n\"somma\",\n\"somnial\",\n\"somnify\",\n\"somnus\",\n\"sompay\",\n\"sompne\",\n\"sompner\",\n\"son\",\n\"sonable\",\n\"sonance\",\n\"sonancy\",\n\"sonant\",\n\"sonar\",\n\"sonata\",\n\"sond\",\n\"sondeli\",\n\"soneri\",\n\"song\",\n\"songful\",\n\"songish\",\n\"songle\",\n\"songlet\",\n\"songman\",\n\"songy\",\n\"sonhood\",\n\"sonic\",\n\"soniou\",\n\"sonk\",\n\"sonless\",\n\"sonlike\",\n\"sonly\",\n\"sonnet\",\n\"sonny\",\n\"sonoric\",\n\"sons\",\n\"sonship\",\n\"sonsy\",\n\"sontag\",\n\"soodle\",\n\"soodly\",\n\"sook\",\n\"sooky\",\n\"sool\",\n\"sooloos\",\n\"soon\",\n\"sooner\",\n\"soonish\",\n\"soonly\",\n\"soorawn\",\n\"soord\",\n\"soorkee\",\n\"soot\",\n\"sooter\",\n\"sooth\",\n\"soothe\",\n\"soother\",\n\"sootily\",\n\"sooty\",\n\"sop\",\n\"sope\",\n\"soph\",\n\"sophia\",\n\"sophic\",\n\"sophism\",\n\"sophy\",\n\"sopite\",\n\"sopor\",\n\"sopper\",\n\"sopping\",\n\"soppy\",\n\"soprani\",\n\"soprano\",\n\"sora\",\n\"sorage\",\n\"soral\",\n\"sorb\",\n\"sorbate\",\n\"sorbent\",\n\"sorbic\",\n\"sorbile\",\n\"sorbin\",\n\"sorbite\",\n\"sorbose\",\n\"sorbus\",\n\"sorcer\",\n\"sorcery\",\n\"sorchin\",\n\"sorda\",\n\"sordes\",\n\"sordid\",\n\"sordine\",\n\"sordino\",\n\"sordor\",\n\"sore\",\n\"soredia\",\n\"soree\",\n\"sorehon\",\n\"sorely\",\n\"sorema\",\n\"sorgho\",\n\"sorghum\",\n\"sorgo\",\n\"sori\",\n\"soricid\",\n\"sorite\",\n\"sorites\",\n\"sorn\",\n\"sornare\",\n\"sornari\",\n\"sorner\",\n\"sorning\",\n\"soroban\",\n\"sororal\",\n\"sorose\",\n\"sorosis\",\n\"sorra\",\n\"sorrel\",\n\"sorrily\",\n\"sorroa\",\n\"sorrow\",\n\"sorrowy\",\n\"sorry\",\n\"sort\",\n\"sortal\",\n\"sorted\",\n\"sorter\",\n\"sortie\",\n\"sortly\",\n\"sorty\",\n\"sorus\",\n\"sorva\",\n\"sory\",\n\"sosh\",\n\"soshed\",\n\"soso\",\n\"sosoish\",\n\"soss\",\n\"sossle\",\n\"sot\",\n\"sotie\",\n\"sotnia\",\n\"sotnik\",\n\"sotol\",\n\"sots\",\n\"sottage\",\n\"sotted\",\n\"sotter\",\n\"sottish\",\n\"sou\",\n\"souari\",\n\"soubise\",\n\"soucar\",\n\"souchet\",\n\"souchy\",\n\"soud\",\n\"souffle\",\n\"sough\",\n\"sougher\",\n\"sought\",\n\"soul\",\n\"soulack\",\n\"souled\",\n\"soulful\",\n\"soulish\",\n\"souly\",\n\"soum\",\n\"sound\",\n\"sounder\",\n\"soundly\",\n\"soup\",\n\"soupcon\",\n\"souper\",\n\"souple\",\n\"soupy\",\n\"sour\",\n\"source\",\n\"soured\",\n\"souren\",\n\"sourer\",\n\"souring\",\n\"sourish\",\n\"sourly\",\n\"sourock\",\n\"soursop\",\n\"sourtop\",\n\"soury\",\n\"souse\",\n\"souser\",\n\"souslik\",\n\"soutane\",\n\"souter\",\n\"south\",\n\"souther\",\n\"sov\",\n\"soviet\",\n\"sovite\",\n\"sovkhoz\",\n\"sovran\",\n\"sow\",\n\"sowable\",\n\"sowan\",\n\"sowans\",\n\"sowar\",\n\"sowarry\",\n\"sowback\",\n\"sowbane\",\n\"sowel\",\n\"sowens\",\n\"sower\",\n\"sowfoot\",\n\"sowing\",\n\"sowins\",\n\"sowl\",\n\"sowle\",\n\"sowlike\",\n\"sowlth\",\n\"sown\",\n\"sowse\",\n\"sowt\",\n\"sowte\",\n\"soy\",\n\"soya\",\n\"soybean\",\n\"sozin\",\n\"sozolic\",\n\"sozzle\",\n\"sozzly\",\n\"spa\",\n\"space\",\n\"spaced\",\n\"spacer\",\n\"spacing\",\n\"spack\",\n\"spacy\",\n\"spad\",\n\"spade\",\n\"spaded\",\n\"spader\",\n\"spadger\",\n\"spading\",\n\"spadix\",\n\"spadone\",\n\"spae\",\n\"spaedom\",\n\"spaeman\",\n\"spaer\",\n\"spahi\",\n\"spaid\",\n\"spaik\",\n\"spairge\",\n\"spak\",\n\"spald\",\n\"spalder\",\n\"spale\",\n\"spall\",\n\"spaller\",\n\"spalt\",\n\"span\",\n\"spancel\",\n\"spandle\",\n\"spandy\",\n\"spane\",\n\"spanemy\",\n\"spang\",\n\"spangle\",\n\"spangly\",\n\"spaniel\",\n\"spaning\",\n\"spank\",\n\"spanker\",\n\"spanky\",\n\"spann\",\n\"spannel\",\n\"spanner\",\n\"spanule\",\n\"spar\",\n\"sparada\",\n\"sparch\",\n\"spare\",\n\"sparely\",\n\"sparer\",\n\"sparge\",\n\"sparger\",\n\"sparid\",\n\"sparing\",\n\"spark\",\n\"sparked\",\n\"sparker\",\n\"sparkle\",\n\"sparkly\",\n\"sparks\",\n\"sparky\",\n\"sparm\",\n\"sparoid\",\n\"sparred\",\n\"sparrer\",\n\"sparrow\",\n\"sparry\",\n\"sparse\",\n\"spart\",\n\"sparth\",\n\"spartle\",\n\"sparver\",\n\"spary\",\n\"spasm\",\n\"spasmed\",\n\"spasmic\",\n\"spastic\",\n\"spat\",\n\"spate\",\n\"spatha\",\n\"spathal\",\n\"spathe\",\n\"spathed\",\n\"spathic\",\n\"spatial\",\n\"spatted\",\n\"spatter\",\n\"spattle\",\n\"spatula\",\n\"spatule\",\n\"spave\",\n\"spaver\",\n\"spavie\",\n\"spavied\",\n\"spaviet\",\n\"spavin\",\n\"spawn\",\n\"spawner\",\n\"spawny\",\n\"spay\",\n\"spayad\",\n\"spayard\",\n\"spaying\",\n\"speak\",\n\"speaker\",\n\"speal\",\n\"spean\",\n\"spear\",\n\"spearer\",\n\"speary\",\n\"spec\",\n\"spece\",\n\"special\",\n\"specie\",\n\"species\",\n\"specify\",\n\"speck\",\n\"specked\",\n\"speckle\",\n\"speckly\",\n\"specks\",\n\"specky\",\n\"specs\",\n\"specter\",\n\"spectra\",\n\"spectry\",\n\"specula\",\n\"specus\",\n\"sped\",\n\"speech\",\n\"speed\",\n\"speeder\",\n\"speedy\",\n\"speel\",\n\"speen\",\n\"speer\",\n\"speiss\",\n\"spelder\",\n\"spelk\",\n\"spell\",\n\"speller\",\n\"spelt\",\n\"spelter\",\n\"speltz\",\n\"spelunk\",\n\"spence\",\n\"spencer\",\n\"spend\",\n\"spender\",\n\"spense\",\n\"spent\",\n\"speos\",\n\"sperate\",\n\"sperity\",\n\"sperket\",\n\"sperm\",\n\"sperma\",\n\"spermic\",\n\"spermy\",\n\"sperone\",\n\"spet\",\n\"spetch\",\n\"spew\",\n\"spewer\",\n\"spewing\",\n\"spewy\",\n\"spex\",\n\"sphacel\",\n\"sphecid\",\n\"spheges\",\n\"sphegid\",\n\"sphene\",\n\"sphenic\",\n\"spheral\",\n\"sphere\",\n\"spheric\",\n\"sphery\",\n\"sphinx\",\n\"spica\",\n\"spical\",\n\"spicant\",\n\"spicate\",\n\"spice\",\n\"spiced\",\n\"spicer\",\n\"spicery\",\n\"spicily\",\n\"spicing\",\n\"spick\",\n\"spicket\",\n\"spickle\",\n\"spicose\",\n\"spicous\",\n\"spicula\",\n\"spicule\",\n\"spicy\",\n\"spider\",\n\"spidery\",\n\"spidger\",\n\"spied\",\n\"spiegel\",\n\"spiel\",\n\"spieler\",\n\"spier\",\n\"spiff\",\n\"spiffed\",\n\"spiffy\",\n\"spig\",\n\"spignet\",\n\"spigot\",\n\"spike\",\n\"spiked\",\n\"spiker\",\n\"spikily\",\n\"spiking\",\n\"spiky\",\n\"spile\",\n\"spiler\",\n\"spiling\",\n\"spilite\",\n\"spill\",\n\"spiller\",\n\"spillet\",\n\"spilly\",\n\"spiloma\",\n\"spilt\",\n\"spilth\",\n\"spilus\",\n\"spin\",\n\"spina\",\n\"spinach\",\n\"spinae\",\n\"spinage\",\n\"spinal\",\n\"spinate\",\n\"spinder\",\n\"spindle\",\n\"spindly\",\n\"spine\",\n\"spined\",\n\"spinel\",\n\"spinet\",\n\"spingel\",\n\"spink\",\n\"spinner\",\n\"spinney\",\n\"spinoid\",\n\"spinose\",\n\"spinous\",\n\"spinule\",\n\"spiny\",\n\"spionid\",\n\"spiral\",\n\"spirale\",\n\"spiran\",\n\"spirant\",\n\"spirate\",\n\"spire\",\n\"spirea\",\n\"spired\",\n\"spireme\",\n\"spiring\",\n\"spirit\",\n\"spirity\",\n\"spirket\",\n\"spiro\",\n\"spiroid\",\n\"spirous\",\n\"spirt\",\n\"spiry\",\n\"spise\",\n\"spit\",\n\"spital\",\n\"spitbox\",\n\"spite\",\n\"spitful\",\n\"spitish\",\n\"spitted\",\n\"spitten\",\n\"spitter\",\n\"spittle\",\n\"spitz\",\n\"spiv\",\n\"spivery\",\n\"splash\",\n\"splashy\",\n\"splat\",\n\"splatch\",\n\"splay\",\n\"splayed\",\n\"splayer\",\n\"spleen\",\n\"spleeny\",\n\"spleet\",\n\"splenic\",\n\"splet\",\n\"splice\",\n\"splicer\",\n\"spline\",\n\"splint\",\n\"splinty\",\n\"split\",\n\"splodge\",\n\"splodgy\",\n\"splore\",\n\"splosh\",\n\"splotch\",\n\"splunge\",\n\"splurge\",\n\"splurgy\",\n\"splurt\",\n\"spoach\",\n\"spode\",\n\"spodium\",\n\"spoffle\",\n\"spoffy\",\n\"spogel\",\n\"spoil\",\n\"spoiled\",\n\"spoiler\",\n\"spoilt\",\n\"spoke\",\n\"spoken\",\n\"spoky\",\n\"spole\",\n\"spolia\",\n\"spolium\",\n\"spondee\",\n\"spondyl\",\n\"spong\",\n\"sponge\",\n\"sponged\",\n\"sponger\",\n\"spongin\",\n\"spongy\",\n\"sponsal\",\n\"sponson\",\n\"sponsor\",\n\"spoof\",\n\"spoofer\",\n\"spook\",\n\"spooky\",\n\"spool\",\n\"spooler\",\n\"spoom\",\n\"spoon\",\n\"spooner\",\n\"spoony\",\n\"spoor\",\n\"spoorer\",\n\"spoot\",\n\"spor\",\n\"sporal\",\n\"spore\",\n\"spored\",\n\"sporid\",\n\"sporoid\",\n\"sporont\",\n\"sporous\",\n\"sporran\",\n\"sport\",\n\"sporter\",\n\"sportly\",\n\"sports\",\n\"sporty\",\n\"sporule\",\n\"sposh\",\n\"sposhy\",\n\"spot\",\n\"spotted\",\n\"spotter\",\n\"spottle\",\n\"spotty\",\n\"spousal\",\n\"spouse\",\n\"spousy\",\n\"spout\",\n\"spouter\",\n\"spouty\",\n\"sprack\",\n\"sprad\",\n\"sprag\",\n\"spraich\",\n\"sprain\",\n\"spraint\",\n\"sprang\",\n\"sprank\",\n\"sprat\",\n\"spratty\",\n\"sprawl\",\n\"sprawly\",\n\"spray\",\n\"sprayer\",\n\"sprayey\",\n\"spread\",\n\"spready\",\n\"spreath\",\n\"spree\",\n\"spreeuw\",\n\"spreng\",\n\"sprent\",\n\"spret\",\n\"sprew\",\n\"sprewl\",\n\"spried\",\n\"sprier\",\n\"spriest\",\n\"sprig\",\n\"spriggy\",\n\"spring\",\n\"springe\",\n\"springy\",\n\"sprink\",\n\"sprint\",\n\"sprit\",\n\"sprite\",\n\"spritty\",\n\"sproat\",\n\"sprod\",\n\"sprogue\",\n\"sproil\",\n\"sprong\",\n\"sprose\",\n\"sprout\",\n\"sprowsy\",\n\"spruce\",\n\"sprue\",\n\"spruer\",\n\"sprug\",\n\"spruit\",\n\"sprung\",\n\"sprunny\",\n\"sprunt\",\n\"spry\",\n\"spryly\",\n\"spud\",\n\"spudder\",\n\"spuddle\",\n\"spuddy\",\n\"spuffle\",\n\"spug\",\n\"spuke\",\n\"spume\",\n\"spumone\",\n\"spumose\",\n\"spumous\",\n\"spumy\",\n\"spun\",\n\"spung\",\n\"spunk\",\n\"spunkie\",\n\"spunky\",\n\"spunny\",\n\"spur\",\n\"spurge\",\n\"spuriae\",\n\"spurl\",\n\"spurlet\",\n\"spurn\",\n\"spurner\",\n\"spurred\",\n\"spurrer\",\n\"spurry\",\n\"spurt\",\n\"spurter\",\n\"spurtle\",\n\"spurway\",\n\"sput\",\n\"sputa\",\n\"sputter\",\n\"sputum\",\n\"spy\",\n\"spyboat\",\n\"spydom\",\n\"spyer\",\n\"spyhole\",\n\"spyism\",\n\"spyship\",\n\"squab\",\n\"squabby\",\n\"squacco\",\n\"squad\",\n\"squaddy\",\n\"squail\",\n\"squalid\",\n\"squall\",\n\"squally\",\n\"squalm\",\n\"squalor\",\n\"squam\",\n\"squama\",\n\"squamae\",\n\"squame\",\n\"square\",\n\"squared\",\n\"squarer\",\n\"squark\",\n\"squary\",\n\"squash\",\n\"squashy\",\n\"squat\",\n\"squatly\",\n\"squatty\",\n\"squaw\",\n\"squawk\",\n\"squawky\",\n\"squdge\",\n\"squdgy\",\n\"squeak\",\n\"squeaky\",\n\"squeal\",\n\"squeald\",\n\"squeam\",\n\"squeamy\",\n\"squeege\",\n\"squeeze\",\n\"squeezy\",\n\"squelch\",\n\"squench\",\n\"squib\",\n\"squid\",\n\"squidge\",\n\"squidgy\",\n\"squiffy\",\n\"squilla\",\n\"squin\",\n\"squinch\",\n\"squinny\",\n\"squinsy\",\n\"squint\",\n\"squinty\",\n\"squire\",\n\"squiret\",\n\"squirk\",\n\"squirm\",\n\"squirmy\",\n\"squirr\",\n\"squirt\",\n\"squirty\",\n\"squish\",\n\"squishy\",\n\"squit\",\n\"squitch\",\n\"squoze\",\n\"squush\",\n\"squushy\",\n\"sraddha\",\n\"sramana\",\n\"sri\",\n\"sruti\",\n\"ssu\",\n\"st\",\n\"staab\",\n\"stab\",\n\"stabber\",\n\"stabile\",\n\"stable\",\n\"stabler\",\n\"stably\",\n\"staboy\",\n\"stacher\",\n\"stachys\",\n\"stack\",\n\"stacker\",\n\"stacte\",\n\"stadda\",\n\"staddle\",\n\"stade\",\n\"stadia\",\n\"stadic\",\n\"stadion\",\n\"stadium\",\n\"staff\",\n\"staffed\",\n\"staffer\",\n\"stag\",\n\"stage\",\n\"staged\",\n\"stager\",\n\"stagery\",\n\"stagese\",\n\"stagger\",\n\"staggie\",\n\"staggy\",\n\"stagily\",\n\"staging\",\n\"stagnum\",\n\"stagy\",\n\"staia\",\n\"staid\",\n\"staidly\",\n\"stain\",\n\"stainer\",\n\"staio\",\n\"stair\",\n\"staired\",\n\"stairy\",\n\"staith\",\n\"staiver\",\n\"stake\",\n\"staker\",\n\"stale\",\n\"stalely\",\n\"staling\",\n\"stalk\",\n\"stalked\",\n\"stalker\",\n\"stalko\",\n\"stalky\",\n\"stall\",\n\"stallar\",\n\"staller\",\n\"stam\",\n\"stambha\",\n\"stamen\",\n\"stamin\",\n\"stamina\",\n\"stammel\",\n\"stammer\",\n\"stamnos\",\n\"stamp\",\n\"stampee\",\n\"stamper\",\n\"stample\",\n\"stance\",\n\"stanch\",\n\"stand\",\n\"standee\",\n\"standel\",\n\"stander\",\n\"stane\",\n\"stang\",\n\"stanine\",\n\"stanjen\",\n\"stank\",\n\"stankie\",\n\"stannel\",\n\"stanner\",\n\"stannic\",\n\"stanno\",\n\"stannum\",\n\"stannyl\",\n\"stanza\",\n\"stanze\",\n\"stap\",\n\"stapes\",\n\"staple\",\n\"stapled\",\n\"stapler\",\n\"star\",\n\"starch\",\n\"starchy\",\n\"stardom\",\n\"stare\",\n\"staree\",\n\"starer\",\n\"starets\",\n\"starful\",\n\"staring\",\n\"stark\",\n\"starken\",\n\"starkly\",\n\"starky\",\n\"starlet\",\n\"starlit\",\n\"starn\",\n\"starnel\",\n\"starnie\",\n\"starost\",\n\"starred\",\n\"starry\",\n\"start\",\n\"starter\",\n\"startle\",\n\"startly\",\n\"startor\",\n\"starty\",\n\"starve\",\n\"starved\",\n\"starver\",\n\"starvy\",\n\"stary\",\n\"stases\",\n\"stash\",\n\"stashie\",\n\"stasis\",\n\"statal\",\n\"statant\",\n\"state\",\n\"stated\",\n\"stately\",\n\"stater\",\n\"static\",\n\"statics\",\n\"station\",\n\"statism\",\n\"statist\",\n\"stative\",\n\"stator\",\n\"statue\",\n\"statued\",\n\"stature\",\n\"status\",\n\"statute\",\n\"stauk\",\n\"staumer\",\n\"staun\",\n\"staunch\",\n\"staup\",\n\"stauter\",\n\"stave\",\n\"staver\",\n\"stavers\",\n\"staving\",\n\"staw\",\n\"stawn\",\n\"staxis\",\n\"stay\",\n\"stayed\",\n\"stayer\",\n\"staynil\",\n\"stays\",\n\"stchi\",\n\"stead\",\n\"steady\",\n\"steak\",\n\"steal\",\n\"stealed\",\n\"stealer\",\n\"stealth\",\n\"stealy\",\n\"steam\",\n\"steamer\",\n\"steamy\",\n\"stean\",\n\"stearic\",\n\"stearin\",\n\"stearyl\",\n\"steatin\",\n\"stech\",\n\"steddle\",\n\"steed\",\n\"steek\",\n\"steel\",\n\"steeler\",\n\"steely\",\n\"steen\",\n\"steenth\",\n\"steep\",\n\"steepen\",\n\"steeper\",\n\"steeple\",\n\"steeply\",\n\"steepy\",\n\"steer\",\n\"steerer\",\n\"steeve\",\n\"steever\",\n\"steg\",\n\"steid\",\n\"steigh\",\n\"stein\",\n\"stekan\",\n\"stela\",\n\"stelae\",\n\"stelai\",\n\"stelar\",\n\"stele\",\n\"stell\",\n\"stella\",\n\"stellar\",\n\"stem\",\n\"stema\",\n\"stemlet\",\n\"stemma\",\n\"stemmed\",\n\"stemmer\",\n\"stemmy\",\n\"stemple\",\n\"stemson\",\n\"sten\",\n\"stenar\",\n\"stench\",\n\"stenchy\",\n\"stencil\",\n\"stend\",\n\"steng\",\n\"stengah\",\n\"stenion\",\n\"steno\",\n\"stenog\",\n\"stent\",\n\"stenter\",\n\"stenton\",\n\"step\",\n\"steppe\",\n\"stepped\",\n\"stepper\",\n\"stepson\",\n\"stept\",\n\"stepway\",\n\"stere\",\n\"stereo\",\n\"steri\",\n\"steric\",\n\"sterics\",\n\"steride\",\n\"sterile\",\n\"sterin\",\n\"sterk\",\n\"sterlet\",\n\"stern\",\n\"sterna\",\n\"sternad\",\n\"sternal\",\n\"sterned\",\n\"sternly\",\n\"sternum\",\n\"stero\",\n\"steroid\",\n\"sterol\",\n\"stert\",\n\"stertor\",\n\"sterve\",\n\"stet\",\n\"stetch\",\n\"stevel\",\n\"steven\",\n\"stevia\",\n\"stew\",\n\"steward\",\n\"stewed\",\n\"stewpan\",\n\"stewpot\",\n\"stewy\",\n\"stey\",\n\"sthenia\",\n\"sthenic\",\n\"stib\",\n\"stibial\",\n\"stibic\",\n\"stibine\",\n\"stibium\",\n\"stich\",\n\"stichic\",\n\"stichid\",\n\"stick\",\n\"sticked\",\n\"sticker\",\n\"stickit\",\n\"stickle\",\n\"stickly\",\n\"sticks\",\n\"stickum\",\n\"sticky\",\n\"stid\",\n\"stiddy\",\n\"stife\",\n\"stiff\",\n\"stiffen\",\n\"stiffly\",\n\"stifle\",\n\"stifler\",\n\"stigma\",\n\"stigmai\",\n\"stigmal\",\n\"stigme\",\n\"stile\",\n\"stilet\",\n\"still\",\n\"stiller\",\n\"stilly\",\n\"stilt\",\n\"stilted\",\n\"stilter\",\n\"stilty\",\n\"stim\",\n\"stime\",\n\"stimuli\",\n\"stimy\",\n\"stine\",\n\"sting\",\n\"stinge\",\n\"stinger\",\n\"stingo\",\n\"stingy\",\n\"stink\",\n\"stinker\",\n\"stint\",\n\"stinted\",\n\"stinter\",\n\"stinty\",\n\"stion\",\n\"stionic\",\n\"stipe\",\n\"stiped\",\n\"stipel\",\n\"stipend\",\n\"stipes\",\n\"stippen\",\n\"stipple\",\n\"stipply\",\n\"stipula\",\n\"stipule\",\n\"stir\",\n\"stirk\",\n\"stirp\",\n\"stirps\",\n\"stirra\",\n\"stirrer\",\n\"stirrup\",\n\"stitch\",\n\"stite\",\n\"stith\",\n\"stithy\",\n\"stive\",\n\"stiver\",\n\"stivy\",\n\"stoa\",\n\"stoach\",\n\"stoat\",\n\"stoater\",\n\"stob\",\n\"stocah\",\n\"stock\",\n\"stocker\",\n\"stocks\",\n\"stocky\",\n\"stod\",\n\"stodge\",\n\"stodger\",\n\"stodgy\",\n\"stoep\",\n\"stof\",\n\"stoff\",\n\"stog\",\n\"stoga\",\n\"stogie\",\n\"stogy\",\n\"stoic\",\n\"stoical\",\n\"stoke\",\n\"stoker\",\n\"stola\",\n\"stolae\",\n\"stole\",\n\"stoled\",\n\"stolen\",\n\"stolid\",\n\"stolist\",\n\"stollen\",\n\"stolon\",\n\"stoma\",\n\"stomach\",\n\"stomata\",\n\"stomate\",\n\"stomium\",\n\"stomp\",\n\"stomper\",\n\"stond\",\n\"stone\",\n\"stoned\",\n\"stonen\",\n\"stoner\",\n\"stong\",\n\"stonied\",\n\"stonify\",\n\"stonily\",\n\"stoning\",\n\"stonish\",\n\"stonker\",\n\"stony\",\n\"stood\",\n\"stooded\",\n\"stooden\",\n\"stoof\",\n\"stooge\",\n\"stook\",\n\"stooker\",\n\"stookie\",\n\"stool\",\n\"stoon\",\n\"stoond\",\n\"stoop\",\n\"stooper\",\n\"stoory\",\n\"stoot\",\n\"stop\",\n\"stopa\",\n\"stope\",\n\"stoper\",\n\"stopgap\",\n\"stoping\",\n\"stopped\",\n\"stopper\",\n\"stoppit\",\n\"stopple\",\n\"storage\",\n\"storax\",\n\"store\",\n\"storeen\",\n\"storer\",\n\"storge\",\n\"storied\",\n\"storier\",\n\"storify\",\n\"stork\",\n\"storken\",\n\"storm\",\n\"stormer\",\n\"stormy\",\n\"story\",\n\"stosh\",\n\"stoss\",\n\"stot\",\n\"stotter\",\n\"stoun\",\n\"stound\",\n\"stoup\",\n\"stour\",\n\"stoury\",\n\"stoush\",\n\"stout\",\n\"stouten\",\n\"stouth\",\n\"stoutly\",\n\"stouty\",\n\"stove\",\n\"stoven\",\n\"stover\",\n\"stow\",\n\"stowage\",\n\"stowce\",\n\"stower\",\n\"stowing\",\n\"stra\",\n\"strack\",\n\"stract\",\n\"strad\",\n\"strade\",\n\"stradl\",\n\"stradld\",\n\"strae\",\n\"strafe\",\n\"strafer\",\n\"strag\",\n\"straik\",\n\"strain\",\n\"straint\",\n\"strait\",\n\"strake\",\n\"straked\",\n\"straky\",\n\"stram\",\n\"stramp\",\n\"strand\",\n\"strang\",\n\"strange\",\n\"strany\",\n\"strap\",\n\"strass\",\n\"strata\",\n\"stratal\",\n\"strath\",\n\"strati\",\n\"stratic\",\n\"stratum\",\n\"stratus\",\n\"strave\",\n\"straw\",\n\"strawen\",\n\"strawer\",\n\"strawy\",\n\"stray\",\n\"strayer\",\n\"stre\",\n\"streak\",\n\"streaky\",\n\"stream\",\n\"streamy\",\n\"streck\",\n\"stree\",\n\"streek\",\n\"streel\",\n\"streen\",\n\"streep\",\n\"street\",\n\"streets\",\n\"streite\",\n\"streke\",\n\"stremma\",\n\"streng\",\n\"strent\",\n\"strenth\",\n\"strepen\",\n\"strepor\",\n\"stress\",\n\"stret\",\n\"stretch\",\n\"strette\",\n\"stretti\",\n\"stretto\",\n\"strew\",\n\"strewer\",\n\"strewn\",\n\"strey\",\n\"streyne\",\n\"stria\",\n\"striae\",\n\"strial\",\n\"striate\",\n\"strich\",\n\"striche\",\n\"strick\",\n\"strict\",\n\"strid\",\n\"stride\",\n\"strider\",\n\"stridor\",\n\"strife\",\n\"strig\",\n\"striga\",\n\"strigae\",\n\"strigal\",\n\"stright\",\n\"strigil\",\n\"strike\",\n\"striker\",\n\"strind\",\n\"string\",\n\"stringy\",\n\"striola\",\n\"strip\",\n\"stripe\",\n\"striped\",\n\"striper\",\n\"stript\",\n\"stripy\",\n\"strit\",\n\"strive\",\n\"strived\",\n\"striven\",\n\"striver\",\n\"strix\",\n\"stroam\",\n\"strobic\",\n\"strode\",\n\"stroil\",\n\"stroke\",\n\"stroker\",\n\"stroky\",\n\"strold\",\n\"stroll\",\n\"strolld\",\n\"strom\",\n\"stroma\",\n\"stromal\",\n\"stromb\",\n\"strome\",\n\"strone\",\n\"strong\",\n\"strook\",\n\"stroot\",\n\"strop\",\n\"strophe\",\n\"stroth\",\n\"stroud\",\n\"stroup\",\n\"strove\",\n\"strow\",\n\"strowd\",\n\"strown\",\n\"stroy\",\n\"stroyer\",\n\"strub\",\n\"struck\",\n\"strudel\",\n\"strue\",\n\"strum\",\n\"struma\",\n\"strumae\",\n\"strung\",\n\"strunt\",\n\"strut\",\n\"struth\",\n\"struv\",\n\"strych\",\n\"stub\",\n\"stubb\",\n\"stubbed\",\n\"stubber\",\n\"stubble\",\n\"stubbly\",\n\"stubboy\",\n\"stubby\",\n\"stuber\",\n\"stuboy\",\n\"stucco\",\n\"stuck\",\n\"stud\",\n\"studder\",\n\"studdie\",\n\"studdle\",\n\"stude\",\n\"student\",\n\"studia\",\n\"studied\",\n\"studier\",\n\"studio\",\n\"studium\",\n\"study\",\n\"stue\",\n\"stuff\",\n\"stuffed\",\n\"stuffer\",\n\"stuffy\",\n\"stug\",\n\"stuggy\",\n\"stuiver\",\n\"stull\",\n\"stuller\",\n\"stulm\",\n\"stum\",\n\"stumble\",\n\"stumbly\",\n\"stumer\",\n\"stummer\",\n\"stummy\",\n\"stump\",\n\"stumper\",\n\"stumpy\",\n\"stun\",\n\"stung\",\n\"stunk\",\n\"stunner\",\n\"stunsle\",\n\"stunt\",\n\"stunted\",\n\"stunter\",\n\"stunty\",\n\"stupa\",\n\"stupe\",\n\"stupefy\",\n\"stupend\",\n\"stupent\",\n\"stupex\",\n\"stupid\",\n\"stupor\",\n\"stupose\",\n\"stupp\",\n\"stuprum\",\n\"sturdy\",\n\"sturine\",\n\"sturk\",\n\"sturt\",\n\"sturtan\",\n\"sturtin\",\n\"stuss\",\n\"stut\",\n\"stutter\",\n\"sty\",\n\"styan\",\n\"styca\",\n\"styful\",\n\"stylar\",\n\"stylate\",\n\"style\",\n\"styler\",\n\"stylet\",\n\"styline\",\n\"styling\",\n\"stylish\",\n\"stylist\",\n\"stylite\",\n\"stylize\",\n\"stylo\",\n\"styloid\",\n\"stylops\",\n\"stylus\",\n\"stymie\",\n\"stypsis\",\n\"styptic\",\n\"styrax\",\n\"styrene\",\n\"styrol\",\n\"styrone\",\n\"styryl\",\n\"stythe\",\n\"styward\",\n\"suable\",\n\"suably\",\n\"suade\",\n\"suaharo\",\n\"suant\",\n\"suantly\",\n\"suasion\",\n\"suasive\",\n\"suasory\",\n\"suave\",\n\"suavely\",\n\"suavify\",\n\"suavity\",\n\"sub\",\n\"subacid\",\n\"subact\",\n\"subage\",\n\"subah\",\n\"subaid\",\n\"subanal\",\n\"subarch\",\n\"subarea\",\n\"subatom\",\n\"subaud\",\n\"subband\",\n\"subbank\",\n\"subbase\",\n\"subbass\",\n\"subbeau\",\n\"subbias\",\n\"subbing\",\n\"subcase\",\n\"subcash\",\n\"subcast\",\n\"subcell\",\n\"subcity\",\n\"subclan\",\n\"subcool\",\n\"subdate\",\n\"subdean\",\n\"subdeb\",\n\"subdial\",\n\"subdie\",\n\"subdual\",\n\"subduce\",\n\"subduct\",\n\"subdue\",\n\"subdued\",\n\"subduer\",\n\"subecho\",\n\"subedit\",\n\"suber\",\n\"suberic\",\n\"suberin\",\n\"subface\",\n\"subfeu\",\n\"subfief\",\n\"subfix\",\n\"subform\",\n\"subfusc\",\n\"subfusk\",\n\"subgape\",\n\"subgens\",\n\"subget\",\n\"subgit\",\n\"subgod\",\n\"subgrin\",\n\"subgyre\",\n\"subhall\",\n\"subhead\",\n\"subherd\",\n\"subhero\",\n\"subicle\",\n\"subidar\",\n\"subidea\",\n\"subitem\",\n\"subjack\",\n\"subject\",\n\"subjee\",\n\"subjoin\",\n\"subking\",\n\"sublate\",\n\"sublet\",\n\"sublid\",\n\"sublime\",\n\"sublong\",\n\"sublot\",\n\"submaid\",\n\"submain\",\n\"subman\",\n\"submind\",\n\"submiss\",\n\"submit\",\n\"subnect\",\n\"subness\",\n\"subnex\",\n\"subnote\",\n\"subnude\",\n\"suboral\",\n\"suborn\",\n\"suboval\",\n\"subpart\",\n\"subpass\",\n\"subpial\",\n\"subpimp\",\n\"subplat\",\n\"subplot\",\n\"subplow\",\n\"subpool\",\n\"subport\",\n\"subrace\",\n\"subrent\",\n\"subroot\",\n\"subrule\",\n\"subsale\",\n\"subsalt\",\n\"subsea\",\n\"subsect\",\n\"subsept\",\n\"subset\",\n\"subside\",\n\"subsidy\",\n\"subsill\",\n\"subsist\",\n\"subsoil\",\n\"subsult\",\n\"subsume\",\n\"subtack\",\n\"subtend\",\n\"subtext\",\n\"subtile\",\n\"subtill\",\n\"subtle\",\n\"subtly\",\n\"subtone\",\n\"subtype\",\n\"subunit\",\n\"suburb\",\n\"subvein\",\n\"subvene\",\n\"subvert\",\n\"subvola\",\n\"subway\",\n\"subwink\",\n\"subzone\",\n\"succade\",\n\"succeed\",\n\"succent\",\n\"success\",\n\"succi\",\n\"succin\",\n\"succise\",\n\"succor\",\n\"succory\",\n\"succous\",\n\"succub\",\n\"succuba\",\n\"succube\",\n\"succula\",\n\"succumb\",\n\"succuss\",\n\"such\",\n\"suck\",\n\"suckage\",\n\"sucken\",\n\"sucker\",\n\"sucking\",\n\"suckle\",\n\"suckler\",\n\"suclat\",\n\"sucrate\",\n\"sucre\",\n\"sucrose\",\n\"suction\",\n\"sucuri\",\n\"sucuriu\",\n\"sud\",\n\"sudamen\",\n\"sudary\",\n\"sudate\",\n\"sudd\",\n\"sudden\",\n\"sudder\",\n\"suddle\",\n\"suddy\",\n\"sudoral\",\n\"sudoric\",\n\"suds\",\n\"sudsman\",\n\"sudsy\",\n\"sue\",\n\"suede\",\n\"suer\",\n\"suet\",\n\"suety\",\n\"suff\",\n\"suffect\",\n\"suffer\",\n\"suffete\",\n\"suffice\",\n\"suffix\",\n\"sufflue\",\n\"suffuse\",\n\"sugamo\",\n\"sugan\",\n\"sugar\",\n\"sugared\",\n\"sugarer\",\n\"sugary\",\n\"sugent\",\n\"suggest\",\n\"sugh\",\n\"sugi\",\n\"suguaro\",\n\"suhuaro\",\n\"suicide\",\n\"suid\",\n\"suidian\",\n\"suiform\",\n\"suimate\",\n\"suine\",\n\"suing\",\n\"suingly\",\n\"suint\",\n\"suist\",\n\"suit\",\n\"suite\",\n\"suiting\",\n\"suitor\",\n\"suity\",\n\"suji\",\n\"sulcal\",\n\"sulcar\",\n\"sulcate\",\n\"sulcus\",\n\"suld\",\n\"sulea\",\n\"sulfa\",\n\"sulfato\",\n\"sulfion\",\n\"sulfury\",\n\"sulk\",\n\"sulka\",\n\"sulker\",\n\"sulkily\",\n\"sulky\",\n\"sull\",\n\"sulla\",\n\"sullage\",\n\"sullen\",\n\"sullow\",\n\"sully\",\n\"sulpha\",\n\"sulpho\",\n\"sulphur\",\n\"sultam\",\n\"sultan\",\n\"sultana\",\n\"sultane\",\n\"sultone\",\n\"sultry\",\n\"sulung\",\n\"sum\",\n\"sumac\",\n\"sumatra\",\n\"sumbul\",\n\"sumless\",\n\"summage\",\n\"summand\",\n\"summar\",\n\"summary\",\n\"summate\",\n\"summed\",\n\"summer\",\n\"summery\",\n\"summist\",\n\"summit\",\n\"summity\",\n\"summon\",\n\"summons\",\n\"summula\",\n\"summut\",\n\"sumner\",\n\"sump\",\n\"sumpage\",\n\"sumper\",\n\"sumph\",\n\"sumphy\",\n\"sumpit\",\n\"sumple\",\n\"sumpman\",\n\"sumpter\",\n\"sun\",\n\"sunbeam\",\n\"sunbird\",\n\"sunbow\",\n\"sunburn\",\n\"suncup\",\n\"sundae\",\n\"sundang\",\n\"sundari\",\n\"sundek\",\n\"sunder\",\n\"sundew\",\n\"sundial\",\n\"sundik\",\n\"sundog\",\n\"sundown\",\n\"sundra\",\n\"sundri\",\n\"sundry\",\n\"sune\",\n\"sunfall\",\n\"sunfast\",\n\"sunfish\",\n\"sung\",\n\"sungha\",\n\"sunglo\",\n\"sunglow\",\n\"sunk\",\n\"sunken\",\n\"sunket\",\n\"sunlamp\",\n\"sunland\",\n\"sunless\",\n\"sunlet\",\n\"sunlike\",\n\"sunlit\",\n\"sunn\",\n\"sunnily\",\n\"sunnud\",\n\"sunny\",\n\"sunray\",\n\"sunrise\",\n\"sunroom\",\n\"sunset\",\n\"sunsmit\",\n\"sunspot\",\n\"sunt\",\n\"sunup\",\n\"sunward\",\n\"sunway\",\n\"sunways\",\n\"sunweed\",\n\"sunwise\",\n\"sunyie\",\n\"sup\",\n\"supa\",\n\"supari\",\n\"supawn\",\n\"supe\",\n\"super\",\n\"superb\",\n\"supine\",\n\"supper\",\n\"supping\",\n\"supple\",\n\"supply\",\n\"support\",\n\"suppose\",\n\"suppost\",\n\"supreme\",\n\"sur\",\n\"sura\",\n\"surah\",\n\"surahi\",\n\"sural\",\n\"suranal\",\n\"surat\",\n\"surbase\",\n\"surbate\",\n\"surbed\",\n\"surcoat\",\n\"surcrue\",\n\"surculi\",\n\"surd\",\n\"surdent\",\n\"surdity\",\n\"sure\",\n\"surely\",\n\"sures\",\n\"surette\",\n\"surety\",\n\"surf\",\n\"surface\",\n\"surfacy\",\n\"surfeit\",\n\"surfer\",\n\"surfle\",\n\"surfman\",\n\"surfuse\",\n\"surfy\",\n\"surge\",\n\"surgent\",\n\"surgeon\",\n\"surgery\",\n\"surging\",\n\"surgy\",\n\"suriga\",\n\"surlily\",\n\"surly\",\n\"surma\",\n\"surmark\",\n\"surmise\",\n\"surname\",\n\"surnap\",\n\"surnay\",\n\"surpass\",\n\"surplus\",\n\"surra\",\n\"surrey\",\n\"surtax\",\n\"surtout\",\n\"survey\",\n\"survive\",\n\"suscept\",\n\"susi\",\n\"suslik\",\n\"suspect\",\n\"suspend\",\n\"suspire\",\n\"sustain\",\n\"susu\",\n\"susurr\",\n\"suther\",\n\"sutile\",\n\"sutler\",\n\"sutlery\",\n\"sutor\",\n\"sutra\",\n\"suttee\",\n\"sutten\",\n\"suttin\",\n\"suttle\",\n\"sutural\",\n\"suture\",\n\"suum\",\n\"suwarro\",\n\"suwe\",\n\"suz\",\n\"svelte\",\n\"swa\",\n\"swab\",\n\"swabber\",\n\"swabble\",\n\"swack\",\n\"swacken\",\n\"swad\",\n\"swaddle\",\n\"swaddy\",\n\"swag\",\n\"swage\",\n\"swager\",\n\"swagger\",\n\"swaggie\",\n\"swaggy\",\n\"swagman\",\n\"swain\",\n\"swaird\",\n\"swale\",\n\"swaler\",\n\"swaling\",\n\"swallet\",\n\"swallo\",\n\"swallow\",\n\"swam\",\n\"swami\",\n\"swamp\",\n\"swamper\",\n\"swampy\",\n\"swan\",\n\"swang\",\n\"swangy\",\n\"swank\",\n\"swanker\",\n\"swanky\",\n\"swanner\",\n\"swanny\",\n\"swap\",\n\"swape\",\n\"swapper\",\n\"swaraj\",\n\"swarbie\",\n\"sward\",\n\"swardy\",\n\"sware\",\n\"swarf\",\n\"swarfer\",\n\"swarm\",\n\"swarmer\",\n\"swarmy\",\n\"swarry\",\n\"swart\",\n\"swarth\",\n\"swarthy\",\n\"swartly\",\n\"swarty\",\n\"swarve\",\n\"swash\",\n\"swasher\",\n\"swashy\",\n\"swat\",\n\"swatch\",\n\"swath\",\n\"swathe\",\n\"swather\",\n\"swathy\",\n\"swatter\",\n\"swattle\",\n\"swaver\",\n\"sway\",\n\"swayed\",\n\"swayer\",\n\"swayful\",\n\"swaying\",\n\"sweal\",\n\"swear\",\n\"swearer\",\n\"sweat\",\n\"sweated\",\n\"sweater\",\n\"sweath\",\n\"sweaty\",\n\"swedge\",\n\"sweeny\",\n\"sweep\",\n\"sweeper\",\n\"sweepy\",\n\"sweer\",\n\"sweered\",\n\"sweet\",\n\"sweeten\",\n\"sweetie\",\n\"sweetly\",\n\"sweety\",\n\"swego\",\n\"swell\",\n\"swelled\",\n\"sweller\",\n\"swelly\",\n\"swelp\",\n\"swelt\",\n\"swelter\",\n\"swelth\",\n\"sweltry\",\n\"swelty\",\n\"swep\",\n\"swept\",\n\"swerd\",\n\"swerve\",\n\"swerver\",\n\"swick\",\n\"swidge\",\n\"swift\",\n\"swiften\",\n\"swifter\",\n\"swifty\",\n\"swig\",\n\"swigger\",\n\"swiggle\",\n\"swile\",\n\"swill\",\n\"swiller\",\n\"swim\",\n\"swimmer\",\n\"swimmy\",\n\"swimy\",\n\"swindle\",\n\"swine\",\n\"swinely\",\n\"swinery\",\n\"swiney\",\n\"swing\",\n\"swinge\",\n\"swinger\",\n\"swingle\",\n\"swingy\",\n\"swinish\",\n\"swink\",\n\"swinney\",\n\"swipe\",\n\"swiper\",\n\"swipes\",\n\"swiple\",\n\"swipper\",\n\"swipy\",\n\"swird\",\n\"swire\",\n\"swirl\",\n\"swirly\",\n\"swish\",\n\"swisher\",\n\"swishy\",\n\"swiss\",\n\"switch\",\n\"switchy\",\n\"swith\",\n\"swithe\",\n\"swithen\",\n\"swither\",\n\"swivel\",\n\"swivet\",\n\"swiz\",\n\"swizzle\",\n\"swob\",\n\"swollen\",\n\"swom\",\n\"swonken\",\n\"swoon\",\n\"swooned\",\n\"swoony\",\n\"swoop\",\n\"swooper\",\n\"swoosh\",\n\"sword\",\n\"swore\",\n\"sworn\",\n\"swosh\",\n\"swot\",\n\"swotter\",\n\"swounds\",\n\"swow\",\n\"swum\",\n\"swung\",\n\"swungen\",\n\"swure\",\n\"syagush\",\n\"sybotic\",\n\"syce\",\n\"sycee\",\n\"sycock\",\n\"sycoma\",\n\"syconid\",\n\"syconus\",\n\"sycosis\",\n\"sye\",\n\"syenite\",\n\"sylid\",\n\"syllab\",\n\"syllabe\",\n\"syllabi\",\n\"sylloge\",\n\"sylph\",\n\"sylphic\",\n\"sylphid\",\n\"sylphy\",\n\"sylva\",\n\"sylvae\",\n\"sylvage\",\n\"sylvan\",\n\"sylvate\",\n\"sylvic\",\n\"sylvine\",\n\"sylvite\",\n\"symbion\",\n\"symbiot\",\n\"symbol\",\n\"sympode\",\n\"symptom\",\n\"synacme\",\n\"synacmy\",\n\"synange\",\n\"synapse\",\n\"synapte\",\n\"synaxar\",\n\"synaxis\",\n\"sync\",\n\"syncarp\",\n\"synch\",\n\"synchro\",\n\"syncope\",\n\"syndic\",\n\"syndoc\",\n\"syne\",\n\"synema\",\n\"synergy\",\n\"synesis\",\n\"syngamy\",\n\"synod\",\n\"synodal\",\n\"synoecy\",\n\"synonym\",\n\"synopsy\",\n\"synovia\",\n\"syntan\",\n\"syntax\",\n\"synthol\",\n\"syntomy\",\n\"syntone\",\n\"syntony\",\n\"syntype\",\n\"synusia\",\n\"sypher\",\n\"syre\",\n\"syringa\",\n\"syringe\",\n\"syrinx\",\n\"syrma\",\n\"syrphid\",\n\"syrt\",\n\"syrtic\",\n\"syrup\",\n\"syruped\",\n\"syruper\",\n\"syrupy\",\n\"syssel\",\n\"system\",\n\"systole\",\n\"systyle\",\n\"syzygy\",\n\"t\",\n\"ta\",\n\"taa\",\n\"taar\",\n\"tab\",\n\"tabacin\",\n\"tabacum\",\n\"tabanid\",\n\"tabard\",\n\"tabaret\",\n\"tabaxir\",\n\"tabber\",\n\"tabby\",\n\"tabefy\",\n\"tabella\",\n\"taberna\",\n\"tabes\",\n\"tabet\",\n\"tabetic\",\n\"tabic\",\n\"tabid\",\n\"tabidly\",\n\"tabific\",\n\"tabinet\",\n\"tabla\",\n\"table\",\n\"tableau\",\n\"tabled\",\n\"tabler\",\n\"tables\",\n\"tablet\",\n\"tabling\",\n\"tabloid\",\n\"tabog\",\n\"taboo\",\n\"taboot\",\n\"tabor\",\n\"taborer\",\n\"taboret\",\n\"taborin\",\n\"tabour\",\n\"tabret\",\n\"tabu\",\n\"tabula\",\n\"tabular\",\n\"tabule\",\n\"tabut\",\n\"taccada\",\n\"tach\",\n\"tache\",\n\"tachiol\",\n\"tacit\",\n\"tacitly\",\n\"tack\",\n\"tacker\",\n\"tacket\",\n\"tackety\",\n\"tackey\",\n\"tacking\",\n\"tackle\",\n\"tackled\",\n\"tackler\",\n\"tacky\",\n\"tacnode\",\n\"tacso\",\n\"tact\",\n\"tactful\",\n\"tactic\",\n\"tactics\",\n\"tactile\",\n\"taction\",\n\"tactite\",\n\"tactive\",\n\"tactor\",\n\"tactual\",\n\"tactus\",\n\"tad\",\n\"tade\",\n\"tadpole\",\n\"tae\",\n\"tael\",\n\"taen\",\n\"taenia\",\n\"taenial\",\n\"taenian\",\n\"taenite\",\n\"taennin\",\n\"taffeta\",\n\"taffety\",\n\"taffle\",\n\"taffy\",\n\"tafia\",\n\"taft\",\n\"tafwiz\",\n\"tag\",\n\"tagetol\",\n\"tagged\",\n\"tagger\",\n\"taggle\",\n\"taggy\",\n\"taglet\",\n\"taglike\",\n\"taglock\",\n\"tagrag\",\n\"tagsore\",\n\"tagtail\",\n\"tagua\",\n\"taguan\",\n\"tagwerk\",\n\"taha\",\n\"taheen\",\n\"tahil\",\n\"tahin\",\n\"tahr\",\n\"tahsil\",\n\"tahua\",\n\"tai\",\n\"taiaha\",\n\"taich\",\n\"taiga\",\n\"taigle\",\n\"taihoa\",\n\"tail\",\n\"tailage\",\n\"tailed\",\n\"tailer\",\n\"tailet\",\n\"tailge\",\n\"tailing\",\n\"taille\",\n\"taillie\",\n\"tailor\",\n\"tailory\",\n\"tailpin\",\n\"taily\",\n\"tailzee\",\n\"tailzie\",\n\"taimen\",\n\"tain\",\n\"taint\",\n\"taintor\",\n\"taipan\",\n\"taipo\",\n\"tairge\",\n\"tairger\",\n\"tairn\",\n\"taisch\",\n\"taise\",\n\"taissle\",\n\"tait\",\n\"taiver\",\n\"taivers\",\n\"taivert\",\n\"taj\",\n\"takable\",\n\"takar\",\n\"take\",\n\"takeful\",\n\"taken\",\n\"taker\",\n\"takin\",\n\"taking\",\n\"takings\",\n\"takosis\",\n\"takt\",\n\"taky\",\n\"takyr\",\n\"tal\",\n\"tala\",\n\"talabon\",\n\"talahib\",\n\"talaje\",\n\"talak\",\n\"talao\",\n\"talar\",\n\"talari\",\n\"talaria\",\n\"talaric\",\n\"talayot\",\n\"talbot\",\n\"talc\",\n\"talcer\",\n\"talcky\",\n\"talcoid\",\n\"talcose\",\n\"talcous\",\n\"talcum\",\n\"tald\",\n\"tale\",\n\"taled\",\n\"taleful\",\n\"talent\",\n\"taler\",\n\"tales\",\n\"tali\",\n\"taliage\",\n\"taliera\",\n\"talion\",\n\"talipat\",\n\"taliped\",\n\"talipes\",\n\"talipot\",\n\"talis\",\n\"talisay\",\n\"talite\",\n\"talitol\",\n\"talk\",\n\"talker\",\n\"talkful\",\n\"talkie\",\n\"talking\",\n\"talky\",\n\"tall\",\n\"tallage\",\n\"tallboy\",\n\"taller\",\n\"tallero\",\n\"talles\",\n\"tallet\",\n\"talliar\",\n\"tallier\",\n\"tallis\",\n\"tallish\",\n\"tallit\",\n\"tallith\",\n\"talloel\",\n\"tallote\",\n\"tallow\",\n\"tallowy\",\n\"tally\",\n\"tallyho\",\n\"talma\",\n\"talon\",\n\"taloned\",\n\"talonic\",\n\"talonid\",\n\"talose\",\n\"talpid\",\n\"talpify\",\n\"talpine\",\n\"talpoid\",\n\"talthib\",\n\"taluk\",\n\"taluka\",\n\"talus\",\n\"taluto\",\n\"talwar\",\n\"talwood\",\n\"tam\",\n\"tamable\",\n\"tamably\",\n\"tamale\",\n\"tamandu\",\n\"tamanu\",\n\"tamara\",\n\"tamarao\",\n\"tamarin\",\n\"tamas\",\n\"tamasha\",\n\"tambac\",\n\"tamber\",\n\"tambo\",\n\"tamboo\",\n\"tambor\",\n\"tambour\",\n\"tame\",\n\"tamein\",\n\"tamely\",\n\"tamer\",\n\"tamis\",\n\"tamise\",\n\"tamlung\",\n\"tammie\",\n\"tammock\",\n\"tammy\",\n\"tamp\",\n\"tampala\",\n\"tampan\",\n\"tampang\",\n\"tamper\",\n\"tampin\",\n\"tamping\",\n\"tampion\",\n\"tampon\",\n\"tampoon\",\n\"tan\",\n\"tana\",\n\"tanach\",\n\"tanager\",\n\"tanaist\",\n\"tanak\",\n\"tanan\",\n\"tanbark\",\n\"tanbur\",\n\"tancel\",\n\"tandan\",\n\"tandem\",\n\"tandle\",\n\"tandour\",\n\"tane\",\n\"tang\",\n\"tanga\",\n\"tanged\",\n\"tangelo\",\n\"tangent\",\n\"tanger\",\n\"tangham\",\n\"tanghan\",\n\"tanghin\",\n\"tangi\",\n\"tangie\",\n\"tangka\",\n\"tanglad\",\n\"tangle\",\n\"tangler\",\n\"tangly\",\n\"tango\",\n\"tangram\",\n\"tangs\",\n\"tangue\",\n\"tangum\",\n\"tangun\",\n\"tangy\",\n\"tanh\",\n\"tanha\",\n\"tania\",\n\"tanica\",\n\"tanier\",\n\"tanist\",\n\"tanjib\",\n\"tanjong\",\n\"tank\",\n\"tanka\",\n\"tankage\",\n\"tankah\",\n\"tankard\",\n\"tanked\",\n\"tanker\",\n\"tankert\",\n\"tankful\",\n\"tankle\",\n\"tankman\",\n\"tanling\",\n\"tannage\",\n\"tannaic\",\n\"tannaim\",\n\"tannase\",\n\"tannate\",\n\"tanned\",\n\"tanner\",\n\"tannery\",\n\"tannic\",\n\"tannide\",\n\"tannin\",\n\"tanning\",\n\"tannoid\",\n\"tannyl\",\n\"tanoa\",\n\"tanquam\",\n\"tanquen\",\n\"tanrec\",\n\"tansy\",\n\"tantara\",\n\"tanti\",\n\"tantivy\",\n\"tantle\",\n\"tantra\",\n\"tantric\",\n\"tantrik\",\n\"tantrum\",\n\"tantum\",\n\"tanwood\",\n\"tanyard\",\n\"tanzeb\",\n\"tanzib\",\n\"tanzy\",\n\"tao\",\n\"taotai\",\n\"taoyin\",\n\"tap\",\n\"tapa\",\n\"tapalo\",\n\"tapas\",\n\"tapasvi\",\n\"tape\",\n\"tapeman\",\n\"tapen\",\n\"taper\",\n\"tapered\",\n\"taperer\",\n\"taperly\",\n\"tapet\",\n\"tapetal\",\n\"tapete\",\n\"tapeti\",\n\"tapetum\",\n\"taphole\",\n\"tapia\",\n\"tapioca\",\n\"tapir\",\n\"tapis\",\n\"tapism\",\n\"tapist\",\n\"taplash\",\n\"taplet\",\n\"tapmost\",\n\"tapnet\",\n\"tapoa\",\n\"tapoun\",\n\"tappa\",\n\"tappall\",\n\"tappaul\",\n\"tappen\",\n\"tapper\",\n\"tappet\",\n\"tapping\",\n\"tappoon\",\n\"taproom\",\n\"taproot\",\n\"taps\",\n\"tapster\",\n\"tapu\",\n\"tapul\",\n\"taqua\",\n\"tar\",\n\"tara\",\n\"taraf\",\n\"tarage\",\n\"tarairi\",\n\"tarand\",\n\"taraph\",\n\"tarapin\",\n\"tarata\",\n\"taratah\",\n\"tarau\",\n\"tarbet\",\n\"tarboy\",\n\"tarbush\",\n\"tardily\",\n\"tardive\",\n\"tardle\",\n\"tardy\",\n\"tare\",\n\"tarea\",\n\"tarefa\",\n\"tarente\",\n\"tarfa\",\n\"targe\",\n\"targer\",\n\"target\",\n\"tarhood\",\n\"tari\",\n\"tarie\",\n\"tariff\",\n\"tarin\",\n\"tariric\",\n\"tarish\",\n\"tarkhan\",\n\"tarlike\",\n\"tarmac\",\n\"tarman\",\n\"tarn\",\n\"tarnal\",\n\"tarnish\",\n\"taro\",\n\"taroc\",\n\"tarocco\",\n\"tarok\",\n\"tarot\",\n\"tarp\",\n\"tarpan\",\n\"tarpon\",\n\"tarpot\",\n\"tarpum\",\n\"tarr\",\n\"tarrack\",\n\"tarras\",\n\"tarrass\",\n\"tarred\",\n\"tarrer\",\n\"tarri\",\n\"tarrie\",\n\"tarrier\",\n\"tarrify\",\n\"tarrily\",\n\"tarrish\",\n\"tarrock\",\n\"tarrow\",\n\"tarry\",\n\"tars\",\n\"tarsal\",\n\"tarsale\",\n\"tarse\",\n\"tarsi\",\n\"tarsia\",\n\"tarsier\",\n\"tarsome\",\n\"tarsus\",\n\"tart\",\n\"tartago\",\n\"tartan\",\n\"tartana\",\n\"tartane\",\n\"tartar\",\n\"tarten\",\n\"tartish\",\n\"tartle\",\n\"tartlet\",\n\"tartly\",\n\"tartro\",\n\"tartryl\",\n\"tarve\",\n\"tarweed\",\n\"tarwood\",\n\"taryard\",\n\"tasajo\",\n\"tascal\",\n\"tasco\",\n\"tash\",\n\"tashie\",\n\"tashlik\",\n\"tashrif\",\n\"task\",\n\"taskage\",\n\"tasker\",\n\"taskit\",\n\"taslet\",\n\"tass\",\n\"tassago\",\n\"tassah\",\n\"tassal\",\n\"tassard\",\n\"tasse\",\n\"tassel\",\n\"tassely\",\n\"tasser\",\n\"tasset\",\n\"tassie\",\n\"tassoo\",\n\"taste\",\n\"tasted\",\n\"tasten\",\n\"taster\",\n\"tastily\",\n\"tasting\",\n\"tasty\",\n\"tasu\",\n\"tat\",\n\"tataupa\",\n\"tatbeb\",\n\"tatchy\",\n\"tate\",\n\"tater\",\n\"tath\",\n\"tatie\",\n\"tatinek\",\n\"tatler\",\n\"tatou\",\n\"tatouay\",\n\"tatsman\",\n\"tatta\",\n\"tatter\",\n\"tattery\",\n\"tatther\",\n\"tattied\",\n\"tatting\",\n\"tattle\",\n\"tattler\",\n\"tattoo\",\n\"tattva\",\n\"tatty\",\n\"tatu\",\n\"tau\",\n\"taught\",\n\"taula\",\n\"taum\",\n\"taun\",\n\"taunt\",\n\"taunter\",\n\"taupe\",\n\"taupo\",\n\"taupou\",\n\"taur\",\n\"taurean\",\n\"taurian\",\n\"tauric\",\n\"taurine\",\n\"taurite\",\n\"tauryl\",\n\"taut\",\n\"tautaug\",\n\"tauted\",\n\"tauten\",\n\"tautit\",\n\"tautly\",\n\"tautog\",\n\"tav\",\n\"tave\",\n\"tavell\",\n\"taver\",\n\"tavern\",\n\"tavers\",\n\"tavert\",\n\"tavola\",\n\"taw\",\n\"tawa\",\n\"tawdry\",\n\"tawer\",\n\"tawery\",\n\"tawie\",\n\"tawite\",\n\"tawkee\",\n\"tawkin\",\n\"tawn\",\n\"tawney\",\n\"tawnily\",\n\"tawnle\",\n\"tawny\",\n\"tawpi\",\n\"tawpie\",\n\"taws\",\n\"tawse\",\n\"tawtie\",\n\"tax\",\n\"taxable\",\n\"taxably\",\n\"taxator\",\n\"taxed\",\n\"taxeme\",\n\"taxemic\",\n\"taxer\",\n\"taxi\",\n\"taxibus\",\n\"taxicab\",\n\"taximan\",\n\"taxine\",\n\"taxing\",\n\"taxis\",\n\"taxite\",\n\"taxitic\",\n\"taxless\",\n\"taxman\",\n\"taxon\",\n\"taxor\",\n\"taxpaid\",\n\"taxwax\",\n\"taxy\",\n\"tay\",\n\"tayer\",\n\"tayir\",\n\"tayra\",\n\"taysaam\",\n\"tazia\",\n\"tch\",\n\"tchai\",\n\"tcharik\",\n\"tchast\",\n\"tche\",\n\"tchick\",\n\"tchu\",\n\"tck\",\n\"te\",\n\"tea\",\n\"teabox\",\n\"teaboy\",\n\"teacake\",\n\"teacart\",\n\"teach\",\n\"teache\",\n\"teacher\",\n\"teachy\",\n\"teacup\",\n\"tead\",\n\"teadish\",\n\"teaer\",\n\"teaey\",\n\"teagle\",\n\"teaish\",\n\"teaism\",\n\"teak\",\n\"teal\",\n\"tealery\",\n\"tealess\",\n\"team\",\n\"teaman\",\n\"teameo\",\n\"teamer\",\n\"teaming\",\n\"teamman\",\n\"tean\",\n\"teanal\",\n\"teap\",\n\"teapot\",\n\"teapoy\",\n\"tear\",\n\"tearage\",\n\"tearcat\",\n\"tearer\",\n\"tearful\",\n\"tearing\",\n\"tearlet\",\n\"tearoom\",\n\"tearpit\",\n\"teart\",\n\"teary\",\n\"tease\",\n\"teasel\",\n\"teaser\",\n\"teashop\",\n\"teasing\",\n\"teasler\",\n\"teasy\",\n\"teat\",\n\"teated\",\n\"teathe\",\n\"teather\",\n\"teatime\",\n\"teatman\",\n\"teaty\",\n\"teave\",\n\"teaware\",\n\"teaze\",\n\"teazer\",\n\"tebbet\",\n\"tec\",\n\"teca\",\n\"tecali\",\n\"tech\",\n\"techily\",\n\"technic\",\n\"techous\",\n\"techy\",\n\"teck\",\n\"tecomin\",\n\"tecon\",\n\"tectal\",\n\"tectum\",\n\"tecum\",\n\"tecuma\",\n\"ted\",\n\"tedder\",\n\"tedge\",\n\"tedious\",\n\"tedium\",\n\"tee\",\n\"teedle\",\n\"teel\",\n\"teem\",\n\"teemer\",\n\"teemful\",\n\"teeming\",\n\"teems\",\n\"teen\",\n\"teenage\",\n\"teenet\",\n\"teens\",\n\"teensy\",\n\"teenty\",\n\"teeny\",\n\"teer\",\n\"teerer\",\n\"teest\",\n\"teet\",\n\"teetan\",\n\"teeter\",\n\"teeth\",\n\"teethe\",\n\"teethy\",\n\"teeting\",\n\"teety\",\n\"teevee\",\n\"teff\",\n\"teg\",\n\"tegmen\",\n\"tegmina\",\n\"tegua\",\n\"tegula\",\n\"tegular\",\n\"tegumen\",\n\"tehseel\",\n\"tehsil\",\n\"teicher\",\n\"teil\",\n\"teind\",\n\"teinder\",\n\"teioid\",\n\"tejon\",\n\"teju\",\n\"tekiah\",\n\"tekke\",\n\"tekken\",\n\"tektite\",\n\"tekya\",\n\"telamon\",\n\"telang\",\n\"telar\",\n\"telary\",\n\"tele\",\n\"teledu\",\n\"telega\",\n\"teleost\",\n\"teleran\",\n\"telergy\",\n\"telesia\",\n\"telesis\",\n\"teleuto\",\n\"televox\",\n\"telfer\",\n\"telford\",\n\"teli\",\n\"telial\",\n\"telic\",\n\"telical\",\n\"telium\",\n\"tell\",\n\"tellach\",\n\"tellee\",\n\"teller\",\n\"telling\",\n\"tellt\",\n\"telome\",\n\"telomic\",\n\"telpath\",\n\"telpher\",\n\"telson\",\n\"telt\",\n\"telurgy\",\n\"telyn\",\n\"temacha\",\n\"teman\",\n\"tembe\",\n\"temblor\",\n\"temenos\",\n\"temiak\",\n\"temin\",\n\"temp\",\n\"temper\",\n\"tempera\",\n\"tempery\",\n\"tempest\",\n\"tempi\",\n\"templar\",\n\"temple\",\n\"templed\",\n\"templet\",\n\"tempo\",\n\"tempora\",\n\"tempre\",\n\"tempt\",\n\"tempter\",\n\"temse\",\n\"temser\",\n\"ten\",\n\"tenable\",\n\"tenably\",\n\"tenace\",\n\"tenai\",\n\"tenancy\",\n\"tenant\",\n\"tench\",\n\"tend\",\n\"tendant\",\n\"tendent\",\n\"tender\",\n\"tending\",\n\"tendon\",\n\"tendour\",\n\"tendril\",\n\"tendron\",\n\"tenebra\",\n\"tenent\",\n\"teneral\",\n\"tenet\",\n\"tenfold\",\n\"teng\",\n\"tengere\",\n\"tengu\",\n\"tenible\",\n\"tenio\",\n\"tenline\",\n\"tenne\",\n\"tenner\",\n\"tennis\",\n\"tennisy\",\n\"tenon\",\n\"tenoner\",\n\"tenor\",\n\"tenpin\",\n\"tenrec\",\n\"tense\",\n\"tensely\",\n\"tensify\",\n\"tensile\",\n\"tension\",\n\"tensity\",\n\"tensive\",\n\"tenson\",\n\"tensor\",\n\"tent\",\n\"tentage\",\n\"tented\",\n\"tenter\",\n\"tentful\",\n\"tenth\",\n\"tenthly\",\n\"tentigo\",\n\"tention\",\n\"tentlet\",\n\"tenture\",\n\"tenty\",\n\"tenuate\",\n\"tenues\",\n\"tenuis\",\n\"tenuity\",\n\"tenuous\",\n\"tenure\",\n\"teopan\",\n\"tepache\",\n\"tepal\",\n\"tepee\",\n\"tepefy\",\n\"tepid\",\n\"tepidly\",\n\"tepor\",\n\"tequila\",\n\"tera\",\n\"terap\",\n\"teras\",\n\"terbia\",\n\"terbic\",\n\"terbium\",\n\"tercel\",\n\"tercer\",\n\"tercet\",\n\"tercia\",\n\"tercine\",\n\"tercio\",\n\"terebic\",\n\"terebra\",\n\"teredo\",\n\"terek\",\n\"terete\",\n\"tereu\",\n\"terfez\",\n\"tergal\",\n\"tergant\",\n\"tergite\",\n\"tergum\",\n\"term\",\n\"terma\",\n\"termage\",\n\"termen\",\n\"termer\",\n\"termin\",\n\"termine\",\n\"termini\",\n\"termino\",\n\"termite\",\n\"termly\",\n\"termon\",\n\"termor\",\n\"tern\",\n\"terna\",\n\"ternal\",\n\"ternar\",\n\"ternary\",\n\"ternate\",\n\"terne\",\n\"ternery\",\n\"ternion\",\n\"ternize\",\n\"ternlet\",\n\"terp\",\n\"terpane\",\n\"terpene\",\n\"terpin\",\n\"terpine\",\n\"terrace\",\n\"terrage\",\n\"terrain\",\n\"terral\",\n\"terrane\",\n\"terrar\",\n\"terrene\",\n\"terret\",\n\"terrier\",\n\"terrify\",\n\"terrine\",\n\"terron\",\n\"terror\",\n\"terry\",\n\"terse\",\n\"tersely\",\n\"tersion\",\n\"tertia\",\n\"tertial\",\n\"tertian\",\n\"tertius\",\n\"terton\",\n\"tervee\",\n\"terzina\",\n\"terzo\",\n\"tesack\",\n\"teskere\",\n\"tessara\",\n\"tessel\",\n\"tessera\",\n\"test\",\n\"testa\",\n\"testacy\",\n\"testar\",\n\"testata\",\n\"testate\",\n\"teste\",\n\"tested\",\n\"testee\",\n\"tester\",\n\"testes\",\n\"testify\",\n\"testily\",\n\"testing\",\n\"testis\",\n\"teston\",\n\"testone\",\n\"testoon\",\n\"testor\",\n\"testril\",\n\"testudo\",\n\"testy\",\n\"tetanic\",\n\"tetanus\",\n\"tetany\",\n\"tetard\",\n\"tetch\",\n\"tetchy\",\n\"tete\",\n\"tetel\",\n\"teth\",\n\"tether\",\n\"tethery\",\n\"tetra\",\n\"tetract\",\n\"tetrad\",\n\"tetrane\",\n\"tetrazo\",\n\"tetric\",\n\"tetrode\",\n\"tetrole\",\n\"tetrose\",\n\"tetryl\",\n\"tetter\",\n\"tettery\",\n\"tettix\",\n\"teucrin\",\n\"teufit\",\n\"teuk\",\n\"teviss\",\n\"tew\",\n\"tewel\",\n\"tewer\",\n\"tewit\",\n\"tewly\",\n\"tewsome\",\n\"text\",\n\"textile\",\n\"textlet\",\n\"textman\",\n\"textual\",\n\"texture\",\n\"tez\",\n\"tezkere\",\n\"th\",\n\"tha\",\n\"thack\",\n\"thacker\",\n\"thakur\",\n\"thalami\",\n\"thaler\",\n\"thalli\",\n\"thallic\",\n\"thallus\",\n\"thameng\",\n\"than\",\n\"thana\",\n\"thanage\",\n\"thanan\",\n\"thane\",\n\"thank\",\n\"thankee\",\n\"thanker\",\n\"thanks\",\n\"thapes\",\n\"thapsia\",\n\"thar\",\n\"tharf\",\n\"tharm\",\n\"that\",\n\"thatch\",\n\"thatchy\",\n\"thatn\",\n\"thats\",\n\"thaught\",\n\"thave\",\n\"thaw\",\n\"thawer\",\n\"thawn\",\n\"thawy\",\n\"the\",\n\"theah\",\n\"theasum\",\n\"theat\",\n\"theater\",\n\"theatry\",\n\"theave\",\n\"theb\",\n\"theca\",\n\"thecae\",\n\"thecal\",\n\"thecate\",\n\"thecia\",\n\"thecium\",\n\"thecla\",\n\"theclan\",\n\"thecoid\",\n\"thee\",\n\"theek\",\n\"theeker\",\n\"theelin\",\n\"theelol\",\n\"theer\",\n\"theet\",\n\"theezan\",\n\"theft\",\n\"thegn\",\n\"thegnly\",\n\"theine\",\n\"their\",\n\"theirn\",\n\"theirs\",\n\"theism\",\n\"theist\",\n\"thelium\",\n\"them\",\n\"thema\",\n\"themata\",\n\"theme\",\n\"themer\",\n\"themis\",\n\"themsel\",\n\"then\",\n\"thenal\",\n\"thenar\",\n\"thence\",\n\"theody\",\n\"theorbo\",\n\"theorem\",\n\"theoria\",\n\"theoric\",\n\"theorum\",\n\"theory\",\n\"theow\",\n\"therapy\",\n\"there\",\n\"thereas\",\n\"thereat\",\n\"thereby\",\n\"therein\",\n\"thereof\",\n\"thereon\",\n\"theres\",\n\"therese\",\n\"thereto\",\n\"thereup\",\n\"theriac\",\n\"therial\",\n\"therm\",\n\"thermae\",\n\"thermal\",\n\"thermic\",\n\"thermit\",\n\"thermo\",\n\"thermos\",\n\"theroid\",\n\"these\",\n\"theses\",\n\"thesial\",\n\"thesis\",\n\"theta\",\n\"thetch\",\n\"thetic\",\n\"thetics\",\n\"thetin\",\n\"thetine\",\n\"theurgy\",\n\"thew\",\n\"thewed\",\n\"thewy\",\n\"they\",\n\"theyll\",\n\"theyre\",\n\"thiamin\",\n\"thiasi\",\n\"thiasoi\",\n\"thiasos\",\n\"thiasus\",\n\"thick\",\n\"thicken\",\n\"thicket\",\n\"thickly\",\n\"thief\",\n\"thienyl\",\n\"thieve\",\n\"thiever\",\n\"thig\",\n\"thigger\",\n\"thigh\",\n\"thighed\",\n\"thight\",\n\"thilk\",\n\"thill\",\n\"thiller\",\n\"thilly\",\n\"thimber\",\n\"thimble\",\n\"thin\",\n\"thine\",\n\"thing\",\n\"thingal\",\n\"thingly\",\n\"thingum\",\n\"thingy\",\n\"think\",\n\"thinker\",\n\"thinly\",\n\"thinner\",\n\"thio\",\n\"thiol\",\n\"thiolic\",\n\"thionic\",\n\"thionyl\",\n\"thir\",\n\"third\",\n\"thirdly\",\n\"thirl\",\n\"thirst\",\n\"thirsty\",\n\"thirt\",\n\"thirty\",\n\"this\",\n\"thishow\",\n\"thisn\",\n\"thissen\",\n\"thistle\",\n\"thistly\",\n\"thither\",\n\"thiuram\",\n\"thivel\",\n\"thixle\",\n\"tho\",\n\"thob\",\n\"thocht\",\n\"thof\",\n\"thoft\",\n\"thoke\",\n\"thokish\",\n\"thole\",\n\"tholi\",\n\"tholoi\",\n\"tholos\",\n\"tholus\",\n\"thon\",\n\"thonder\",\n\"thone\",\n\"thong\",\n\"thonged\",\n\"thongy\",\n\"thoo\",\n\"thooid\",\n\"thoom\",\n\"thoral\",\n\"thorax\",\n\"thore\",\n\"thoria\",\n\"thoric\",\n\"thorina\",\n\"thorite\",\n\"thorium\",\n\"thorn\",\n\"thorned\",\n\"thornen\",\n\"thorny\",\n\"thoro\",\n\"thoron\",\n\"thorp\",\n\"thort\",\n\"thorter\",\n\"those\",\n\"thou\",\n\"though\",\n\"thought\",\n\"thouse\",\n\"thow\",\n\"thowel\",\n\"thowt\",\n\"thrack\",\n\"thraep\",\n\"thrail\",\n\"thrain\",\n\"thrall\",\n\"thram\",\n\"thrang\",\n\"thrap\",\n\"thrash\",\n\"thrast\",\n\"thrave\",\n\"thraver\",\n\"thraw\",\n\"thrawn\",\n\"thread\",\n\"thready\",\n\"threap\",\n\"threat\",\n\"three\",\n\"threne\",\n\"threnos\",\n\"threose\",\n\"thresh\",\n\"threw\",\n\"thrice\",\n\"thrift\",\n\"thrifty\",\n\"thrill\",\n\"thrilly\",\n\"thrimp\",\n\"thring\",\n\"thrip\",\n\"thripel\",\n\"thrips\",\n\"thrive\",\n\"thriven\",\n\"thriver\",\n\"thro\",\n\"throat\",\n\"throaty\",\n\"throb\",\n\"throck\",\n\"throddy\",\n\"throe\",\n\"thronal\",\n\"throne\",\n\"throng\",\n\"throu\",\n\"throuch\",\n\"through\",\n\"throve\",\n\"throw\",\n\"thrower\",\n\"thrown\",\n\"thrum\",\n\"thrummy\",\n\"thrush\",\n\"thrushy\",\n\"thrust\",\n\"thrutch\",\n\"thruv\",\n\"thrymsa\",\n\"thud\",\n\"thug\",\n\"thugdom\",\n\"thuggee\",\n\"thujene\",\n\"thujin\",\n\"thujone\",\n\"thujyl\",\n\"thulia\",\n\"thulir\",\n\"thulite\",\n\"thulium\",\n\"thulr\",\n\"thuluth\",\n\"thumb\",\n\"thumbed\",\n\"thumber\",\n\"thumble\",\n\"thumby\",\n\"thump\",\n\"thumper\",\n\"thunder\",\n\"thung\",\n\"thunge\",\n\"thuoc\",\n\"thurify\",\n\"thurl\",\n\"thurm\",\n\"thurmus\",\n\"thurse\",\n\"thurt\",\n\"thus\",\n\"thusly\",\n\"thutter\",\n\"thwack\",\n\"thwaite\",\n\"thwart\",\n\"thwite\",\n\"thy\",\n\"thyine\",\n\"thymate\",\n\"thyme\",\n\"thymele\",\n\"thymene\",\n\"thymic\",\n\"thymine\",\n\"thymol\",\n\"thymoma\",\n\"thymus\",\n\"thymy\",\n\"thymyl\",\n\"thynnid\",\n\"thyroid\",\n\"thyrse\",\n\"thyrsus\",\n\"thysel\",\n\"thyself\",\n\"thysen\",\n\"ti\",\n\"tiang\",\n\"tiao\",\n\"tiar\",\n\"tiara\",\n\"tib\",\n\"tibby\",\n\"tibet\",\n\"tibey\",\n\"tibia\",\n\"tibiad\",\n\"tibiae\",\n\"tibial\",\n\"tibiale\",\n\"tiburon\",\n\"tic\",\n\"tical\",\n\"ticca\",\n\"tice\",\n\"ticer\",\n\"tick\",\n\"ticked\",\n\"ticken\",\n\"ticker\",\n\"ticket\",\n\"tickey\",\n\"tickie\",\n\"ticking\",\n\"tickle\",\n\"tickled\",\n\"tickler\",\n\"tickly\",\n\"tickney\",\n\"ticky\",\n\"ticul\",\n\"tid\",\n\"tidal\",\n\"tidally\",\n\"tidbit\",\n\"tiddle\",\n\"tiddler\",\n\"tiddley\",\n\"tiddy\",\n\"tide\",\n\"tided\",\n\"tideful\",\n\"tidely\",\n\"tideway\",\n\"tidily\",\n\"tiding\",\n\"tidings\",\n\"tidley\",\n\"tidy\",\n\"tidyism\",\n\"tie\",\n\"tieback\",\n\"tied\",\n\"tien\",\n\"tiepin\",\n\"tier\",\n\"tierce\",\n\"tierced\",\n\"tiered\",\n\"tierer\",\n\"tietick\",\n\"tiewig\",\n\"tiff\",\n\"tiffany\",\n\"tiffie\",\n\"tiffin\",\n\"tiffish\",\n\"tiffle\",\n\"tiffy\",\n\"tift\",\n\"tifter\",\n\"tig\",\n\"tige\",\n\"tigella\",\n\"tigelle\",\n\"tiger\",\n\"tigerly\",\n\"tigery\",\n\"tigger\",\n\"tight\",\n\"tighten\",\n\"tightly\",\n\"tights\",\n\"tiglic\",\n\"tignum\",\n\"tigress\",\n\"tigrine\",\n\"tigroid\",\n\"tigtag\",\n\"tikka\",\n\"tikker\",\n\"tiklin\",\n\"tikor\",\n\"tikur\",\n\"til\",\n\"tilaite\",\n\"tilaka\",\n\"tilbury\",\n\"tilde\",\n\"tile\",\n\"tiled\",\n\"tiler\",\n\"tilery\",\n\"tilikum\",\n\"tiling\",\n\"till\",\n\"tillage\",\n\"tiller\",\n\"tilley\",\n\"tillite\",\n\"tillot\",\n\"tilly\",\n\"tilmus\",\n\"tilpah\",\n\"tilt\",\n\"tilter\",\n\"tilth\",\n\"tilting\",\n\"tiltup\",\n\"tilty\",\n\"tilyer\",\n\"timable\",\n\"timar\",\n\"timarau\",\n\"timawa\",\n\"timbal\",\n\"timbale\",\n\"timbang\",\n\"timbe\",\n\"timber\",\n\"timbern\",\n\"timbery\",\n\"timbo\",\n\"timbre\",\n\"timbrel\",\n\"time\",\n\"timed\",\n\"timeful\",\n\"timely\",\n\"timeous\",\n\"timer\",\n\"times\",\n\"timid\",\n\"timidly\",\n\"timing\",\n\"timish\",\n\"timist\",\n\"timon\",\n\"timor\",\n\"timothy\",\n\"timpani\",\n\"timpano\",\n\"tin\",\n\"tinamou\",\n\"tincal\",\n\"tinchel\",\n\"tinclad\",\n\"tinct\",\n\"tind\",\n\"tindal\",\n\"tindalo\",\n\"tinder\",\n\"tindery\",\n\"tine\",\n\"tinea\",\n\"tineal\",\n\"tinean\",\n\"tined\",\n\"tineid\",\n\"tineine\",\n\"tineman\",\n\"tineoid\",\n\"tinety\",\n\"tinful\",\n\"ting\",\n\"tinge\",\n\"tinged\",\n\"tinger\",\n\"tingi\",\n\"tingid\",\n\"tingle\",\n\"tingler\",\n\"tingly\",\n\"tinguy\",\n\"tinhorn\",\n\"tinily\",\n\"tining\",\n\"tink\",\n\"tinker\",\n\"tinkle\",\n\"tinkler\",\n\"tinkly\",\n\"tinlet\",\n\"tinlike\",\n\"tinman\",\n\"tinned\",\n\"tinner\",\n\"tinnery\",\n\"tinnet\",\n\"tinnily\",\n\"tinning\",\n\"tinnock\",\n\"tinny\",\n\"tinosa\",\n\"tinsel\",\n\"tinsman\",\n\"tint\",\n\"tinta\",\n\"tintage\",\n\"tinted\",\n\"tinter\",\n\"tintie\",\n\"tinting\",\n\"tintist\",\n\"tinty\",\n\"tintype\",\n\"tinwald\",\n\"tinware\",\n\"tinwork\",\n\"tiny\",\n\"tip\",\n\"tipburn\",\n\"tipcart\",\n\"tipcat\",\n\"tipe\",\n\"tipful\",\n\"tiphead\",\n\"tipiti\",\n\"tiple\",\n\"tipless\",\n\"tiplet\",\n\"tipman\",\n\"tipmost\",\n\"tiponi\",\n\"tipped\",\n\"tippee\",\n\"tipper\",\n\"tippet\",\n\"tipping\",\n\"tipple\",\n\"tippler\",\n\"tipply\",\n\"tippy\",\n\"tipsify\",\n\"tipsily\",\n\"tipster\",\n\"tipsy\",\n\"tiptail\",\n\"tiptilt\",\n\"tiptoe\",\n\"tiptop\",\n\"tipulid\",\n\"tipup\",\n\"tirade\",\n\"tiralee\",\n\"tire\",\n\"tired\",\n\"tiredly\",\n\"tiredom\",\n\"tireman\",\n\"tirer\",\n\"tiriba\",\n\"tiring\",\n\"tirl\",\n\"tirma\",\n\"tirr\",\n\"tirret\",\n\"tirrlie\",\n\"tirve\",\n\"tirwit\",\n\"tisane\",\n\"tisar\",\n\"tissual\",\n\"tissue\",\n\"tissued\",\n\"tissuey\",\n\"tiswin\",\n\"tit\",\n\"titania\",\n\"titanic\",\n\"titano\",\n\"titanyl\",\n\"titar\",\n\"titbit\",\n\"tite\",\n\"titer\",\n\"titfish\",\n\"tithal\",\n\"tithe\",\n\"tither\",\n\"tithing\",\n\"titi\",\n\"titian\",\n\"titien\",\n\"titlark\",\n\"title\",\n\"titled\",\n\"titler\",\n\"titlike\",\n\"titling\",\n\"titlist\",\n\"titmal\",\n\"titman\",\n\"titoki\",\n\"titrate\",\n\"titre\",\n\"titter\",\n\"tittery\",\n\"tittie\",\n\"tittle\",\n\"tittler\",\n\"tittup\",\n\"tittupy\",\n\"titty\",\n\"titular\",\n\"titule\",\n\"titulus\",\n\"tiver\",\n\"tivoli\",\n\"tivy\",\n\"tiza\",\n\"tizeur\",\n\"tizzy\",\n\"tji\",\n\"tjosite\",\n\"tlaco\",\n\"tmema\",\n\"tmesis\",\n\"to\",\n\"toa\",\n\"toad\",\n\"toadeat\",\n\"toader\",\n\"toadery\",\n\"toadess\",\n\"toadier\",\n\"toadish\",\n\"toadlet\",\n\"toady\",\n\"toast\",\n\"toastee\",\n\"toaster\",\n\"toasty\",\n\"toat\",\n\"toatoa\",\n\"tobacco\",\n\"tobe\",\n\"tobine\",\n\"tobira\",\n\"toby\",\n\"tobyman\",\n\"toccata\",\n\"tocher\",\n\"tock\",\n\"toco\",\n\"tocome\",\n\"tocsin\",\n\"tocusso\",\n\"tod\",\n\"today\",\n\"todder\",\n\"toddick\",\n\"toddite\",\n\"toddle\",\n\"toddler\",\n\"toddy\",\n\"tode\",\n\"tody\",\n\"toe\",\n\"toecap\",\n\"toed\",\n\"toeless\",\n\"toelike\",\n\"toenail\",\n\"toetoe\",\n\"toff\",\n\"toffee\",\n\"toffing\",\n\"toffish\",\n\"toffy\",\n\"toft\",\n\"tofter\",\n\"toftman\",\n\"tofu\",\n\"tog\",\n\"toga\",\n\"togaed\",\n\"togata\",\n\"togate\",\n\"togated\",\n\"toggel\",\n\"toggery\",\n\"toggle\",\n\"toggler\",\n\"togless\",\n\"togs\",\n\"togt\",\n\"togue\",\n\"toher\",\n\"toheroa\",\n\"toho\",\n\"tohunga\",\n\"toi\",\n\"toil\",\n\"toiled\",\n\"toiler\",\n\"toilet\",\n\"toilful\",\n\"toiling\",\n\"toise\",\n\"toit\",\n\"toitish\",\n\"toity\",\n\"tokay\",\n\"toke\",\n\"token\",\n\"tokened\",\n\"toko\",\n\"tokopat\",\n\"tol\",\n\"tolan\",\n\"tolane\",\n\"told\",\n\"toldo\",\n\"tole\",\n\"tolite\",\n\"toll\",\n\"tollage\",\n\"toller\",\n\"tollery\",\n\"tolling\",\n\"tollman\",\n\"tolly\",\n\"tolsey\",\n\"tolt\",\n\"tolter\",\n\"tolu\",\n\"toluate\",\n\"toluene\",\n\"toluic\",\n\"toluide\",\n\"toluido\",\n\"toluol\",\n\"toluyl\",\n\"tolyl\",\n\"toman\",\n\"tomato\",\n\"tomb\",\n\"tombac\",\n\"tombal\",\n\"tombe\",\n\"tombic\",\n\"tomblet\",\n\"tombola\",\n\"tombolo\",\n\"tomboy\",\n\"tomcat\",\n\"tomcod\",\n\"tome\",\n\"tomeful\",\n\"tomelet\",\n\"toment\",\n\"tomfool\",\n\"tomial\",\n\"tomin\",\n\"tomish\",\n\"tomium\",\n\"tomjohn\",\n\"tomkin\",\n\"tommy\",\n\"tomnoup\",\n\"tomorn\",\n\"tomosis\",\n\"tompon\",\n\"tomtate\",\n\"tomtit\",\n\"ton\",\n\"tonal\",\n\"tonally\",\n\"tonant\",\n\"tondino\",\n\"tone\",\n\"toned\",\n\"toneme\",\n\"toner\",\n\"tonetic\",\n\"tong\",\n\"tonga\",\n\"tonger\",\n\"tongman\",\n\"tongs\",\n\"tongue\",\n\"tongued\",\n\"tonguer\",\n\"tonguey\",\n\"tonic\",\n\"tonify\",\n\"tonight\",\n\"tonish\",\n\"tonite\",\n\"tonjon\",\n\"tonk\",\n\"tonkin\",\n\"tonlet\",\n\"tonnage\",\n\"tonneau\",\n\"tonner\",\n\"tonnish\",\n\"tonous\",\n\"tonsil\",\n\"tonsor\",\n\"tonsure\",\n\"tontine\",\n\"tonus\",\n\"tony\",\n\"too\",\n\"toodle\",\n\"took\",\n\"tooken\",\n\"tool\",\n\"toolbox\",\n\"tooler\",\n\"tooling\",\n\"toolman\",\n\"toom\",\n\"toomly\",\n\"toon\",\n\"toop\",\n\"toorie\",\n\"toorock\",\n\"tooroo\",\n\"toosh\",\n\"toot\",\n\"tooter\",\n\"tooth\",\n\"toothed\",\n\"toother\",\n\"toothy\",\n\"tootle\",\n\"tootler\",\n\"tootsy\",\n\"toozle\",\n\"toozoo\",\n\"top\",\n\"toparch\",\n\"topass\",\n\"topaz\",\n\"topazy\",\n\"topcap\",\n\"topcast\",\n\"topcoat\",\n\"tope\",\n\"topee\",\n\"topeng\",\n\"topepo\",\n\"toper\",\n\"topfull\",\n\"toph\",\n\"tophus\",\n\"topi\",\n\"topia\",\n\"topiary\",\n\"topic\",\n\"topical\",\n\"topknot\",\n\"topless\",\n\"toplike\",\n\"topline\",\n\"topman\",\n\"topmast\",\n\"topmost\",\n\"topo\",\n\"toponym\",\n\"topped\",\n\"topper\",\n\"topping\",\n\"topple\",\n\"toppler\",\n\"topply\",\n\"toppy\",\n\"toprail\",\n\"toprope\",\n\"tops\",\n\"topsail\",\n\"topside\",\n\"topsl\",\n\"topsman\",\n\"topsoil\",\n\"toptail\",\n\"topwise\",\n\"toque\",\n\"tor\",\n\"tora\",\n\"torah\",\n\"toral\",\n\"toran\",\n\"torc\",\n\"torcel\",\n\"torch\",\n\"torcher\",\n\"torchon\",\n\"tore\",\n\"tored\",\n\"torero\",\n\"torfel\",\n\"torgoch\",\n\"toric\",\n\"torii\",\n\"torma\",\n\"tormen\",\n\"torment\",\n\"tormina\",\n\"torn\",\n\"tornade\",\n\"tornado\",\n\"tornal\",\n\"tornese\",\n\"torney\",\n\"tornote\",\n\"tornus\",\n\"toro\",\n\"toroid\",\n\"torose\",\n\"torous\",\n\"torpedo\",\n\"torpent\",\n\"torpid\",\n\"torpify\",\n\"torpor\",\n\"torque\",\n\"torqued\",\n\"torques\",\n\"torrefy\",\n\"torrent\",\n\"torrid\",\n\"torsade\",\n\"torse\",\n\"torsel\",\n\"torsile\",\n\"torsion\",\n\"torsive\",\n\"torsk\",\n\"torso\",\n\"tort\",\n\"torta\",\n\"torteau\",\n\"tortile\",\n\"tortive\",\n\"tortula\",\n\"torture\",\n\"toru\",\n\"torula\",\n\"torulin\",\n\"torulus\",\n\"torus\",\n\"torve\",\n\"torvid\",\n\"torvity\",\n\"torvous\",\n\"tory\",\n\"tosh\",\n\"tosher\",\n\"toshery\",\n\"toshly\",\n\"toshy\",\n\"tosily\",\n\"toss\",\n\"tosser\",\n\"tossily\",\n\"tossing\",\n\"tosspot\",\n\"tossup\",\n\"tossy\",\n\"tost\",\n\"toston\",\n\"tosy\",\n\"tot\",\n\"total\",\n\"totally\",\n\"totara\",\n\"totchka\",\n\"tote\",\n\"totem\",\n\"totemic\",\n\"totemy\",\n\"toter\",\n\"tother\",\n\"totient\",\n\"toto\",\n\"totora\",\n\"totquot\",\n\"totter\",\n\"tottery\",\n\"totting\",\n\"tottle\",\n\"totty\",\n\"totuava\",\n\"totum\",\n\"toty\",\n\"totyman\",\n\"tou\",\n\"toucan\",\n\"touch\",\n\"touched\",\n\"toucher\",\n\"touchy\",\n\"toug\",\n\"tough\",\n\"toughen\",\n\"toughly\",\n\"tought\",\n\"tould\",\n\"toumnah\",\n\"toup\",\n\"toupee\",\n\"toupeed\",\n\"toupet\",\n\"tour\",\n\"touraco\",\n\"tourer\",\n\"touring\",\n\"tourism\",\n\"tourist\",\n\"tourize\",\n\"tourn\",\n\"tournay\",\n\"tournee\",\n\"tourney\",\n\"tourte\",\n\"tousche\",\n\"touse\",\n\"touser\",\n\"tousle\",\n\"tously\",\n\"tousy\",\n\"tout\",\n\"touter\",\n\"tovar\",\n\"tow\",\n\"towable\",\n\"towage\",\n\"towai\",\n\"towan\",\n\"toward\",\n\"towards\",\n\"towboat\",\n\"towcock\",\n\"towd\",\n\"towel\",\n\"towelry\",\n\"tower\",\n\"towered\",\n\"towery\",\n\"towght\",\n\"towhead\",\n\"towhee\",\n\"towing\",\n\"towkay\",\n\"towlike\",\n\"towline\",\n\"towmast\",\n\"town\",\n\"towned\",\n\"townee\",\n\"towner\",\n\"townet\",\n\"townful\",\n\"townify\",\n\"townish\",\n\"townist\",\n\"townlet\",\n\"townly\",\n\"townman\",\n\"towny\",\n\"towpath\",\n\"towrope\",\n\"towser\",\n\"towy\",\n\"tox\",\n\"toxa\",\n\"toxamin\",\n\"toxcatl\",\n\"toxemia\",\n\"toxemic\",\n\"toxic\",\n\"toxical\",\n\"toxicum\",\n\"toxifer\",\n\"toxin\",\n\"toxity\",\n\"toxoid\",\n\"toxon\",\n\"toxone\",\n\"toxosis\",\n\"toxotae\",\n\"toy\",\n\"toydom\",\n\"toyer\",\n\"toyful\",\n\"toying\",\n\"toyish\",\n\"toyland\",\n\"toyless\",\n\"toylike\",\n\"toyman\",\n\"toyon\",\n\"toyshop\",\n\"toysome\",\n\"toytown\",\n\"toywort\",\n\"toze\",\n\"tozee\",\n\"tozer\",\n\"tra\",\n\"trabal\",\n\"trabant\",\n\"trabea\",\n\"trabeae\",\n\"trabuch\",\n\"trace\",\n\"tracer\",\n\"tracery\",\n\"trachea\",\n\"trachle\",\n\"tracing\",\n\"track\",\n\"tracked\",\n\"tracker\",\n\"tract\",\n\"tractor\",\n\"tradal\",\n\"trade\",\n\"trader\",\n\"trading\",\n\"tradite\",\n\"traduce\",\n\"trady\",\n\"traffic\",\n\"trag\",\n\"tragal\",\n\"tragedy\",\n\"tragi\",\n\"tragic\",\n\"tragus\",\n\"trah\",\n\"traheen\",\n\"traik\",\n\"trail\",\n\"trailer\",\n\"traily\",\n\"train\",\n\"trained\",\n\"trainee\",\n\"trainer\",\n\"trainy\",\n\"traipse\",\n\"trait\",\n\"traitor\",\n\"traject\",\n\"trajet\",\n\"tralira\",\n\"tram\",\n\"trama\",\n\"tramal\",\n\"tramcar\",\n\"trame\",\n\"tramful\",\n\"tramman\",\n\"trammel\",\n\"trammer\",\n\"trammon\",\n\"tramp\",\n\"tramper\",\n\"trample\",\n\"trampot\",\n\"tramway\",\n\"trance\",\n\"tranced\",\n\"traneen\",\n\"trank\",\n\"tranka\",\n\"tranker\",\n\"trankum\",\n\"tranky\",\n\"transit\",\n\"transom\",\n\"trant\",\n\"tranter\",\n\"trap\",\n\"trapes\",\n\"trapeze\",\n\"trapped\",\n\"trapper\",\n\"trappy\",\n\"traps\",\n\"trash\",\n\"traship\",\n\"trashy\",\n\"trass\",\n\"trasy\",\n\"trauma\",\n\"travail\",\n\"travale\",\n\"trave\",\n\"travel\",\n\"travis\",\n\"travois\",\n\"travoy\",\n\"trawl\",\n\"trawler\",\n\"tray\",\n\"trayful\",\n\"treacle\",\n\"treacly\",\n\"tread\",\n\"treader\",\n\"treadle\",\n\"treason\",\n\"treat\",\n\"treatee\",\n\"treater\",\n\"treator\",\n\"treaty\",\n\"treble\",\n\"trebly\",\n\"treddle\",\n\"tree\",\n\"treed\",\n\"treeful\",\n\"treeify\",\n\"treelet\",\n\"treeman\",\n\"treen\",\n\"treetop\",\n\"treey\",\n\"tref\",\n\"trefle\",\n\"trefoil\",\n\"tregerg\",\n\"tregohm\",\n\"trehala\",\n\"trek\",\n\"trekker\",\n\"trellis\",\n\"tremble\",\n\"trembly\",\n\"tremie\",\n\"tremolo\",\n\"tremor\",\n\"trenail\",\n\"trench\",\n\"trend\",\n\"trendle\",\n\"trental\",\n\"trepan\",\n\"trepang\",\n\"trepid\",\n\"tress\",\n\"tressed\",\n\"tresson\",\n\"tressy\",\n\"trest\",\n\"trestle\",\n\"tret\",\n\"trevet\",\n\"trews\",\n\"trey\",\n\"tri\",\n\"triable\",\n\"triace\",\n\"triacid\",\n\"triact\",\n\"triad\",\n\"triadic\",\n\"triaene\",\n\"triage\",\n\"trial\",\n\"triamid\",\n\"triarch\",\n\"triarii\",\n\"triatic\",\n\"triaxon\",\n\"triazin\",\n\"triazo\",\n\"tribade\",\n\"tribady\",\n\"tribal\",\n\"tribase\",\n\"tribble\",\n\"tribe\",\n\"triblet\",\n\"tribrac\",\n\"tribual\",\n\"tribuna\",\n\"tribune\",\n\"tribute\",\n\"trica\",\n\"tricae\",\n\"tricar\",\n\"trice\",\n\"triceps\",\n\"trichi\",\n\"trichia\",\n\"trichy\",\n\"trick\",\n\"tricker\",\n\"trickle\",\n\"trickly\",\n\"tricksy\",\n\"tricky\",\n\"triclad\",\n\"tricorn\",\n\"tricot\",\n\"trident\",\n\"triduan\",\n\"triduum\",\n\"tried\",\n\"triedly\",\n\"triene\",\n\"triens\",\n\"trier\",\n\"trifa\",\n\"trifid\",\n\"trifle\",\n\"trifler\",\n\"triflet\",\n\"trifoil\",\n\"trifold\",\n\"trifoly\",\n\"triform\",\n\"trig\",\n\"trigamy\",\n\"trigger\",\n\"triglid\",\n\"triglot\",\n\"trigly\",\n\"trigon\",\n\"trigone\",\n\"trigram\",\n\"trigyn\",\n\"trikaya\",\n\"trike\",\n\"triker\",\n\"triketo\",\n\"trikir\",\n\"trilabe\",\n\"trilby\",\n\"trilit\",\n\"trilite\",\n\"trilith\",\n\"trill\",\n\"trillet\",\n\"trilli\",\n\"trillo\",\n\"trilobe\",\n\"trilogy\",\n\"trim\",\n\"trimer\",\n\"trimly\",\n\"trimmer\",\n\"trin\",\n\"trinal\",\n\"trinary\",\n\"trindle\",\n\"trine\",\n\"trinely\",\n\"tringle\",\n\"trinity\",\n\"trink\",\n\"trinket\",\n\"trinkle\",\n\"trinode\",\n\"trinol\",\n\"trintle\",\n\"trio\",\n\"triobol\",\n\"triode\",\n\"triodia\",\n\"triole\",\n\"triolet\",\n\"trionym\",\n\"trior\",\n\"triose\",\n\"trip\",\n\"tripal\",\n\"tripara\",\n\"tripart\",\n\"tripe\",\n\"tripel\",\n\"tripery\",\n\"triple\",\n\"triplet\",\n\"triplex\",\n\"triplum\",\n\"triply\",\n\"tripod\",\n\"tripody\",\n\"tripoli\",\n\"tripos\",\n\"tripper\",\n\"trippet\",\n\"tripple\",\n\"tripsis\",\n\"tripy\",\n\"trireme\",\n\"trisalt\",\n\"trisazo\",\n\"trisect\",\n\"triseme\",\n\"trishna\",\n\"trismic\",\n\"trismus\",\n\"trisome\",\n\"trisomy\",\n\"trist\",\n\"trisul\",\n\"trisula\",\n\"tritaph\",\n\"trite\",\n\"tritely\",\n\"tritish\",\n\"tritium\",\n\"tritolo\",\n\"triton\",\n\"tritone\",\n\"tritor\",\n\"trityl\",\n\"triumph\",\n\"triunal\",\n\"triune\",\n\"triurid\",\n\"trivant\",\n\"trivet\",\n\"trivia\",\n\"trivial\",\n\"trivium\",\n\"trivvet\",\n\"trizoic\",\n\"trizone\",\n\"troat\",\n\"troca\",\n\"trocar\",\n\"trochal\",\n\"troche\",\n\"trochee\",\n\"trochi\",\n\"trochid\",\n\"trochus\",\n\"trock\",\n\"troco\",\n\"trod\",\n\"trodden\",\n\"trode\",\n\"troft\",\n\"trog\",\n\"trogger\",\n\"troggin\",\n\"trogon\",\n\"trogs\",\n\"trogue\",\n\"troika\",\n\"troke\",\n\"troker\",\n\"troll\",\n\"troller\",\n\"trolley\",\n\"trollol\",\n\"trollop\",\n\"trolly\",\n\"tromba\",\n\"trombe\",\n\"trommel\",\n\"tromp\",\n\"trompe\",\n\"trompil\",\n\"tromple\",\n\"tron\",\n\"trona\",\n\"tronage\",\n\"tronc\",\n\"trone\",\n\"troner\",\n\"troolie\",\n\"troop\",\n\"trooper\",\n\"troot\",\n\"tropal\",\n\"tropary\",\n\"tropate\",\n\"trope\",\n\"tropeic\",\n\"troper\",\n\"trophal\",\n\"trophi\",\n\"trophic\",\n\"trophy\",\n\"tropic\",\n\"tropine\",\n\"tropism\",\n\"tropist\",\n\"tropoyl\",\n\"tropyl\",\n\"trot\",\n\"troth\",\n\"trotlet\",\n\"trotol\",\n\"trotter\",\n\"trottie\",\n\"trotty\",\n\"trotyl\",\n\"trouble\",\n\"troubly\",\n\"trough\",\n\"troughy\",\n\"trounce\",\n\"troupe\",\n\"trouper\",\n\"trouse\",\n\"trouser\",\n\"trout\",\n\"trouter\",\n\"trouty\",\n\"trove\",\n\"trover\",\n\"trow\",\n\"trowel\",\n\"trowing\",\n\"trowman\",\n\"trowth\",\n\"troy\",\n\"truancy\",\n\"truant\",\n\"trub\",\n\"trubu\",\n\"truce\",\n\"trucial\",\n\"truck\",\n\"trucker\",\n\"truckle\",\n\"trucks\",\n\"truddo\",\n\"trudge\",\n\"trudgen\",\n\"trudger\",\n\"true\",\n\"truer\",\n\"truff\",\n\"truffle\",\n\"trug\",\n\"truish\",\n\"truism\",\n\"trull\",\n\"truller\",\n\"trullo\",\n\"truly\",\n\"trummel\",\n\"trump\",\n\"trumper\",\n\"trumpet\",\n\"trumph\",\n\"trumpie\",\n\"trun\",\n\"truncal\",\n\"trunch\",\n\"trundle\",\n\"trunk\",\n\"trunked\",\n\"trunnel\",\n\"trush\",\n\"trusion\",\n\"truss\",\n\"trussed\",\n\"trusser\",\n\"trust\",\n\"trustee\",\n\"trusten\",\n\"truster\",\n\"trustle\",\n\"trusty\",\n\"truth\",\n\"truthy\",\n\"truvat\",\n\"try\",\n\"trygon\",\n\"trying\",\n\"tryma\",\n\"tryout\",\n\"tryp\",\n\"trypa\",\n\"trypan\",\n\"trypsin\",\n\"tryptic\",\n\"trysail\",\n\"tryst\",\n\"tryster\",\n\"tryt\",\n\"tsadik\",\n\"tsamba\",\n\"tsantsa\",\n\"tsar\",\n\"tsardom\",\n\"tsarina\",\n\"tsatlee\",\n\"tsere\",\n\"tsetse\",\n\"tsia\",\n\"tsine\",\n\"tst\",\n\"tsuba\",\n\"tsubo\",\n\"tsun\",\n\"tsunami\",\n\"tsungtu\",\n\"tu\",\n\"tua\",\n\"tuan\",\n\"tuarn\",\n\"tuart\",\n\"tuatara\",\n\"tuatera\",\n\"tuath\",\n\"tub\",\n\"tuba\",\n\"tubae\",\n\"tubage\",\n\"tubal\",\n\"tubar\",\n\"tubate\",\n\"tubba\",\n\"tubbal\",\n\"tubbeck\",\n\"tubber\",\n\"tubbie\",\n\"tubbing\",\n\"tubbish\",\n\"tubboe\",\n\"tubby\",\n\"tube\",\n\"tubeful\",\n\"tubelet\",\n\"tubeman\",\n\"tuber\",\n\"tuberin\",\n\"tubfish\",\n\"tubful\",\n\"tubicen\",\n\"tubifer\",\n\"tubig\",\n\"tubik\",\n\"tubing\",\n\"tublet\",\n\"tublike\",\n\"tubman\",\n\"tubular\",\n\"tubule\",\n\"tubulet\",\n\"tubuli\",\n\"tubulus\",\n\"tuchit\",\n\"tuchun\",\n\"tuck\",\n\"tucker\",\n\"tucket\",\n\"tucking\",\n\"tuckner\",\n\"tucktoo\",\n\"tucky\",\n\"tucum\",\n\"tucuma\",\n\"tucuman\",\n\"tudel\",\n\"tue\",\n\"tueiron\",\n\"tufa\",\n\"tufan\",\n\"tuff\",\n\"tuffet\",\n\"tuffing\",\n\"tuft\",\n\"tufted\",\n\"tufter\",\n\"tuftily\",\n\"tufting\",\n\"tuftlet\",\n\"tufty\",\n\"tug\",\n\"tugboat\",\n\"tugger\",\n\"tuggery\",\n\"tugging\",\n\"tughra\",\n\"tugless\",\n\"tuglike\",\n\"tugman\",\n\"tugrik\",\n\"tugui\",\n\"tui\",\n\"tuik\",\n\"tuille\",\n\"tuilyie\",\n\"tuism\",\n\"tuition\",\n\"tuitive\",\n\"tuke\",\n\"tukra\",\n\"tula\",\n\"tulare\",\n\"tulasi\",\n\"tulchan\",\n\"tulchin\",\n\"tule\",\n\"tuliac\",\n\"tulip\",\n\"tulipy\",\n\"tulisan\",\n\"tulle\",\n\"tulsi\",\n\"tulwar\",\n\"tum\",\n\"tumasha\",\n\"tumbak\",\n\"tumble\",\n\"tumbled\",\n\"tumbler\",\n\"tumbly\",\n\"tumbrel\",\n\"tume\",\n\"tumefy\",\n\"tumid\",\n\"tumidly\",\n\"tummals\",\n\"tummel\",\n\"tummer\",\n\"tummock\",\n\"tummy\",\n\"tumor\",\n\"tumored\",\n\"tump\",\n\"tumtum\",\n\"tumular\",\n\"tumuli\",\n\"tumult\",\n\"tumulus\",\n\"tun\",\n\"tuna\",\n\"tunable\",\n\"tunably\",\n\"tunca\",\n\"tund\",\n\"tunder\",\n\"tundish\",\n\"tundra\",\n\"tundun\",\n\"tune\",\n\"tuned\",\n\"tuneful\",\n\"tuner\",\n\"tunful\",\n\"tung\",\n\"tungate\",\n\"tungo\",\n\"tunhoof\",\n\"tunic\",\n\"tunicin\",\n\"tunicle\",\n\"tuning\",\n\"tunish\",\n\"tunist\",\n\"tunk\",\n\"tunket\",\n\"tunlike\",\n\"tunmoot\",\n\"tunna\",\n\"tunnel\",\n\"tunner\",\n\"tunnery\",\n\"tunnor\",\n\"tunny\",\n\"tuno\",\n\"tunu\",\n\"tuny\",\n\"tup\",\n\"tupara\",\n\"tupek\",\n\"tupelo\",\n\"tupik\",\n\"tupman\",\n\"tupuna\",\n\"tuque\",\n\"tur\",\n\"turacin\",\n\"turb\",\n\"turban\",\n\"turbary\",\n\"turbeh\",\n\"turbid\",\n\"turbine\",\n\"turbit\",\n\"turbith\",\n\"turbo\",\n\"turbot\",\n\"turco\",\n\"turd\",\n\"turdine\",\n\"turdoid\",\n\"tureen\",\n\"turf\",\n\"turfage\",\n\"turfdom\",\n\"turfed\",\n\"turfen\",\n\"turfing\",\n\"turfite\",\n\"turfman\",\n\"turfy\",\n\"turgent\",\n\"turgid\",\n\"turgite\",\n\"turgoid\",\n\"turgor\",\n\"turgy\",\n\"turio\",\n\"turion\",\n\"turjite\",\n\"turk\",\n\"turken\",\n\"turkey\",\n\"turkis\",\n\"turkle\",\n\"turm\",\n\"turma\",\n\"turment\",\n\"turmit\",\n\"turmoil\",\n\"turn\",\n\"turncap\",\n\"turndun\",\n\"turned\",\n\"turnel\",\n\"turner\",\n\"turnery\",\n\"turney\",\n\"turning\",\n\"turnip\",\n\"turnipy\",\n\"turnix\",\n\"turnkey\",\n\"turnoff\",\n\"turnout\",\n\"turnpin\",\n\"turnrow\",\n\"turns\",\n\"turnup\",\n\"turp\",\n\"turpeth\",\n\"turpid\",\n\"turps\",\n\"turr\",\n\"turret\",\n\"turse\",\n\"tursio\",\n\"turtle\",\n\"turtler\",\n\"turtlet\",\n\"turtosa\",\n\"tururi\",\n\"turus\",\n\"turwar\",\n\"tusche\",\n\"tush\",\n\"tushed\",\n\"tusher\",\n\"tushery\",\n\"tusk\",\n\"tuskar\",\n\"tusked\",\n\"tusker\",\n\"tuskish\",\n\"tusky\",\n\"tussah\",\n\"tussal\",\n\"tusser\",\n\"tussis\",\n\"tussive\",\n\"tussle\",\n\"tussock\",\n\"tussore\",\n\"tussur\",\n\"tut\",\n\"tutania\",\n\"tutball\",\n\"tute\",\n\"tutee\",\n\"tutela\",\n\"tutelar\",\n\"tutenag\",\n\"tuth\",\n\"tutin\",\n\"tutly\",\n\"tutman\",\n\"tutor\",\n\"tutorer\",\n\"tutorly\",\n\"tutory\",\n\"tutoyer\",\n\"tutress\",\n\"tutrice\",\n\"tutrix\",\n\"tuts\",\n\"tutsan\",\n\"tutster\",\n\"tutti\",\n\"tutty\",\n\"tutu\",\n\"tutulus\",\n\"tutwork\",\n\"tuwi\",\n\"tux\",\n\"tuxedo\",\n\"tuyere\",\n\"tuza\",\n\"tuzzle\",\n\"twa\",\n\"twaddle\",\n\"twaddly\",\n\"twaddy\",\n\"twae\",\n\"twagger\",\n\"twain\",\n\"twaite\",\n\"twal\",\n\"twale\",\n\"twalt\",\n\"twang\",\n\"twanger\",\n\"twangle\",\n\"twangy\",\n\"twank\",\n\"twanker\",\n\"twankle\",\n\"twanky\",\n\"twant\",\n\"twarly\",\n\"twas\",\n\"twasome\",\n\"twat\",\n\"twattle\",\n\"tway\",\n\"twazzy\",\n\"tweag\",\n\"tweak\",\n\"tweaker\",\n\"tweaky\",\n\"twee\",\n\"tweed\",\n\"tweeded\",\n\"tweedle\",\n\"tweedy\",\n\"tweeg\",\n\"tweel\",\n\"tween\",\n\"tweeny\",\n\"tweesh\",\n\"tweesht\",\n\"tweest\",\n\"tweet\",\n\"tweeter\",\n\"tweeze\",\n\"tweezer\",\n\"tweil\",\n\"twelfth\",\n\"twelve\",\n\"twenty\",\n\"twere\",\n\"twerp\",\n\"twibil\",\n\"twice\",\n\"twicer\",\n\"twicet\",\n\"twick\",\n\"twiddle\",\n\"twiddly\",\n\"twifoil\",\n\"twifold\",\n\"twig\",\n\"twigful\",\n\"twigged\",\n\"twiggen\",\n\"twigger\",\n\"twiggy\",\n\"twiglet\",\n\"twilit\",\n\"twill\",\n\"twilled\",\n\"twiller\",\n\"twilly\",\n\"twilt\",\n\"twin\",\n\"twindle\",\n\"twine\",\n\"twiner\",\n\"twinge\",\n\"twingle\",\n\"twinism\",\n\"twink\",\n\"twinkle\",\n\"twinkly\",\n\"twinly\",\n\"twinned\",\n\"twinner\",\n\"twinter\",\n\"twiny\",\n\"twire\",\n\"twirk\",\n\"twirl\",\n\"twirler\",\n\"twirly\",\n\"twiscar\",\n\"twisel\",\n\"twist\",\n\"twisted\",\n\"twister\",\n\"twistle\",\n\"twisty\",\n\"twit\",\n\"twitch\",\n\"twitchy\",\n\"twite\",\n\"twitten\",\n\"twitter\",\n\"twitty\",\n\"twixt\",\n\"twizzle\",\n\"two\",\n\"twofold\",\n\"twoling\",\n\"twoness\",\n\"twosome\",\n\"tychism\",\n\"tychite\",\n\"tycoon\",\n\"tyddyn\",\n\"tydie\",\n\"tye\",\n\"tyee\",\n\"tyg\",\n\"tying\",\n\"tyke\",\n\"tyken\",\n\"tykhana\",\n\"tyking\",\n\"tylarus\",\n\"tylion\",\n\"tyloma\",\n\"tylopod\",\n\"tylose\",\n\"tylosis\",\n\"tylote\",\n\"tylotic\",\n\"tylotus\",\n\"tylus\",\n\"tymp\",\n\"tympan\",\n\"tympana\",\n\"tympani\",\n\"tympany\",\n\"tynd\",\n\"typal\",\n\"type\",\n\"typer\",\n\"typeset\",\n\"typhia\",\n\"typhic\",\n\"typhlon\",\n\"typhoid\",\n\"typhoon\",\n\"typhose\",\n\"typhous\",\n\"typhus\",\n\"typic\",\n\"typica\",\n\"typical\",\n\"typicon\",\n\"typicum\",\n\"typify\",\n\"typist\",\n\"typo\",\n\"typobar\",\n\"typonym\",\n\"typp\",\n\"typy\",\n\"tyranny\",\n\"tyrant\",\n\"tyre\",\n\"tyro\",\n\"tyroma\",\n\"tyrone\",\n\"tyronic\",\n\"tyrosyl\",\n\"tyste\",\n\"tyt\",\n\"tzolkin\",\n\"tzontle\",\n\"u\",\n\"uang\",\n\"uayeb\",\n\"uberant\",\n\"uberous\",\n\"uberty\",\n\"ubi\",\n\"ubiety\",\n\"ubiquit\",\n\"ubussu\",\n\"uckia\",\n\"udal\",\n\"udaler\",\n\"udaller\",\n\"udalman\",\n\"udasi\",\n\"udder\",\n\"uddered\",\n\"udell\",\n\"udo\",\n\"ug\",\n\"ugh\",\n\"uglify\",\n\"uglily\",\n\"ugly\",\n\"ugsome\",\n\"uhlan\",\n\"uhllo\",\n\"uhtsong\",\n\"uily\",\n\"uinal\",\n\"uintjie\",\n\"uitspan\",\n\"uji\",\n\"ukase\",\n\"uke\",\n\"ukiyoye\",\n\"ukulele\",\n\"ula\",\n\"ulcer\",\n\"ulcered\",\n\"ulcery\",\n\"ule\",\n\"ulema\",\n\"uletic\",\n\"ulex\",\n\"ulexine\",\n\"ulexite\",\n\"ulitis\",\n\"ull\",\n\"ulla\",\n\"ullage\",\n\"ullaged\",\n\"uller\",\n\"ulling\",\n\"ulluco\",\n\"ulmic\",\n\"ulmin\",\n\"ulminic\",\n\"ulmo\",\n\"ulmous\",\n\"ulna\",\n\"ulnad\",\n\"ulnae\",\n\"ulnar\",\n\"ulnare\",\n\"ulnaria\",\n\"uloid\",\n\"uloncus\",\n\"ulster\",\n\"ultima\",\n\"ultimo\",\n\"ultimum\",\n\"ultra\",\n\"ulu\",\n\"ulua\",\n\"uluhi\",\n\"ululant\",\n\"ululate\",\n\"ululu\",\n\"um\",\n\"umbel\",\n\"umbeled\",\n\"umbella\",\n\"umber\",\n\"umbilic\",\n\"umble\",\n\"umbo\",\n\"umbonal\",\n\"umbone\",\n\"umbones\",\n\"umbonic\",\n\"umbra\",\n\"umbrae\",\n\"umbrage\",\n\"umbral\",\n\"umbrel\",\n\"umbril\",\n\"umbrine\",\n\"umbrose\",\n\"umbrous\",\n\"ume\",\n\"umiak\",\n\"umiri\",\n\"umlaut\",\n\"ump\",\n\"umph\",\n\"umpire\",\n\"umpirer\",\n\"umpteen\",\n\"umpty\",\n\"umu\",\n\"un\",\n\"unable\",\n\"unably\",\n\"unact\",\n\"unacted\",\n\"unacute\",\n\"unadapt\",\n\"unadd\",\n\"unadded\",\n\"unadopt\",\n\"unadorn\",\n\"unadult\",\n\"unafire\",\n\"unaflow\",\n\"unaged\",\n\"unagile\",\n\"unaging\",\n\"unaided\",\n\"unaimed\",\n\"unaired\",\n\"unakin\",\n\"unakite\",\n\"unal\",\n\"unalarm\",\n\"unalert\",\n\"unalike\",\n\"unalist\",\n\"unalive\",\n\"unallow\",\n\"unalone\",\n\"unaloud\",\n\"unamend\",\n\"unamiss\",\n\"unamo\",\n\"unample\",\n\"unamply\",\n\"unangry\",\n\"unannex\",\n\"unapart\",\n\"unapt\",\n\"unaptly\",\n\"unarch\",\n\"unark\",\n\"unarm\",\n\"unarmed\",\n\"unarray\",\n\"unarted\",\n\"unary\",\n\"unasked\",\n\"unau\",\n\"unavian\",\n\"unawake\",\n\"unaware\",\n\"unaway\",\n\"unawed\",\n\"unawful\",\n\"unawned\",\n\"unaxled\",\n\"unbag\",\n\"unbain\",\n\"unbait\",\n\"unbaked\",\n\"unbale\",\n\"unbank\",\n\"unbar\",\n\"unbarb\",\n\"unbare\",\n\"unbark\",\n\"unbase\",\n\"unbased\",\n\"unbaste\",\n\"unbated\",\n\"unbay\",\n\"unbe\",\n\"unbear\",\n\"unbeard\",\n\"unbeast\",\n\"unbed\",\n\"unbefit\",\n\"unbeget\",\n\"unbegot\",\n\"unbegun\",\n\"unbeing\",\n\"unbell\",\n\"unbelt\",\n\"unbench\",\n\"unbend\",\n\"unbent\",\n\"unberth\",\n\"unbeset\",\n\"unbesot\",\n\"unbet\",\n\"unbias\",\n\"unbid\",\n\"unbind\",\n\"unbit\",\n\"unbitt\",\n\"unblade\",\n\"unbled\",\n\"unblent\",\n\"unbless\",\n\"unblest\",\n\"unblind\",\n\"unbliss\",\n\"unblock\",\n\"unbloom\",\n\"unblown\",\n\"unblued\",\n\"unblush\",\n\"unboat\",\n\"unbody\",\n\"unbog\",\n\"unboggy\",\n\"unbokel\",\n\"unbold\",\n\"unbolt\",\n\"unbone\",\n\"unboned\",\n\"unbonny\",\n\"unboot\",\n\"unbored\",\n\"unborn\",\n\"unborne\",\n\"unbosom\",\n\"unbound\",\n\"unbow\",\n\"unbowed\",\n\"unbowel\",\n\"unbox\",\n\"unboxed\",\n\"unboy\",\n\"unbrace\",\n\"unbraid\",\n\"unbran\",\n\"unbrand\",\n\"unbrave\",\n\"unbraze\",\n\"unbred\",\n\"unbrent\",\n\"unbrick\",\n\"unbrief\",\n\"unbroad\",\n\"unbroke\",\n\"unbrown\",\n\"unbrute\",\n\"unbud\",\n\"unbuild\",\n\"unbuilt\",\n\"unbulky\",\n\"unbung\",\n\"unburly\",\n\"unburn\",\n\"unburnt\",\n\"unburst\",\n\"unbury\",\n\"unbush\",\n\"unbusk\",\n\"unbusy\",\n\"unbuxom\",\n\"unca\",\n\"uncage\",\n\"uncaged\",\n\"uncake\",\n\"uncalk\",\n\"uncall\",\n\"uncalm\",\n\"uncaned\",\n\"uncanny\",\n\"uncap\",\n\"uncart\",\n\"uncase\",\n\"uncased\",\n\"uncask\",\n\"uncast\",\n\"uncaste\",\n\"uncate\",\n\"uncave\",\n\"unceded\",\n\"unchain\",\n\"unchair\",\n\"uncharm\",\n\"unchary\",\n\"uncheat\",\n\"uncheck\",\n\"unchid\",\n\"unchild\",\n\"unchurn\",\n\"unci\",\n\"uncia\",\n\"uncial\",\n\"uncinal\",\n\"uncinch\",\n\"uncinct\",\n\"uncini\",\n\"uncinus\",\n\"uncite\",\n\"uncited\",\n\"uncity\",\n\"uncivic\",\n\"uncivil\",\n\"unclad\",\n\"unclamp\",\n\"unclasp\",\n\"unclay\",\n\"uncle\",\n\"unclead\",\n\"unclean\",\n\"unclear\",\n\"uncleft\",\n\"unclew\",\n\"unclick\",\n\"unclify\",\n\"unclimb\",\n\"uncling\",\n\"unclip\",\n\"uncloak\",\n\"unclog\",\n\"unclose\",\n\"uncloud\",\n\"unclout\",\n\"unclub\",\n\"unco\",\n\"uncoach\",\n\"uncoat\",\n\"uncock\",\n\"uncoded\",\n\"uncoif\",\n\"uncoil\",\n\"uncoin\",\n\"uncoked\",\n\"uncolt\",\n\"uncoly\",\n\"uncome\",\n\"uncomfy\",\n\"uncomic\",\n\"uncoop\",\n\"uncope\",\n\"uncord\",\n\"uncore\",\n\"uncored\",\n\"uncork\",\n\"uncost\",\n\"uncouch\",\n\"uncous\",\n\"uncouth\",\n\"uncover\",\n\"uncowed\",\n\"uncowl\",\n\"uncoy\",\n\"uncram\",\n\"uncramp\",\n\"uncream\",\n\"uncrest\",\n\"uncrib\",\n\"uncried\",\n\"uncrime\",\n\"uncrisp\",\n\"uncrook\",\n\"uncropt\",\n\"uncross\",\n\"uncrown\",\n\"uncrude\",\n\"uncruel\",\n\"unction\",\n\"uncubic\",\n\"uncular\",\n\"uncurb\",\n\"uncurd\",\n\"uncured\",\n\"uncurl\",\n\"uncurse\",\n\"uncurst\",\n\"uncus\",\n\"uncut\",\n\"uncuth\",\n\"undaily\",\n\"undam\",\n\"undamn\",\n\"undared\",\n\"undark\",\n\"undate\",\n\"undated\",\n\"undaub\",\n\"undazed\",\n\"unde\",\n\"undead\",\n\"undeaf\",\n\"undealt\",\n\"undean\",\n\"undear\",\n\"undeck\",\n\"undecyl\",\n\"undeep\",\n\"undeft\",\n\"undeify\",\n\"undelve\",\n\"unden\",\n\"under\",\n\"underdo\",\n\"underer\",\n\"undergo\",\n\"underly\",\n\"undern\",\n\"undevil\",\n\"undewed\",\n\"undewy\",\n\"undid\",\n\"undies\",\n\"undig\",\n\"undight\",\n\"undiked\",\n\"undim\",\n\"undine\",\n\"undined\",\n\"undirk\",\n\"undo\",\n\"undock\",\n\"undoer\",\n\"undog\",\n\"undoing\",\n\"undomed\",\n\"undon\",\n\"undone\",\n\"undoped\",\n\"undose\",\n\"undosed\",\n\"undowny\",\n\"undrab\",\n\"undrag\",\n\"undrape\",\n\"undraw\",\n\"undrawn\",\n\"undress\",\n\"undried\",\n\"undrunk\",\n\"undry\",\n\"undub\",\n\"unducal\",\n\"undue\",\n\"undug\",\n\"unduke\",\n\"undular\",\n\"undull\",\n\"unduly\",\n\"unduped\",\n\"undust\",\n\"unduty\",\n\"undwelt\",\n\"undy\",\n\"undye\",\n\"undyed\",\n\"undying\",\n\"uneager\",\n\"unearly\",\n\"unearth\",\n\"unease\",\n\"uneasy\",\n\"uneaten\",\n\"uneath\",\n\"unebbed\",\n\"unedge\",\n\"unedged\",\n\"unelect\",\n\"unempt\",\n\"unempty\",\n\"unended\",\n\"unepic\",\n\"unequal\",\n\"unerect\",\n\"unethic\",\n\"uneven\",\n\"unevil\",\n\"unexact\",\n\"uneye\",\n\"uneyed\",\n\"unface\",\n\"unfaced\",\n\"unfact\",\n\"unfaded\",\n\"unfain\",\n\"unfaint\",\n\"unfair\",\n\"unfaith\",\n\"unfaked\",\n\"unfalse\",\n\"unfamed\",\n\"unfancy\",\n\"unfar\",\n\"unfast\",\n\"unfeary\",\n\"unfed\",\n\"unfeed\",\n\"unfele\",\n\"unfelon\",\n\"unfelt\",\n\"unfence\",\n\"unfeted\",\n\"unfeued\",\n\"unfew\",\n\"unfiber\",\n\"unfiend\",\n\"unfiery\",\n\"unfight\",\n\"unfile\",\n\"unfiled\",\n\"unfill\",\n\"unfilm\",\n\"unfine\",\n\"unfined\",\n\"unfired\",\n\"unfirm\",\n\"unfit\",\n\"unfitly\",\n\"unfitty\",\n\"unfix\",\n\"unfixed\",\n\"unflag\",\n\"unflaky\",\n\"unflank\",\n\"unflat\",\n\"unflead\",\n\"unflesh\",\n\"unflock\",\n\"unfloor\",\n\"unflown\",\n\"unfluid\",\n\"unflush\",\n\"unfoggy\",\n\"unfold\",\n\"unfond\",\n\"unfool\",\n\"unfork\",\n\"unform\",\n\"unfoul\",\n\"unfound\",\n\"unfoxy\",\n\"unfrail\",\n\"unframe\",\n\"unfrank\",\n\"unfree\",\n\"unfreed\",\n\"unfret\",\n\"unfried\",\n\"unfrill\",\n\"unfrizz\",\n\"unfrock\",\n\"unfrost\",\n\"unfroze\",\n\"unfull\",\n\"unfully\",\n\"unfumed\",\n\"unfunny\",\n\"unfur\",\n\"unfurl\",\n\"unfused\",\n\"unfussy\",\n\"ungag\",\n\"ungaged\",\n\"ungain\",\n\"ungaite\",\n\"ungaro\",\n\"ungaudy\",\n\"ungear\",\n\"ungelt\",\n\"unget\",\n\"ungiant\",\n\"ungiddy\",\n\"ungild\",\n\"ungill\",\n\"ungilt\",\n\"ungird\",\n\"ungirt\",\n\"ungirth\",\n\"ungive\",\n\"ungiven\",\n\"ungka\",\n\"unglad\",\n\"unglaze\",\n\"unglee\",\n\"unglobe\",\n\"ungloom\",\n\"unglory\",\n\"ungloss\",\n\"unglove\",\n\"unglue\",\n\"unglued\",\n\"ungnaw\",\n\"ungnawn\",\n\"ungod\",\n\"ungodly\",\n\"ungold\",\n\"ungone\",\n\"ungood\",\n\"ungored\",\n\"ungorge\",\n\"ungot\",\n\"ungouty\",\n\"ungown\",\n\"ungrace\",\n\"ungraft\",\n\"ungrain\",\n\"ungrand\",\n\"ungrasp\",\n\"ungrave\",\n\"ungreat\",\n\"ungreen\",\n\"ungrip\",\n\"ungripe\",\n\"ungross\",\n\"ungrow\",\n\"ungrown\",\n\"ungruff\",\n\"ungual\",\n\"unguard\",\n\"ungueal\",\n\"unguent\",\n\"ungues\",\n\"unguis\",\n\"ungula\",\n\"ungulae\",\n\"ungular\",\n\"unguled\",\n\"ungull\",\n\"ungulp\",\n\"ungum\",\n\"unguyed\",\n\"ungyve\",\n\"ungyved\",\n\"unhabit\",\n\"unhad\",\n\"unhaft\",\n\"unhair\",\n\"unhairy\",\n\"unhand\",\n\"unhandy\",\n\"unhang\",\n\"unhap\",\n\"unhappy\",\n\"unhard\",\n\"unhardy\",\n\"unharsh\",\n\"unhasp\",\n\"unhaste\",\n\"unhasty\",\n\"unhat\",\n\"unhate\",\n\"unhated\",\n\"unhaunt\",\n\"unhave\",\n\"unhayed\",\n\"unhazed\",\n\"unhead\",\n\"unheady\",\n\"unheal\",\n\"unheard\",\n\"unheart\",\n\"unheavy\",\n\"unhedge\",\n\"unheed\",\n\"unheedy\",\n\"unheld\",\n\"unhele\",\n\"unheler\",\n\"unhelm\",\n\"unherd\",\n\"unhero\",\n\"unhewed\",\n\"unhewn\",\n\"unhex\",\n\"unhid\",\n\"unhide\",\n\"unhigh\",\n\"unhinge\",\n\"unhired\",\n\"unhit\",\n\"unhitch\",\n\"unhive\",\n\"unhoard\",\n\"unhoary\",\n\"unhoed\",\n\"unhoist\",\n\"unhold\",\n\"unholy\",\n\"unhome\",\n\"unhoned\",\n\"unhood\",\n\"unhook\",\n\"unhoop\",\n\"unhoped\",\n\"unhorny\",\n\"unhorse\",\n\"unhose\",\n\"unhosed\",\n\"unhot\",\n\"unhouse\",\n\"unhull\",\n\"unhuman\",\n\"unhumid\",\n\"unhung\",\n\"unhurt\",\n\"unhusk\",\n\"uniat\",\n\"uniate\",\n\"uniaxal\",\n\"unible\",\n\"unice\",\n\"uniced\",\n\"unicell\",\n\"unicism\",\n\"unicist\",\n\"unicity\",\n\"unicorn\",\n\"unicum\",\n\"unideal\",\n\"unidle\",\n\"unidly\",\n\"unie\",\n\"uniface\",\n\"unific\",\n\"unified\",\n\"unifier\",\n\"uniflow\",\n\"uniform\",\n\"unify\",\n\"unilobe\",\n\"unimped\",\n\"uninked\",\n\"uninn\",\n\"unio\",\n\"unioid\",\n\"union\",\n\"unioned\",\n\"unionic\",\n\"unionid\",\n\"unioval\",\n\"unipara\",\n\"uniped\",\n\"unipod\",\n\"unique\",\n\"unireme\",\n\"unisoil\",\n\"unison\",\n\"unit\",\n\"unitage\",\n\"unital\",\n\"unitary\",\n\"unite\",\n\"united\",\n\"uniter\",\n\"uniting\",\n\"unition\",\n\"unitism\",\n\"unitive\",\n\"unitize\",\n\"unitude\",\n\"unity\",\n\"univied\",\n\"unjaded\",\n\"unjam\",\n\"unjewel\",\n\"unjoin\",\n\"unjoint\",\n\"unjolly\",\n\"unjoyed\",\n\"unjudge\",\n\"unjuicy\",\n\"unjust\",\n\"unkamed\",\n\"unked\",\n\"unkempt\",\n\"unken\",\n\"unkept\",\n\"unket\",\n\"unkey\",\n\"unkeyed\",\n\"unkid\",\n\"unkill\",\n\"unkin\",\n\"unkind\",\n\"unking\",\n\"unkink\",\n\"unkirk\",\n\"unkiss\",\n\"unkist\",\n\"unknave\",\n\"unknew\",\n\"unknit\",\n\"unknot\",\n\"unknow\",\n\"unknown\",\n\"unlace\",\n\"unlaced\",\n\"unlade\",\n\"unladen\",\n\"unlaid\",\n\"unlame\",\n\"unlamed\",\n\"unland\",\n\"unlap\",\n\"unlarge\",\n\"unlash\",\n\"unlatch\",\n\"unlath\",\n\"unlaugh\",\n\"unlaved\",\n\"unlaw\",\n\"unlawed\",\n\"unlawly\",\n\"unlay\",\n\"unlead\",\n\"unleaf\",\n\"unleaky\",\n\"unleal\",\n\"unlean\",\n\"unlearn\",\n\"unleash\",\n\"unleave\",\n\"unled\",\n\"unleft\",\n\"unlegal\",\n\"unlent\",\n\"unless\",\n\"unlet\",\n\"unlevel\",\n\"unlid\",\n\"unlie\",\n\"unlight\",\n\"unlike\",\n\"unliked\",\n\"unliken\",\n\"unlimb\",\n\"unlime\",\n\"unlimed\",\n\"unlimp\",\n\"unline\",\n\"unlined\",\n\"unlink\",\n\"unlist\",\n\"unlisty\",\n\"unlit\",\n\"unlive\",\n\"unload\",\n\"unloath\",\n\"unlobed\",\n\"unlocal\",\n\"unlock\",\n\"unlodge\",\n\"unlofty\",\n\"unlogic\",\n\"unlook\",\n\"unloop\",\n\"unloose\",\n\"unlord\",\n\"unlost\",\n\"unlousy\",\n\"unlove\",\n\"unloved\",\n\"unlowly\",\n\"unloyal\",\n\"unlucid\",\n\"unluck\",\n\"unlucky\",\n\"unlunar\",\n\"unlured\",\n\"unlust\",\n\"unlusty\",\n\"unlute\",\n\"unluted\",\n\"unlying\",\n\"unmad\",\n\"unmade\",\n\"unmagic\",\n\"unmaid\",\n\"unmail\",\n\"unmake\",\n\"unmaker\",\n\"unman\",\n\"unmaned\",\n\"unmanly\",\n\"unmarch\",\n\"unmarry\",\n\"unmask\",\n\"unmast\",\n\"unmate\",\n\"unmated\",\n\"unmaze\",\n\"unmeant\",\n\"unmeek\",\n\"unmeet\",\n\"unmerge\",\n\"unmerry\",\n\"unmesh\",\n\"unmet\",\n\"unmeted\",\n\"unmew\",\n\"unmewed\",\n\"unmind\",\n\"unmined\",\n\"unmired\",\n\"unmiry\",\n\"unmist\",\n\"unmiter\",\n\"unmix\",\n\"unmixed\",\n\"unmodel\",\n\"unmoist\",\n\"unmold\",\n\"unmoldy\",\n\"unmoor\",\n\"unmoral\",\n\"unmount\",\n\"unmoved\",\n\"unmowed\",\n\"unmown\",\n\"unmuddy\",\n\"unmuted\",\n\"unnail\",\n\"unnaked\",\n\"unname\",\n\"unnamed\",\n\"unneat\",\n\"unneedy\",\n\"unnegro\",\n\"unnerve\",\n\"unnest\",\n\"unneth\",\n\"unnethe\",\n\"unnew\",\n\"unnewly\",\n\"unnice\",\n\"unnigh\",\n\"unnoble\",\n\"unnobly\",\n\"unnose\",\n\"unnosed\",\n\"unnoted\",\n\"unnovel\",\n\"unoared\",\n\"unobese\",\n\"unode\",\n\"unoften\",\n\"unogled\",\n\"unoil\",\n\"unoiled\",\n\"unoily\",\n\"unold\",\n\"unoped\",\n\"unopen\",\n\"unorbed\",\n\"unorder\",\n\"unorn\",\n\"unornly\",\n\"unovert\",\n\"unowed\",\n\"unowing\",\n\"unown\",\n\"unowned\",\n\"unpaced\",\n\"unpack\",\n\"unpagan\",\n\"unpaged\",\n\"unpaid\",\n\"unpaint\",\n\"unpale\",\n\"unpaled\",\n\"unpanel\",\n\"unpapal\",\n\"unpaper\",\n\"unparch\",\n\"unpared\",\n\"unpark\",\n\"unparty\",\n\"unpass\",\n\"unpaste\",\n\"unpave\",\n\"unpaved\",\n\"unpawed\",\n\"unpawn\",\n\"unpeace\",\n\"unpeel\",\n\"unpeg\",\n\"unpen\",\n\"unpenal\",\n\"unpent\",\n\"unperch\",\n\"unpetal\",\n\"unpick\",\n\"unpiece\",\n\"unpiety\",\n\"unpile\",\n\"unpiled\",\n\"unpin\",\n\"unpious\",\n\"unpiped\",\n\"unplace\",\n\"unplaid\",\n\"unplain\",\n\"unplait\",\n\"unplan\",\n\"unplank\",\n\"unplant\",\n\"unplat\",\n\"unpleat\",\n\"unplied\",\n\"unplow\",\n\"unplug\",\n\"unplumb\",\n\"unplume\",\n\"unplump\",\n\"unpoise\",\n\"unpoled\",\n\"unpope\",\n\"unposed\",\n\"unpot\",\n\"unpower\",\n\"unpray\",\n\"unprim\",\n\"unprime\",\n\"unprint\",\n\"unprop\",\n\"unproud\",\n\"unpure\",\n\"unpurse\",\n\"unput\",\n\"unqueen\",\n\"unquick\",\n\"unquiet\",\n\"unquit\",\n\"unquote\",\n\"unraced\",\n\"unrack\",\n\"unrainy\",\n\"unrake\",\n\"unraked\",\n\"unram\",\n\"unrank\",\n\"unraped\",\n\"unrare\",\n\"unrash\",\n\"unrated\",\n\"unravel\",\n\"unray\",\n\"unrayed\",\n\"unrazed\",\n\"unread\",\n\"unready\",\n\"unreal\",\n\"unreave\",\n\"unrebel\",\n\"unred\",\n\"unreel\",\n\"unreeve\",\n\"unregal\",\n\"unrein\",\n\"unrent\",\n\"unrest\",\n\"unresty\",\n\"unrhyme\",\n\"unrich\",\n\"unricht\",\n\"unrid\",\n\"unride\",\n\"unrife\",\n\"unrig\",\n\"unright\",\n\"unrigid\",\n\"unrind\",\n\"unring\",\n\"unrip\",\n\"unripe\",\n\"unriped\",\n\"unrisen\",\n\"unrisky\",\n\"unrived\",\n\"unriven\",\n\"unrivet\",\n\"unroast\",\n\"unrobe\",\n\"unrobed\",\n\"unroll\",\n\"unroof\",\n\"unroomy\",\n\"unroost\",\n\"unroot\",\n\"unrope\",\n\"unroped\",\n\"unrosed\",\n\"unroted\",\n\"unrough\",\n\"unround\",\n\"unrove\",\n\"unroved\",\n\"unrow\",\n\"unrowed\",\n\"unroyal\",\n\"unrule\",\n\"unruled\",\n\"unruly\",\n\"unrun\",\n\"unrung\",\n\"unrural\",\n\"unrust\",\n\"unruth\",\n\"unsack\",\n\"unsad\",\n\"unsafe\",\n\"unsage\",\n\"unsaid\",\n\"unsaint\",\n\"unsalt\",\n\"unsane\",\n\"unsappy\",\n\"unsash\",\n\"unsated\",\n\"unsatin\",\n\"unsaved\",\n\"unsawed\",\n\"unsawn\",\n\"unsay\",\n\"unscale\",\n\"unscaly\",\n\"unscarb\",\n\"unscent\",\n\"unscrew\",\n\"unseal\",\n\"unseam\",\n\"unseat\",\n\"unsee\",\n\"unseen\",\n\"unself\",\n\"unsense\",\n\"unsent\",\n\"unset\",\n\"unsew\",\n\"unsewed\",\n\"unsewn\",\n\"unsex\",\n\"unsexed\",\n\"unshade\",\n\"unshady\",\n\"unshape\",\n\"unsharp\",\n\"unshawl\",\n\"unsheaf\",\n\"unshed\",\n\"unsheet\",\n\"unshell\",\n\"unship\",\n\"unshod\",\n\"unshoe\",\n\"unshoed\",\n\"unshop\",\n\"unshore\",\n\"unshorn\",\n\"unshort\",\n\"unshot\",\n\"unshown\",\n\"unshowy\",\n\"unshrew\",\n\"unshut\",\n\"unshy\",\n\"unshyly\",\n\"unsick\",\n\"unsided\",\n\"unsiege\",\n\"unsight\",\n\"unsilly\",\n\"unsin\",\n\"unsinew\",\n\"unsing\",\n\"unsized\",\n\"unskin\",\n\"unslack\",\n\"unslain\",\n\"unslate\",\n\"unslave\",\n\"unsleek\",\n\"unslept\",\n\"unsling\",\n\"unslip\",\n\"unslit\",\n\"unslot\",\n\"unslow\",\n\"unslung\",\n\"unsly\",\n\"unsmart\",\n\"unsmoky\",\n\"unsmote\",\n\"unsnaky\",\n\"unsnap\",\n\"unsnare\",\n\"unsnarl\",\n\"unsneck\",\n\"unsnib\",\n\"unsnow\",\n\"unsober\",\n\"unsoft\",\n\"unsoggy\",\n\"unsoil\",\n\"unsolar\",\n\"unsold\",\n\"unsole\",\n\"unsoled\",\n\"unsolid\",\n\"unsome\",\n\"unson\",\n\"unsonsy\",\n\"unsooty\",\n\"unsore\",\n\"unsorry\",\n\"unsort\",\n\"unsoul\",\n\"unsound\",\n\"unsour\",\n\"unsowed\",\n\"unsown\",\n\"unspan\",\n\"unspar\",\n\"unspeak\",\n\"unsped\",\n\"unspeed\",\n\"unspell\",\n\"unspelt\",\n\"unspent\",\n\"unspicy\",\n\"unspied\",\n\"unspike\",\n\"unspin\",\n\"unspit\",\n\"unsplit\",\n\"unspoil\",\n\"unspot\",\n\"unspun\",\n\"unstack\",\n\"unstagy\",\n\"unstaid\",\n\"unstain\",\n\"unstar\",\n\"unstate\",\n\"unsteck\",\n\"unsteel\",\n\"unsteep\",\n\"unstep\",\n\"unstern\",\n\"unstick\",\n\"unstill\",\n\"unsting\",\n\"unstock\",\n\"unstoic\",\n\"unstone\",\n\"unstony\",\n\"unstop\",\n\"unstore\",\n\"unstout\",\n\"unstow\",\n\"unstrap\",\n\"unstrip\",\n\"unstuck\",\n\"unstuff\",\n\"unstung\",\n\"unsty\",\n\"unsued\",\n\"unsuit\",\n\"unsulky\",\n\"unsun\",\n\"unsung\",\n\"unsunk\",\n\"unsunny\",\n\"unsure\",\n\"unswear\",\n\"unsweat\",\n\"unsweet\",\n\"unswell\",\n\"unswept\",\n\"unswing\",\n\"unsworn\",\n\"unswung\",\n\"untack\",\n\"untaint\",\n\"untaken\",\n\"untall\",\n\"untame\",\n\"untamed\",\n\"untap\",\n\"untaped\",\n\"untar\",\n\"untaste\",\n\"untasty\",\n\"untaut\",\n\"untawed\",\n\"untax\",\n\"untaxed\",\n\"unteach\",\n\"unteam\",\n\"unteem\",\n\"untell\",\n\"untense\",\n\"untent\",\n\"untenty\",\n\"untewed\",\n\"unthank\",\n\"unthaw\",\n\"unthick\",\n\"unthink\",\n\"unthorn\",\n\"unthrid\",\n\"unthrob\",\n\"untidal\",\n\"untidy\",\n\"untie\",\n\"untied\",\n\"untight\",\n\"until\",\n\"untile\",\n\"untiled\",\n\"untill\",\n\"untilt\",\n\"untimed\",\n\"untin\",\n\"untinct\",\n\"untine\",\n\"untipt\",\n\"untire\",\n\"untired\",\n\"unto\",\n\"untold\",\n\"untomb\",\n\"untone\",\n\"untoned\",\n\"untooth\",\n\"untop\",\n\"untorn\",\n\"untouch\",\n\"untough\",\n\"untown\",\n\"untrace\",\n\"untrain\",\n\"untread\",\n\"untreed\",\n\"untress\",\n\"untried\",\n\"untrig\",\n\"untrill\",\n\"untrim\",\n\"untripe\",\n\"untrite\",\n\"untrod\",\n\"untruck\",\n\"untrue\",\n\"untruly\",\n\"untruss\",\n\"untrust\",\n\"untruth\",\n\"untuck\",\n\"untumid\",\n\"untune\",\n\"untuned\",\n\"unturf\",\n\"unturn\",\n\"untwine\",\n\"untwirl\",\n\"untwist\",\n\"untying\",\n\"untz\",\n\"unugly\",\n\"unultra\",\n\"unupset\",\n\"unurban\",\n\"unurged\",\n\"unurn\",\n\"unurned\",\n\"unuse\",\n\"unused\",\n\"unusual\",\n\"unvain\",\n\"unvalid\",\n\"unvalue\",\n\"unveil\",\n\"unvenom\",\n\"unvest\",\n\"unvexed\",\n\"unvicar\",\n\"unvisor\",\n\"unvital\",\n\"unvivid\",\n\"unvocal\",\n\"unvoice\",\n\"unvote\",\n\"unvoted\",\n\"unvowed\",\n\"unwaded\",\n\"unwaged\",\n\"unwaked\",\n\"unwall\",\n\"unwan\",\n\"unware\",\n\"unwarm\",\n\"unwarn\",\n\"unwarp\",\n\"unwary\",\n\"unwater\",\n\"unwaved\",\n\"unwax\",\n\"unwaxed\",\n\"unwayed\",\n\"unweal\",\n\"unweary\",\n\"unweave\",\n\"unweb\",\n\"unwed\",\n\"unwedge\",\n\"unweel\",\n\"unweft\",\n\"unweld\",\n\"unwell\",\n\"unwept\",\n\"unwet\",\n\"unwheel\",\n\"unwhig\",\n\"unwhip\",\n\"unwhite\",\n\"unwield\",\n\"unwifed\",\n\"unwig\",\n\"unwild\",\n\"unwill\",\n\"unwily\",\n\"unwind\",\n\"unwindy\",\n\"unwiped\",\n\"unwire\",\n\"unwired\",\n\"unwise\",\n\"unwish\",\n\"unwist\",\n\"unwitch\",\n\"unwitty\",\n\"unwive\",\n\"unwived\",\n\"unwoful\",\n\"unwoman\",\n\"unwomb\",\n\"unwon\",\n\"unwooed\",\n\"unwoof\",\n\"unwooly\",\n\"unwordy\",\n\"unwork\",\n\"unworld\",\n\"unwormy\",\n\"unworn\",\n\"unworth\",\n\"unwound\",\n\"unwoven\",\n\"unwrap\",\n\"unwrit\",\n\"unwrite\",\n\"unwrung\",\n\"unyoke\",\n\"unyoked\",\n\"unyoung\",\n\"unze\",\n\"unzen\",\n\"unzone\",\n\"unzoned\",\n\"up\",\n\"upaisle\",\n\"upalley\",\n\"upalong\",\n\"uparch\",\n\"uparise\",\n\"uparm\",\n\"uparna\",\n\"upas\",\n\"upattic\",\n\"upbank\",\n\"upbar\",\n\"upbay\",\n\"upbear\",\n\"upbeat\",\n\"upbelch\",\n\"upbelt\",\n\"upbend\",\n\"upbid\",\n\"upbind\",\n\"upblast\",\n\"upblaze\",\n\"upblow\",\n\"upboil\",\n\"upbolt\",\n\"upboost\",\n\"upborne\",\n\"upbotch\",\n\"upbound\",\n\"upbrace\",\n\"upbraid\",\n\"upbray\",\n\"upbreak\",\n\"upbred\",\n\"upbreed\",\n\"upbrim\",\n\"upbring\",\n\"upbrook\",\n\"upbrow\",\n\"upbuild\",\n\"upbuoy\",\n\"upburn\",\n\"upburst\",\n\"upbuy\",\n\"upcall\",\n\"upcanal\",\n\"upcarry\",\n\"upcast\",\n\"upcatch\",\n\"upchoke\",\n\"upchuck\",\n\"upcity\",\n\"upclimb\",\n\"upclose\",\n\"upcoast\",\n\"upcock\",\n\"upcoil\",\n\"upcome\",\n\"upcover\",\n\"upcrane\",\n\"upcrawl\",\n\"upcreek\",\n\"upcreep\",\n\"upcrop\",\n\"upcrowd\",\n\"upcry\",\n\"upcurl\",\n\"upcurve\",\n\"upcut\",\n\"updart\",\n\"update\",\n\"updeck\",\n\"updelve\",\n\"updive\",\n\"updo\",\n\"updome\",\n\"updraft\",\n\"updrag\",\n\"updraw\",\n\"updrink\",\n\"updry\",\n\"upeat\",\n\"upend\",\n\"upeygan\",\n\"upfeed\",\n\"upfield\",\n\"upfill\",\n\"upflame\",\n\"upflare\",\n\"upflash\",\n\"upflee\",\n\"upfling\",\n\"upfloat\",\n\"upflood\",\n\"upflow\",\n\"upflung\",\n\"upfly\",\n\"upfold\",\n\"upframe\",\n\"upfurl\",\n\"upgale\",\n\"upgang\",\n\"upgape\",\n\"upgaze\",\n\"upget\",\n\"upgird\",\n\"upgirt\",\n\"upgive\",\n\"upglean\",\n\"upglide\",\n\"upgo\",\n\"upgorge\",\n\"upgrade\",\n\"upgrave\",\n\"upgrow\",\n\"upgully\",\n\"upgush\",\n\"uphand\",\n\"uphang\",\n\"uphasp\",\n\"upheal\",\n\"upheap\",\n\"upheave\",\n\"upheld\",\n\"uphelm\",\n\"uphelya\",\n\"upher\",\n\"uphill\",\n\"uphoard\",\n\"uphoist\",\n\"uphold\",\n\"uphung\",\n\"uphurl\",\n\"upjerk\",\n\"upjet\",\n\"upkeep\",\n\"upknell\",\n\"upknit\",\n\"upla\",\n\"uplaid\",\n\"uplake\",\n\"upland\",\n\"uplane\",\n\"uplay\",\n\"uplead\",\n\"upleap\",\n\"upleg\",\n\"uplick\",\n\"uplift\",\n\"uplight\",\n\"uplimb\",\n\"upline\",\n\"uplock\",\n\"uplong\",\n\"uplook\",\n\"uploom\",\n\"uploop\",\n\"uplying\",\n\"upmast\",\n\"upmix\",\n\"upmost\",\n\"upmount\",\n\"upmove\",\n\"upness\",\n\"upo\",\n\"upon\",\n\"uppard\",\n\"uppent\",\n\"upper\",\n\"upperch\",\n\"upperer\",\n\"uppers\",\n\"uppile\",\n\"upping\",\n\"uppish\",\n\"uppity\",\n\"upplow\",\n\"uppluck\",\n\"uppoint\",\n\"uppoise\",\n\"uppop\",\n\"uppour\",\n\"uppowoc\",\n\"upprick\",\n\"upprop\",\n\"uppuff\",\n\"uppull\",\n\"uppush\",\n\"upraise\",\n\"upreach\",\n\"uprear\",\n\"uprein\",\n\"uprend\",\n\"uprest\",\n\"uprid\",\n\"upridge\",\n\"upright\",\n\"uprip\",\n\"uprisal\",\n\"uprise\",\n\"uprisen\",\n\"upriser\",\n\"uprist\",\n\"uprive\",\n\"upriver\",\n\"uproad\",\n\"uproar\",\n\"uproom\",\n\"uproot\",\n\"uprose\",\n\"uprouse\",\n\"uproute\",\n\"uprun\",\n\"uprush\",\n\"upscale\",\n\"upscrew\",\n\"upseal\",\n\"upseek\",\n\"upseize\",\n\"upsend\",\n\"upset\",\n\"upsey\",\n\"upshaft\",\n\"upshear\",\n\"upshoot\",\n\"upshore\",\n\"upshot\",\n\"upshove\",\n\"upshut\",\n\"upside\",\n\"upsides\",\n\"upsilon\",\n\"upsit\",\n\"upslant\",\n\"upslip\",\n\"upslope\",\n\"upsmite\",\n\"upsoak\",\n\"upsoar\",\n\"upsolve\",\n\"upspeak\",\n\"upspear\",\n\"upspeed\",\n\"upspew\",\n\"upspin\",\n\"upspire\",\n\"upspout\",\n\"upspurt\",\n\"upstaff\",\n\"upstage\",\n\"upstair\",\n\"upstamp\",\n\"upstand\",\n\"upstare\",\n\"upstart\",\n\"upstate\",\n\"upstay\",\n\"upsteal\",\n\"upsteam\",\n\"upstem\",\n\"upstep\",\n\"upstick\",\n\"upstir\",\n\"upsuck\",\n\"upsun\",\n\"upsup\",\n\"upsurge\",\n\"upswarm\",\n\"upsway\",\n\"upsweep\",\n\"upswell\",\n\"upswing\",\n\"uptable\",\n\"uptake\",\n\"uptaker\",\n\"uptear\",\n\"uptend\",\n\"upthrow\",\n\"uptide\",\n\"uptie\",\n\"uptill\",\n\"uptilt\",\n\"uptorn\",\n\"uptoss\",\n\"uptower\",\n\"uptown\",\n\"uptrace\",\n\"uptrack\",\n\"uptrail\",\n\"uptrain\",\n\"uptree\",\n\"uptrend\",\n\"uptrill\",\n\"uptrunk\",\n\"uptruss\",\n\"uptube\",\n\"uptuck\",\n\"upturn\",\n\"uptwist\",\n\"upupoid\",\n\"upvomit\",\n\"upwaft\",\n\"upwall\",\n\"upward\",\n\"upwards\",\n\"upwarp\",\n\"upwax\",\n\"upway\",\n\"upways\",\n\"upwell\",\n\"upwent\",\n\"upwheel\",\n\"upwhelm\",\n\"upwhir\",\n\"upwhirl\",\n\"upwind\",\n\"upwith\",\n\"upwork\",\n\"upwound\",\n\"upwrap\",\n\"upwring\",\n\"upyard\",\n\"upyoke\",\n\"ur\",\n\"ura\",\n\"urachal\",\n\"urachus\",\n\"uracil\",\n\"uraemic\",\n\"uraeus\",\n\"ural\",\n\"urali\",\n\"uraline\",\n\"uralite\",\n\"uralium\",\n\"uramido\",\n\"uramil\",\n\"uramino\",\n\"uran\",\n\"uranate\",\n\"uranic\",\n\"uraniid\",\n\"uranin\",\n\"uranine\",\n\"uranion\",\n\"uranism\",\n\"uranist\",\n\"uranite\",\n\"uranium\",\n\"uranous\",\n\"uranyl\",\n\"urao\",\n\"urare\",\n\"urari\",\n\"urase\",\n\"urate\",\n\"uratic\",\n\"uratoma\",\n\"urazine\",\n\"urazole\",\n\"urban\",\n\"urbane\",\n\"urbian\",\n\"urbic\",\n\"urbify\",\n\"urceole\",\n\"urceoli\",\n\"urceus\",\n\"urchin\",\n\"urd\",\n\"urde\",\n\"urdee\",\n\"ure\",\n\"urea\",\n\"ureal\",\n\"urease\",\n\"uredema\",\n\"uredine\",\n\"uredo\",\n\"ureic\",\n\"ureid\",\n\"ureide\",\n\"ureido\",\n\"uremia\",\n\"uremic\",\n\"urent\",\n\"uresis\",\n\"uretal\",\n\"ureter\",\n\"urethan\",\n\"urethra\",\n\"uretic\",\n\"urf\",\n\"urge\",\n\"urgence\",\n\"urgency\",\n\"urgent\",\n\"urger\",\n\"urging\",\n\"urheen\",\n\"urial\",\n\"uric\",\n\"urinal\",\n\"urinant\",\n\"urinary\",\n\"urinate\",\n\"urine\",\n\"urinose\",\n\"urinous\",\n\"urite\",\n\"urlar\",\n\"urled\",\n\"urling\",\n\"urluch\",\n\"urman\",\n\"urn\",\n\"urna\",\n\"urnae\",\n\"urnal\",\n\"urnful\",\n\"urning\",\n\"urnism\",\n\"urnlike\",\n\"urocele\",\n\"urocyst\",\n\"urodele\",\n\"urogram\",\n\"urohyal\",\n\"urolith\",\n\"urology\",\n\"uromere\",\n\"uronic\",\n\"uropod\",\n\"urosis\",\n\"urosome\",\n\"urostea\",\n\"urotoxy\",\n\"uroxin\",\n\"ursal\",\n\"ursine\",\n\"ursoid\",\n\"ursolic\",\n\"urson\",\n\"ursone\",\n\"ursuk\",\n\"urtica\",\n\"urtite\",\n\"urubu\",\n\"urucu\",\n\"urucuri\",\n\"uruisg\",\n\"urunday\",\n\"urus\",\n\"urushi\",\n\"urushic\",\n\"urva\",\n\"us\",\n\"usable\",\n\"usage\",\n\"usager\",\n\"usance\",\n\"usar\",\n\"usara\",\n\"usaron\",\n\"usation\",\n\"use\",\n\"used\",\n\"usedly\",\n\"usednt\",\n\"usee\",\n\"useful\",\n\"usehold\",\n\"useless\",\n\"usent\",\n\"user\",\n\"ush\",\n\"ushabti\",\n\"usher\",\n\"usherer\",\n\"usings\",\n\"usitate\",\n\"usnea\",\n\"usneoid\",\n\"usnic\",\n\"usninic\",\n\"usque\",\n\"usself\",\n\"ussels\",\n\"ust\",\n\"uster\",\n\"ustion\",\n\"usual\",\n\"usually\",\n\"usuary\",\n\"usucapt\",\n\"usure\",\n\"usurer\",\n\"usuress\",\n\"usurp\",\n\"usurper\",\n\"usurpor\",\n\"usury\",\n\"usward\",\n\"uswards\",\n\"ut\",\n\"uta\",\n\"utahite\",\n\"utai\",\n\"utas\",\n\"utch\",\n\"utchy\",\n\"utees\",\n\"utensil\",\n\"uteri\",\n\"uterine\",\n\"uterus\",\n\"utick\",\n\"utile\",\n\"utility\",\n\"utilize\",\n\"utinam\",\n\"utmost\",\n\"utopia\",\n\"utopian\",\n\"utopism\",\n\"utopist\",\n\"utricle\",\n\"utricul\",\n\"utrubi\",\n\"utrum\",\n\"utsuk\",\n\"utter\",\n\"utterer\",\n\"utterly\",\n\"utu\",\n\"utum\",\n\"uva\",\n\"uval\",\n\"uvalha\",\n\"uvanite\",\n\"uvate\",\n\"uvea\",\n\"uveal\",\n\"uveitic\",\n\"uveitis\",\n\"uveous\",\n\"uvic\",\n\"uvid\",\n\"uviol\",\n\"uvitic\",\n\"uvito\",\n\"uvrou\",\n\"uvula\",\n\"uvulae\",\n\"uvular\",\n\"uvver\",\n\"uxorial\",\n\"uzan\",\n\"uzara\",\n\"uzarin\",\n\"uzaron\",\n\"v\",\n\"vaagmer\",\n\"vaalite\",\n\"vacancy\",\n\"vacant\",\n\"vacate\",\n\"vacatur\",\n\"vaccary\",\n\"vaccina\",\n\"vaccine\",\n\"vache\",\n\"vacoa\",\n\"vacona\",\n\"vacoua\",\n\"vacouf\",\n\"vacual\",\n\"vacuate\",\n\"vacuefy\",\n\"vacuist\",\n\"vacuity\",\n\"vacuole\",\n\"vacuome\",\n\"vacuous\",\n\"vacuum\",\n\"vacuuma\",\n\"vade\",\n\"vadium\",\n\"vadose\",\n\"vady\",\n\"vag\",\n\"vagal\",\n\"vagary\",\n\"vagas\",\n\"vage\",\n\"vagile\",\n\"vagina\",\n\"vaginal\",\n\"vagitus\",\n\"vagrant\",\n\"vagrate\",\n\"vagrom\",\n\"vague\",\n\"vaguely\",\n\"vaguish\",\n\"vaguity\",\n\"vagus\",\n\"vahine\",\n\"vail\",\n\"vain\",\n\"vainful\",\n\"vainly\",\n\"vair\",\n\"vairagi\",\n\"vaire\",\n\"vairy\",\n\"vaivode\",\n\"vajra\",\n\"vakass\",\n\"vakia\",\n\"vakil\",\n\"valance\",\n\"vale\",\n\"valence\",\n\"valency\",\n\"valent\",\n\"valeral\",\n\"valeric\",\n\"valerin\",\n\"valeryl\",\n\"valet\",\n\"valeta\",\n\"valetry\",\n\"valeur\",\n\"valgoid\",\n\"valgus\",\n\"valhall\",\n\"vali\",\n\"valiant\",\n\"valid\",\n\"validly\",\n\"valine\",\n\"valise\",\n\"vall\",\n\"vallar\",\n\"vallary\",\n\"vallate\",\n\"valley\",\n\"vallis\",\n\"vallum\",\n\"valonia\",\n\"valor\",\n\"valse\",\n\"valsoid\",\n\"valuate\",\n\"value\",\n\"valued\",\n\"valuer\",\n\"valuta\",\n\"valva\",\n\"valval\",\n\"valvate\",\n\"valve\",\n\"valved\",\n\"valvula\",\n\"valvule\",\n\"valyl\",\n\"vamfont\",\n\"vamoose\",\n\"vamp\",\n\"vamped\",\n\"vamper\",\n\"vampire\",\n\"van\",\n\"vanadic\",\n\"vanadyl\",\n\"vane\",\n\"vaned\",\n\"vanfoss\",\n\"vang\",\n\"vangee\",\n\"vangeli\",\n\"vanglo\",\n\"vanilla\",\n\"vanille\",\n\"vanish\",\n\"vanity\",\n\"vanman\",\n\"vanmost\",\n\"vanner\",\n\"vannet\",\n\"vansire\",\n\"vantage\",\n\"vanward\",\n\"vapid\",\n\"vapidly\",\n\"vapor\",\n\"vapored\",\n\"vaporer\",\n\"vapory\",\n\"vara\",\n\"varahan\",\n\"varan\",\n\"varanid\",\n\"vardy\",\n\"vare\",\n\"varec\",\n\"vareuse\",\n\"vari\",\n\"variant\",\n\"variate\",\n\"varical\",\n\"varices\",\n\"varied\",\n\"varier\",\n\"variety\",\n\"variola\",\n\"variole\",\n\"various\",\n\"varisse\",\n\"varix\",\n\"varlet\",\n\"varment\",\n\"varna\",\n\"varnish\",\n\"varsha\",\n\"varsity\",\n\"varus\",\n\"varve\",\n\"varved\",\n\"vary\",\n\"vas\",\n\"vasa\",\n\"vasal\",\n\"vase\",\n\"vaseful\",\n\"vaselet\",\n\"vassal\",\n\"vast\",\n\"vastate\",\n\"vastily\",\n\"vastity\",\n\"vastly\",\n\"vasty\",\n\"vasu\",\n\"vat\",\n\"vatful\",\n\"vatic\",\n\"vatman\",\n\"vatter\",\n\"vau\",\n\"vaudy\",\n\"vault\",\n\"vaulted\",\n\"vaulter\",\n\"vaulty\",\n\"vaunt\",\n\"vaunted\",\n\"vaunter\",\n\"vaunty\",\n\"vauxite\",\n\"vavasor\",\n\"vaward\",\n\"veal\",\n\"vealer\",\n\"vealy\",\n\"vection\",\n\"vectis\",\n\"vector\",\n\"vecture\",\n\"vedana\",\n\"vedette\",\n\"vedika\",\n\"vedro\",\n\"veduis\",\n\"vee\",\n\"veen\",\n\"veep\",\n\"veer\",\n\"veery\",\n\"vegetal\",\n\"vegete\",\n\"vehicle\",\n\"vei\",\n\"veigle\",\n\"veil\",\n\"veiled\",\n\"veiler\",\n\"veiling\",\n\"veily\",\n\"vein\",\n\"veinage\",\n\"veinal\",\n\"veined\",\n\"veiner\",\n\"veinery\",\n\"veining\",\n\"veinlet\",\n\"veinous\",\n\"veinule\",\n\"veiny\",\n\"vejoces\",\n\"vela\",\n\"velal\",\n\"velamen\",\n\"velar\",\n\"velaric\",\n\"velary\",\n\"velate\",\n\"velated\",\n\"veldman\",\n\"veldt\",\n\"velic\",\n\"veliger\",\n\"vell\",\n\"vellala\",\n\"velleda\",\n\"vellon\",\n\"vellum\",\n\"vellumy\",\n\"velo\",\n\"velours\",\n\"velte\",\n\"velum\",\n\"velumen\",\n\"velure\",\n\"velvet\",\n\"velvety\",\n\"venada\",\n\"venal\",\n\"venally\",\n\"venatic\",\n\"venator\",\n\"vencola\",\n\"vend\",\n\"vendace\",\n\"vendee\",\n\"vender\",\n\"vending\",\n\"vendor\",\n\"vendue\",\n\"veneer\",\n\"venene\",\n\"veneral\",\n\"venerer\",\n\"venery\",\n\"venesia\",\n\"venger\",\n\"venial\",\n\"venie\",\n\"venin\",\n\"venison\",\n\"vennel\",\n\"venner\",\n\"venom\",\n\"venomed\",\n\"venomer\",\n\"venomly\",\n\"venomy\",\n\"venosal\",\n\"venose\",\n\"venous\",\n\"vent\",\n\"ventage\",\n\"ventail\",\n\"venter\",\n\"ventil\",\n\"ventose\",\n\"ventrad\",\n\"ventral\",\n\"ventric\",\n\"venture\",\n\"venue\",\n\"venula\",\n\"venular\",\n\"venule\",\n\"venust\",\n\"vera\",\n\"veranda\",\n\"verb\",\n\"verbal\",\n\"verbate\",\n\"verbena\",\n\"verbene\",\n\"verbid\",\n\"verbify\",\n\"verbile\",\n\"verbose\",\n\"verbous\",\n\"verby\",\n\"verchok\",\n\"verd\",\n\"verdant\",\n\"verdea\",\n\"verdet\",\n\"verdict\",\n\"verdin\",\n\"verdoy\",\n\"verdun\",\n\"verdure\",\n\"verek\",\n\"verge\",\n\"vergent\",\n\"verger\",\n\"vergery\",\n\"vergi\",\n\"verglas\",\n\"veri\",\n\"veridic\",\n\"verify\",\n\"verily\",\n\"verine\",\n\"verism\",\n\"verist\",\n\"verite\",\n\"verity\",\n\"vermeil\",\n\"vermian\",\n\"vermin\",\n\"verminy\",\n\"vermis\",\n\"vermix\",\n\"vernal\",\n\"vernant\",\n\"vernier\",\n\"vernile\",\n\"vernin\",\n\"vernine\",\n\"verre\",\n\"verrel\",\n\"verruca\",\n\"verruga\",\n\"versal\",\n\"versant\",\n\"versate\",\n\"verse\",\n\"versed\",\n\"verser\",\n\"verset\",\n\"versify\",\n\"versine\",\n\"version\",\n\"verso\",\n\"versor\",\n\"verst\",\n\"versta\",\n\"versual\",\n\"versus\",\n\"vert\",\n\"vertex\",\n\"vertigo\",\n\"veruled\",\n\"vervain\",\n\"verve\",\n\"vervel\",\n\"vervet\",\n\"very\",\n\"vesania\",\n\"vesanic\",\n\"vesbite\",\n\"vesicae\",\n\"vesical\",\n\"vesicle\",\n\"veskit\",\n\"vespal\",\n\"vesper\",\n\"vespers\",\n\"vespery\",\n\"vespid\",\n\"vespine\",\n\"vespoid\",\n\"vessel\",\n\"vest\",\n\"vestal\",\n\"vestee\",\n\"vester\",\n\"vestige\",\n\"vesting\",\n\"vestlet\",\n\"vestral\",\n\"vestry\",\n\"vesture\",\n\"vet\",\n\"veta\",\n\"vetanda\",\n\"vetch\",\n\"vetchy\",\n\"veteran\",\n\"vetiver\",\n\"veto\",\n\"vetoer\",\n\"vetoism\",\n\"vetoist\",\n\"vetust\",\n\"vetusty\",\n\"veuve\",\n\"vex\",\n\"vexable\",\n\"vexed\",\n\"vexedly\",\n\"vexer\",\n\"vexful\",\n\"vexil\",\n\"vext\",\n\"via\",\n\"viable\",\n\"viaduct\",\n\"viagram\",\n\"viajaca\",\n\"vial\",\n\"vialful\",\n\"viand\",\n\"viander\",\n\"viatic\",\n\"viatica\",\n\"viator\",\n\"vibex\",\n\"vibgyor\",\n\"vibix\",\n\"vibrant\",\n\"vibrate\",\n\"vibrato\",\n\"vibrion\",\n\"vicar\",\n\"vicarly\",\n\"vice\",\n\"viceroy\",\n\"vicety\",\n\"vicilin\",\n\"vicinal\",\n\"vicine\",\n\"vicious\",\n\"vicoite\",\n\"victim\",\n\"victor\",\n\"victory\",\n\"victrix\",\n\"victual\",\n\"vicuna\",\n\"viddui\",\n\"video\",\n\"vidette\",\n\"vidonia\",\n\"vidry\",\n\"viduage\",\n\"vidual\",\n\"viduate\",\n\"viduine\",\n\"viduity\",\n\"viduous\",\n\"vidya\",\n\"vie\",\n\"vielle\",\n\"vier\",\n\"viertel\",\n\"view\",\n\"viewer\",\n\"viewly\",\n\"viewy\",\n\"vifda\",\n\"viga\",\n\"vigia\",\n\"vigil\",\n\"vignin\",\n\"vigonia\",\n\"vigor\",\n\"vihara\",\n\"vihuela\",\n\"vijao\",\n\"viking\",\n\"vila\",\n\"vilayet\",\n\"vile\",\n\"vilely\",\n\"vilify\",\n\"vility\",\n\"vill\",\n\"villa\",\n\"village\",\n\"villain\",\n\"villar\",\n\"villate\",\n\"ville\",\n\"villein\",\n\"villoid\",\n\"villose\",\n\"villous\",\n\"villus\",\n\"vim\",\n\"vimana\",\n\"vimen\",\n\"vimful\",\n\"viminal\",\n\"vina\",\n\"vinage\",\n\"vinal\",\n\"vinasse\",\n\"vinata\",\n\"vincent\",\n\"vindex\",\n\"vine\",\n\"vinea\",\n\"vineal\",\n\"vined\",\n\"vinegar\",\n\"vineity\",\n\"vinelet\",\n\"viner\",\n\"vinery\",\n\"vinic\",\n\"vinny\",\n\"vino\",\n\"vinose\",\n\"vinous\",\n\"vint\",\n\"vinta\",\n\"vintage\",\n\"vintem\",\n\"vintner\",\n\"vintry\",\n\"viny\",\n\"vinyl\",\n\"vinylic\",\n\"viol\",\n\"viola\",\n\"violal\",\n\"violate\",\n\"violent\",\n\"violer\",\n\"violet\",\n\"violety\",\n\"violin\",\n\"violina\",\n\"violine\",\n\"violist\",\n\"violon\",\n\"violone\",\n\"viper\",\n\"viperan\",\n\"viperid\",\n\"vipery\",\n\"viqueen\",\n\"viragin\",\n\"virago\",\n\"viral\",\n\"vire\",\n\"virelay\",\n\"viremia\",\n\"viremic\",\n\"virent\",\n\"vireo\",\n\"virga\",\n\"virgal\",\n\"virgate\",\n\"virgin\",\n\"virgula\",\n\"virgule\",\n\"virial\",\n\"virid\",\n\"virific\",\n\"virify\",\n\"virile\",\n\"virl\",\n\"virole\",\n\"viroled\",\n\"viron\",\n\"virose\",\n\"virosis\",\n\"virous\",\n\"virtu\",\n\"virtual\",\n\"virtue\",\n\"virtued\",\n\"viruela\",\n\"virus\",\n\"vis\",\n\"visa\",\n\"visage\",\n\"visaged\",\n\"visarga\",\n\"viscera\",\n\"viscid\",\n\"viscin\",\n\"viscose\",\n\"viscous\",\n\"viscus\",\n\"vise\",\n\"viseman\",\n\"visible\",\n\"visibly\",\n\"visie\",\n\"visile\",\n\"vision\",\n\"visit\",\n\"visita\",\n\"visite\",\n\"visitee\",\n\"visiter\",\n\"visitor\",\n\"visive\",\n\"visne\",\n\"vison\",\n\"visor\",\n\"vista\",\n\"vistaed\",\n\"vistal\",\n\"visto\",\n\"visual\",\n\"vita\",\n\"vital\",\n\"vitalic\",\n\"vitally\",\n\"vitals\",\n\"vitamer\",\n\"vitamin\",\n\"vitasti\",\n\"vitiate\",\n\"vitium\",\n\"vitrage\",\n\"vitrail\",\n\"vitrain\",\n\"vitraux\",\n\"vitreal\",\n\"vitrean\",\n\"vitreum\",\n\"vitric\",\n\"vitrics\",\n\"vitrify\",\n\"vitrine\",\n\"vitriol\",\n\"vitrite\",\n\"vitrous\",\n\"vitta\",\n\"vittate\",\n\"vitular\",\n\"viuva\",\n\"viva\",\n\"vivary\",\n\"vivax\",\n\"vive\",\n\"vively\",\n\"vivency\",\n\"viver\",\n\"vivers\",\n\"vives\",\n\"vivid\",\n\"vividly\",\n\"vivific\",\n\"vivify\",\n\"vixen\",\n\"vixenly\",\n\"vizard\",\n\"vizier\",\n\"vlei\",\n\"voar\",\n\"vocable\",\n\"vocably\",\n\"vocal\",\n\"vocalic\",\n\"vocally\",\n\"vocate\",\n\"vocular\",\n\"vocule\",\n\"vodka\",\n\"voe\",\n\"voet\",\n\"voeten\",\n\"vog\",\n\"voglite\",\n\"vogue\",\n\"voguey\",\n\"voguish\",\n\"voice\",\n\"voiced\",\n\"voicer\",\n\"voicing\",\n\"void\",\n\"voided\",\n\"voidee\",\n\"voider\",\n\"voiding\",\n\"voidly\",\n\"voile\",\n\"voivode\",\n\"vol\",\n\"volable\",\n\"volage\",\n\"volant\",\n\"volar\",\n\"volata\",\n\"volatic\",\n\"volcan\",\n\"volcano\",\n\"vole\",\n\"volency\",\n\"volent\",\n\"volery\",\n\"volet\",\n\"volley\",\n\"volost\",\n\"volt\",\n\"voltage\",\n\"voltaic\",\n\"voltize\",\n\"voluble\",\n\"volubly\",\n\"volume\",\n\"volumed\",\n\"volupt\",\n\"volupty\",\n\"voluta\",\n\"volute\",\n\"voluted\",\n\"volutin\",\n\"volva\",\n\"volvate\",\n\"volvent\",\n\"vomer\",\n\"vomica\",\n\"vomit\",\n\"vomiter\",\n\"vomito\",\n\"vomitus\",\n\"voodoo\",\n\"vorago\",\n\"vorant\",\n\"vorhand\",\n\"vorpal\",\n\"vortex\",\n\"vota\",\n\"votable\",\n\"votal\",\n\"votally\",\n\"votary\",\n\"vote\",\n\"voteen\",\n\"voter\",\n\"voting\",\n\"votive\",\n\"votress\",\n\"vouch\",\n\"vouchee\",\n\"voucher\",\n\"vouge\",\n\"vow\",\n\"vowed\",\n\"vowel\",\n\"vowely\",\n\"vower\",\n\"vowess\",\n\"vowless\",\n\"voyage\",\n\"voyager\",\n\"voyance\",\n\"voyeur\",\n\"vraic\",\n\"vrbaite\",\n\"vriddhi\",\n\"vrother\",\n\"vug\",\n\"vuggy\",\n\"vulgar\",\n\"vulgare\",\n\"vulgate\",\n\"vulgus\",\n\"vuln\",\n\"vulnose\",\n\"vulpic\",\n\"vulpine\",\n\"vulture\",\n\"vulturn\",\n\"vulva\",\n\"vulval\",\n\"vulvar\",\n\"vulvate\",\n\"vum\",\n\"vying\",\n\"vyingly\",\n\"w\",\n\"wa\",\n\"waag\",\n\"waapa\",\n\"waar\",\n\"wab\",\n\"wabber\",\n\"wabble\",\n\"wabbly\",\n\"wabby\",\n\"wabe\",\n\"wabeno\",\n\"wabster\",\n\"wacago\",\n\"wace\",\n\"wachna\",\n\"wack\",\n\"wacke\",\n\"wacken\",\n\"wacker\",\n\"wacky\",\n\"wad\",\n\"waddent\",\n\"wadder\",\n\"wadding\",\n\"waddler\",\n\"waddly\",\n\"waddy\",\n\"wade\",\n\"wader\",\n\"wadi\",\n\"wading\",\n\"wadlike\",\n\"wadmal\",\n\"wadmeal\",\n\"wadna\",\n\"wadset\",\n\"wae\",\n\"waeg\",\n\"waer\",\n\"waesome\",\n\"waesuck\",\n\"wafer\",\n\"waferer\",\n\"wafery\",\n\"waff\",\n\"waffle\",\n\"waffly\",\n\"waft\",\n\"waftage\",\n\"wafter\",\n\"wafture\",\n\"wafty\",\n\"wag\",\n\"wagaun\",\n\"wage\",\n\"waged\",\n\"wagedom\",\n\"wager\",\n\"wagerer\",\n\"wages\",\n\"waggel\",\n\"wagger\",\n\"waggery\",\n\"waggie\",\n\"waggish\",\n\"waggle\",\n\"waggly\",\n\"waggy\",\n\"waglike\",\n\"wagling\",\n\"wagon\",\n\"wagoner\",\n\"wagonry\",\n\"wagsome\",\n\"wagtail\",\n\"wagwag\",\n\"wagwit\",\n\"wah\",\n\"wahahe\",\n\"wahine\",\n\"wahoo\",\n\"waiata\",\n\"waif\",\n\"waik\",\n\"waikly\",\n\"wail\",\n\"wailer\",\n\"wailful\",\n\"waily\",\n\"wain\",\n\"wainage\",\n\"wainer\",\n\"wainful\",\n\"wainman\",\n\"waipiro\",\n\"wairch\",\n\"waird\",\n\"wairepo\",\n\"wairsh\",\n\"waise\",\n\"waist\",\n\"waisted\",\n\"waister\",\n\"wait\",\n\"waiter\",\n\"waiting\",\n\"waive\",\n\"waiver\",\n\"waivery\",\n\"waivod\",\n\"waiwode\",\n\"wajang\",\n\"waka\",\n\"wakan\",\n\"wake\",\n\"wakeel\",\n\"wakeful\",\n\"waken\",\n\"wakener\",\n\"waker\",\n\"wakes\",\n\"wakf\",\n\"wakif\",\n\"wakiki\",\n\"waking\",\n\"wakiup\",\n\"wakken\",\n\"wakon\",\n\"wakonda\",\n\"waky\",\n\"walahee\",\n\"wale\",\n\"waled\",\n\"waler\",\n\"wali\",\n\"waling\",\n\"walk\",\n\"walker\",\n\"walking\",\n\"walkist\",\n\"walkout\",\n\"walkway\",\n\"wall\",\n\"wallaba\",\n\"wallaby\",\n\"wallah\",\n\"walled\",\n\"waller\",\n\"wallet\",\n\"walleye\",\n\"wallful\",\n\"walling\",\n\"wallise\",\n\"wallman\",\n\"walloon\",\n\"wallop\",\n\"wallow\",\n\"wally\",\n\"walnut\",\n\"walrus\",\n\"walsh\",\n\"walt\",\n\"walter\",\n\"walth\",\n\"waltz\",\n\"waltzer\",\n\"wamara\",\n\"wambais\",\n\"wamble\",\n\"wambly\",\n\"wame\",\n\"wamefou\",\n\"wamel\",\n\"wamp\",\n\"wampee\",\n\"wample\",\n\"wampum\",\n\"wampus\",\n\"wamus\",\n\"wan\",\n\"wand\",\n\"wander\",\n\"wandery\",\n\"wandle\",\n\"wandoo\",\n\"wandy\",\n\"wane\",\n\"waned\",\n\"wang\",\n\"wanga\",\n\"wangala\",\n\"wangan\",\n\"wanghee\",\n\"wangle\",\n\"wangler\",\n\"wanhope\",\n\"wanhorn\",\n\"wanigan\",\n\"waning\",\n\"wankle\",\n\"wankly\",\n\"wanle\",\n\"wanly\",\n\"wanner\",\n\"wanness\",\n\"wannish\",\n\"wanny\",\n\"wanrufe\",\n\"want\",\n\"wantage\",\n\"wanter\",\n\"wantful\",\n\"wanting\",\n\"wanton\",\n\"wantwit\",\n\"wanty\",\n\"wany\",\n\"wap\",\n\"wapacut\",\n\"wapatoo\",\n\"wapiti\",\n\"wapp\",\n\"wapper\",\n\"wapping\",\n\"war\",\n\"warabi\",\n\"waratah\",\n\"warble\",\n\"warbled\",\n\"warbler\",\n\"warblet\",\n\"warbly\",\n\"warch\",\n\"ward\",\n\"wardage\",\n\"warday\",\n\"warded\",\n\"warden\",\n\"warder\",\n\"warding\",\n\"wardite\",\n\"wardman\",\n\"ware\",\n\"warehou\",\n\"wareman\",\n\"warf\",\n\"warfare\",\n\"warful\",\n\"warily\",\n\"warish\",\n\"warison\",\n\"wark\",\n\"warl\",\n\"warless\",\n\"warlike\",\n\"warlock\",\n\"warluck\",\n\"warly\",\n\"warm\",\n\"warman\",\n\"warmed\",\n\"warmer\",\n\"warmful\",\n\"warming\",\n\"warmish\",\n\"warmly\",\n\"warmth\",\n\"warmus\",\n\"warn\",\n\"warnel\",\n\"warner\",\n\"warning\",\n\"warnish\",\n\"warnoth\",\n\"warnt\",\n\"warp\",\n\"warpage\",\n\"warped\",\n\"warper\",\n\"warping\",\n\"warple\",\n\"warran\",\n\"warrand\",\n\"warrant\",\n\"warree\",\n\"warren\",\n\"warrer\",\n\"warrin\",\n\"warrior\",\n\"warrok\",\n\"warsaw\",\n\"warse\",\n\"warsel\",\n\"warship\",\n\"warsle\",\n\"warsler\",\n\"warst\",\n\"wart\",\n\"warted\",\n\"wartern\",\n\"warth\",\n\"wartime\",\n\"wartlet\",\n\"warty\",\n\"warve\",\n\"warwolf\",\n\"warworn\",\n\"wary\",\n\"was\",\n\"wasabi\",\n\"wase\",\n\"wasel\",\n\"wash\",\n\"washday\",\n\"washed\",\n\"washen\",\n\"washer\",\n\"washery\",\n\"washin\",\n\"washing\",\n\"washman\",\n\"washoff\",\n\"washout\",\n\"washpot\",\n\"washrag\",\n\"washtub\",\n\"washway\",\n\"washy\",\n\"wasnt\",\n\"wasp\",\n\"waspen\",\n\"waspily\",\n\"waspish\",\n\"waspy\",\n\"wassail\",\n\"wassie\",\n\"wast\",\n\"wastage\",\n\"waste\",\n\"wasted\",\n\"wastel\",\n\"waster\",\n\"wasting\",\n\"wastrel\",\n\"wasty\",\n\"wat\",\n\"watap\",\n\"watch\",\n\"watched\",\n\"watcher\",\n\"water\",\n\"watered\",\n\"waterer\",\n\"waterie\",\n\"watery\",\n\"wath\",\n\"watt\",\n\"wattage\",\n\"wattape\",\n\"wattle\",\n\"wattled\",\n\"wattman\",\n\"wauble\",\n\"wauch\",\n\"wauchle\",\n\"waucht\",\n\"wauf\",\n\"waugh\",\n\"waughy\",\n\"wauken\",\n\"waukit\",\n\"waul\",\n\"waumle\",\n\"wauner\",\n\"wauns\",\n\"waup\",\n\"waur\",\n\"wauve\",\n\"wavable\",\n\"wavably\",\n\"wave\",\n\"waved\",\n\"wavelet\",\n\"waver\",\n\"waverer\",\n\"wavery\",\n\"waveson\",\n\"wavey\",\n\"wavicle\",\n\"wavily\",\n\"waving\",\n\"wavy\",\n\"waw\",\n\"wawa\",\n\"wawah\",\n\"wax\",\n\"waxbill\",\n\"waxbird\",\n\"waxbush\",\n\"waxen\",\n\"waxer\",\n\"waxily\",\n\"waxing\",\n\"waxlike\",\n\"waxman\",\n\"waxweed\",\n\"waxwing\",\n\"waxwork\",\n\"waxy\",\n\"way\",\n\"wayaka\",\n\"wayang\",\n\"wayback\",\n\"waybill\",\n\"waybird\",\n\"waybook\",\n\"waybung\",\n\"wayfare\",\n\"waygang\",\n\"waygate\",\n\"waygone\",\n\"waying\",\n\"waylaid\",\n\"waylay\",\n\"wayless\",\n\"wayman\",\n\"waymark\",\n\"waymate\",\n\"waypost\",\n\"ways\",\n\"wayside\",\n\"wayward\",\n\"waywode\",\n\"wayworn\",\n\"waywort\",\n\"we\",\n\"weak\",\n\"weaken\",\n\"weakish\",\n\"weakly\",\n\"weaky\",\n\"weal\",\n\"weald\",\n\"wealth\",\n\"wealthy\",\n\"weam\",\n\"wean\",\n\"weanel\",\n\"weaner\",\n\"weanyer\",\n\"weapon\",\n\"wear\",\n\"wearer\",\n\"wearied\",\n\"wearier\",\n\"wearily\",\n\"wearing\",\n\"wearish\",\n\"weary\",\n\"weasand\",\n\"weasel\",\n\"weaser\",\n\"weason\",\n\"weather\",\n\"weave\",\n\"weaved\",\n\"weaver\",\n\"weaving\",\n\"weazen\",\n\"weazeny\",\n\"web\",\n\"webbed\",\n\"webber\",\n\"webbing\",\n\"webby\",\n\"weber\",\n\"webeye\",\n\"webfoot\",\n\"webless\",\n\"weblike\",\n\"webster\",\n\"webwork\",\n\"webworm\",\n\"wecht\",\n\"wed\",\n\"wedana\",\n\"wedbed\",\n\"wedded\",\n\"wedder\",\n\"wedding\",\n\"wede\",\n\"wedge\",\n\"wedged\",\n\"wedger\",\n\"wedging\",\n\"wedgy\",\n\"wedlock\",\n\"wedset\",\n\"wee\",\n\"weeble\",\n\"weed\",\n\"weeda\",\n\"weedage\",\n\"weeded\",\n\"weeder\",\n\"weedery\",\n\"weedful\",\n\"weedish\",\n\"weedow\",\n\"weedy\",\n\"week\",\n\"weekday\",\n\"weekend\",\n\"weekly\",\n\"weekwam\",\n\"weel\",\n\"weemen\",\n\"ween\",\n\"weeness\",\n\"weening\",\n\"weenong\",\n\"weeny\",\n\"weep\",\n\"weeper\",\n\"weepful\",\n\"weeping\",\n\"weeps\",\n\"weepy\",\n\"weesh\",\n\"weeshy\",\n\"weet\",\n\"weever\",\n\"weevil\",\n\"weevily\",\n\"weewow\",\n\"weeze\",\n\"weft\",\n\"weftage\",\n\"wefted\",\n\"wefty\",\n\"weigh\",\n\"weighed\",\n\"weigher\",\n\"weighin\",\n\"weight\",\n\"weighty\",\n\"weir\",\n\"weird\",\n\"weirdly\",\n\"weiring\",\n\"weism\",\n\"wejack\",\n\"weka\",\n\"wekau\",\n\"wekeen\",\n\"weki\",\n\"welcome\",\n\"weld\",\n\"welder\",\n\"welding\",\n\"weldor\",\n\"welfare\",\n\"welk\",\n\"welkin\",\n\"well\",\n\"wellat\",\n\"welling\",\n\"wellish\",\n\"wellman\",\n\"welly\",\n\"wels\",\n\"welsh\",\n\"welsher\",\n\"welsium\",\n\"welt\",\n\"welted\",\n\"welter\",\n\"welting\",\n\"wem\",\n\"wemless\",\n\"wen\",\n\"wench\",\n\"wencher\",\n\"wend\",\n\"wende\",\n\"wene\",\n\"wennish\",\n\"wenny\",\n\"went\",\n\"wenzel\",\n\"wept\",\n\"wer\",\n\"were\",\n\"werefox\",\n\"werent\",\n\"werf\",\n\"wergil\",\n\"weri\",\n\"wert\",\n\"wervel\",\n\"wese\",\n\"weskit\",\n\"west\",\n\"weste\",\n\"wester\",\n\"western\",\n\"westing\",\n\"westy\",\n\"wet\",\n\"weta\",\n\"wetback\",\n\"wetbird\",\n\"wetched\",\n\"wetchet\",\n\"wether\",\n\"wetly\",\n\"wetness\",\n\"wetted\",\n\"wetter\",\n\"wetting\",\n\"wettish\",\n\"weve\",\n\"wevet\",\n\"wey\",\n\"wha\",\n\"whabby\",\n\"whack\",\n\"whacker\",\n\"whacky\",\n\"whale\",\n\"whaler\",\n\"whalery\",\n\"whaling\",\n\"whalish\",\n\"whally\",\n\"whalm\",\n\"whalp\",\n\"whaly\",\n\"wham\",\n\"whamble\",\n\"whame\",\n\"whammle\",\n\"whamp\",\n\"whampee\",\n\"whample\",\n\"whan\",\n\"whand\",\n\"whang\",\n\"whangam\",\n\"whangee\",\n\"whank\",\n\"whap\",\n\"whappet\",\n\"whapuka\",\n\"whapuku\",\n\"whar\",\n\"whare\",\n\"whareer\",\n\"wharf\",\n\"wharl\",\n\"wharp\",\n\"wharry\",\n\"whart\",\n\"wharve\",\n\"whase\",\n\"whasle\",\n\"what\",\n\"whata\",\n\"whatkin\",\n\"whatna\",\n\"whatnot\",\n\"whats\",\n\"whatso\",\n\"whatten\",\n\"whau\",\n\"whauk\",\n\"whaup\",\n\"whaur\",\n\"whauve\",\n\"wheal\",\n\"whealy\",\n\"wheam\",\n\"wheat\",\n\"wheaten\",\n\"wheaty\",\n\"whedder\",\n\"whee\",\n\"wheedle\",\n\"wheel\",\n\"wheeled\",\n\"wheeler\",\n\"wheely\",\n\"wheem\",\n\"wheen\",\n\"wheenge\",\n\"wheep\",\n\"wheeple\",\n\"wheer\",\n\"wheesht\",\n\"wheetle\",\n\"wheeze\",\n\"wheezer\",\n\"wheezle\",\n\"wheezy\",\n\"wheft\",\n\"whein\",\n\"whekau\",\n\"wheki\",\n\"whelk\",\n\"whelked\",\n\"whelker\",\n\"whelky\",\n\"whelm\",\n\"whelp\",\n\"whelve\",\n\"whemmel\",\n\"when\",\n\"whenas\",\n\"whence\",\n\"wheneer\",\n\"whenso\",\n\"where\",\n\"whereas\",\n\"whereat\",\n\"whereby\",\n\"whereer\",\n\"wherein\",\n\"whereof\",\n\"whereon\",\n\"whereso\",\n\"whereto\",\n\"whereup\",\n\"wherret\",\n\"wherrit\",\n\"wherry\",\n\"whet\",\n\"whether\",\n\"whetile\",\n\"whetter\",\n\"whew\",\n\"whewer\",\n\"whewl\",\n\"whewt\",\n\"whey\",\n\"wheyey\",\n\"wheyish\",\n\"whiba\",\n\"which\",\n\"whick\",\n\"whicken\",\n\"whicker\",\n\"whid\",\n\"whidah\",\n\"whidder\",\n\"whiff\",\n\"whiffer\",\n\"whiffet\",\n\"whiffle\",\n\"whiffy\",\n\"whift\",\n\"whig\",\n\"while\",\n\"whileen\",\n\"whilere\",\n\"whiles\",\n\"whilie\",\n\"whilk\",\n\"whill\",\n\"whilly\",\n\"whilock\",\n\"whilom\",\n\"whils\",\n\"whilst\",\n\"whilter\",\n\"whim\",\n\"whimble\",\n\"whimmy\",\n\"whimper\",\n\"whimsey\",\n\"whimsic\",\n\"whin\",\n\"whincow\",\n\"whindle\",\n\"whine\",\n\"whiner\",\n\"whing\",\n\"whinge\",\n\"whinger\",\n\"whinnel\",\n\"whinner\",\n\"whinny\",\n\"whiny\",\n\"whip\",\n\"whipcat\",\n\"whipman\",\n\"whippa\",\n\"whipped\",\n\"whipper\",\n\"whippet\",\n\"whippy\",\n\"whipsaw\",\n\"whipt\",\n\"whir\",\n\"whirken\",\n\"whirl\",\n\"whirled\",\n\"whirler\",\n\"whirley\",\n\"whirly\",\n\"whirret\",\n\"whirrey\",\n\"whirroo\",\n\"whirry\",\n\"whirtle\",\n\"whish\",\n\"whisk\",\n\"whisker\",\n\"whiskey\",\n\"whisky\",\n\"whisp\",\n\"whisper\",\n\"whissle\",\n\"whist\",\n\"whister\",\n\"whistle\",\n\"whistly\",\n\"whit\",\n\"white\",\n\"whited\",\n\"whitely\",\n\"whiten\",\n\"whites\",\n\"whither\",\n\"whiting\",\n\"whitish\",\n\"whitlow\",\n\"whits\",\n\"whittaw\",\n\"whitten\",\n\"whitter\",\n\"whittle\",\n\"whity\",\n\"whiz\",\n\"whizgig\",\n\"whizzer\",\n\"whizzle\",\n\"who\",\n\"whoa\",\n\"whoever\",\n\"whole\",\n\"wholly\",\n\"whom\",\n\"whomble\",\n\"whomso\",\n\"whone\",\n\"whoo\",\n\"whoof\",\n\"whoop\",\n\"whoopee\",\n\"whooper\",\n\"whoops\",\n\"whoosh\",\n\"whop\",\n\"whopper\",\n\"whorage\",\n\"whore\",\n\"whorish\",\n\"whorl\",\n\"whorled\",\n\"whorly\",\n\"whort\",\n\"whortle\",\n\"whose\",\n\"whosen\",\n\"whud\",\n\"whuff\",\n\"whuffle\",\n\"whulk\",\n\"whulter\",\n\"whummle\",\n\"whun\",\n\"whup\",\n\"whush\",\n\"whuskie\",\n\"whussle\",\n\"whute\",\n\"whuther\",\n\"whutter\",\n\"whuz\",\n\"why\",\n\"whyever\",\n\"whyfor\",\n\"whyness\",\n\"whyo\",\n\"wi\",\n\"wice\",\n\"wicht\",\n\"wichtje\",\n\"wick\",\n\"wicked\",\n\"wicken\",\n\"wicker\",\n\"wicket\",\n\"wicking\",\n\"wickiup\",\n\"wickup\",\n\"wicky\",\n\"wicopy\",\n\"wid\",\n\"widbin\",\n\"widder\",\n\"widdle\",\n\"widdy\",\n\"wide\",\n\"widegab\",\n\"widely\",\n\"widen\",\n\"widener\",\n\"widgeon\",\n\"widish\",\n\"widow\",\n\"widowed\",\n\"widower\",\n\"widowly\",\n\"widowy\",\n\"width\",\n\"widu\",\n\"wield\",\n\"wielder\",\n\"wieldy\",\n\"wiener\",\n\"wienie\",\n\"wife\",\n\"wifedom\",\n\"wifeism\",\n\"wifekin\",\n\"wifelet\",\n\"wifely\",\n\"wifie\",\n\"wifish\",\n\"wifock\",\n\"wig\",\n\"wigan\",\n\"wigdom\",\n\"wigful\",\n\"wigged\",\n\"wiggen\",\n\"wigger\",\n\"wiggery\",\n\"wigging\",\n\"wiggish\",\n\"wiggism\",\n\"wiggle\",\n\"wiggler\",\n\"wiggly\",\n\"wiggy\",\n\"wight\",\n\"wightly\",\n\"wigless\",\n\"wiglet\",\n\"wiglike\",\n\"wigtail\",\n\"wigwag\",\n\"wigwam\",\n\"wiikite\",\n\"wild\",\n\"wildcat\",\n\"wilded\",\n\"wilder\",\n\"wilding\",\n\"wildish\",\n\"wildly\",\n\"wile\",\n\"wileful\",\n\"wilga\",\n\"wilgers\",\n\"wilily\",\n\"wilk\",\n\"wilkin\",\n\"will\",\n\"willawa\",\n\"willed\",\n\"willer\",\n\"willet\",\n\"willey\",\n\"willful\",\n\"willie\",\n\"willier\",\n\"willies\",\n\"willing\",\n\"willock\",\n\"willow\",\n\"willowy\",\n\"willy\",\n\"willyer\",\n\"wilsome\",\n\"wilt\",\n\"wilter\",\n\"wily\",\n\"wim\",\n\"wimble\",\n\"wimbrel\",\n\"wime\",\n\"wimick\",\n\"wimple\",\n\"win\",\n\"wince\",\n\"wincer\",\n\"wincey\",\n\"winch\",\n\"wincher\",\n\"wincing\",\n\"wind\",\n\"windage\",\n\"windbag\",\n\"winddog\",\n\"winded\",\n\"winder\",\n\"windigo\",\n\"windily\",\n\"winding\",\n\"windle\",\n\"windles\",\n\"windlin\",\n\"windock\",\n\"windore\",\n\"window\",\n\"windowy\",\n\"windrow\",\n\"windup\",\n\"windway\",\n\"windy\",\n\"wine\",\n\"wined\",\n\"winemay\",\n\"winepot\",\n\"winer\",\n\"winery\",\n\"winesop\",\n\"winevat\",\n\"winful\",\n\"wing\",\n\"wingcut\",\n\"winged\",\n\"winger\",\n\"wingle\",\n\"winglet\",\n\"wingman\",\n\"wingy\",\n\"winish\",\n\"wink\",\n\"winkel\",\n\"winker\",\n\"winking\",\n\"winkle\",\n\"winklet\",\n\"winly\",\n\"winna\",\n\"winnard\",\n\"winnel\",\n\"winner\",\n\"winning\",\n\"winnle\",\n\"winnow\",\n\"winrace\",\n\"winrow\",\n\"winsome\",\n\"wint\",\n\"winter\",\n\"wintle\",\n\"wintry\",\n\"winy\",\n\"winze\",\n\"wipe\",\n\"wiper\",\n\"wippen\",\n\"wips\",\n\"wir\",\n\"wirable\",\n\"wirble\",\n\"wird\",\n\"wire\",\n\"wirebar\",\n\"wired\",\n\"wireman\",\n\"wirer\",\n\"wireway\",\n\"wirily\",\n\"wiring\",\n\"wirl\",\n\"wirling\",\n\"wirr\",\n\"wirra\",\n\"wirrah\",\n\"wiry\",\n\"wis\",\n\"wisdom\",\n\"wise\",\n\"wisely\",\n\"wiseman\",\n\"wisen\",\n\"wisent\",\n\"wiser\",\n\"wish\",\n\"wisha\",\n\"wished\",\n\"wisher\",\n\"wishful\",\n\"wishing\",\n\"wishly\",\n\"wishmay\",\n\"wisht\",\n\"wisket\",\n\"wisp\",\n\"wispish\",\n\"wispy\",\n\"wiss\",\n\"wisse\",\n\"wissel\",\n\"wist\",\n\"wiste\",\n\"wistful\",\n\"wistit\",\n\"wistiti\",\n\"wit\",\n\"witan\",\n\"witch\",\n\"witched\",\n\"witchen\",\n\"witchet\",\n\"witchy\",\n\"wite\",\n\"witess\",\n\"witful\",\n\"with\",\n\"withal\",\n\"withe\",\n\"withen\",\n\"wither\",\n\"withers\",\n\"withery\",\n\"within\",\n\"without\",\n\"withy\",\n\"witjar\",\n\"witless\",\n\"witlet\",\n\"witling\",\n\"witloof\",\n\"witness\",\n\"witney\",\n\"witship\",\n\"wittal\",\n\"witted\",\n\"witter\",\n\"wittily\",\n\"witting\",\n\"wittol\",\n\"witty\",\n\"witwall\",\n\"wive\",\n\"wiver\",\n\"wivern\",\n\"wiz\",\n\"wizard\",\n\"wizen\",\n\"wizened\",\n\"wizier\",\n\"wizzen\",\n\"wloka\",\n\"wo\",\n\"woad\",\n\"woader\",\n\"woadman\",\n\"woady\",\n\"woak\",\n\"woald\",\n\"woan\",\n\"wob\",\n\"wobble\",\n\"wobbler\",\n\"wobbly\",\n\"wobster\",\n\"wod\",\n\"woddie\",\n\"wode\",\n\"wodge\",\n\"wodgy\",\n\"woe\",\n\"woeful\",\n\"woesome\",\n\"woevine\",\n\"woeworn\",\n\"woffler\",\n\"woft\",\n\"wog\",\n\"wogiet\",\n\"woibe\",\n\"wokas\",\n\"woke\",\n\"wokowi\",\n\"wold\",\n\"woldy\",\n\"wolf\",\n\"wolfdom\",\n\"wolfen\",\n\"wolfer\",\n\"wolfish\",\n\"wolfkin\",\n\"wolfram\",\n\"wollop\",\n\"wolter\",\n\"wolve\",\n\"wolver\",\n\"woman\",\n\"womanly\",\n\"womb\",\n\"wombat\",\n\"wombed\",\n\"womble\",\n\"womby\",\n\"womera\",\n\"won\",\n\"wonder\",\n\"wone\",\n\"wonegan\",\n\"wong\",\n\"wonga\",\n\"wongen\",\n\"wongshy\",\n\"wongsky\",\n\"woning\",\n\"wonky\",\n\"wonna\",\n\"wonned\",\n\"wonner\",\n\"wonning\",\n\"wonnot\",\n\"wont\",\n\"wonted\",\n\"wonting\",\n\"woo\",\n\"wooable\",\n\"wood\",\n\"woodbin\",\n\"woodcut\",\n\"wooded\",\n\"wooden\",\n\"woodeny\",\n\"woodine\",\n\"wooding\",\n\"woodish\",\n\"woodlet\",\n\"woodly\",\n\"woodman\",\n\"woodrow\",\n\"woodsy\",\n\"woodwax\",\n\"woody\",\n\"wooer\",\n\"woof\",\n\"woofed\",\n\"woofell\",\n\"woofer\",\n\"woofy\",\n\"woohoo\",\n\"wooing\",\n\"wool\",\n\"woold\",\n\"woolder\",\n\"wooled\",\n\"woolen\",\n\"wooler\",\n\"woolert\",\n\"woolly\",\n\"woolman\",\n\"woolsey\",\n\"woom\",\n\"woomer\",\n\"woon\",\n\"woons\",\n\"woorali\",\n\"woorari\",\n\"woosh\",\n\"wootz\",\n\"woozle\",\n\"woozy\",\n\"wop\",\n\"woppish\",\n\"wops\",\n\"worble\",\n\"word\",\n\"wordage\",\n\"worded\",\n\"worder\",\n\"wordily\",\n\"wording\",\n\"wordish\",\n\"wordle\",\n\"wordman\",\n\"wordy\",\n\"wore\",\n\"work\",\n\"workbag\",\n\"workbox\",\n\"workday\",\n\"worked\",\n\"worker\",\n\"working\",\n\"workman\",\n\"workout\",\n\"workpan\",\n\"works\",\n\"worky\",\n\"world\",\n\"worlded\",\n\"worldly\",\n\"worldy\",\n\"worm\",\n\"wormed\",\n\"wormer\",\n\"wormil\",\n\"worming\",\n\"wormy\",\n\"worn\",\n\"wornil\",\n\"worral\",\n\"worried\",\n\"worrier\",\n\"worrit\",\n\"worry\",\n\"worse\",\n\"worsen\",\n\"worser\",\n\"worset\",\n\"worship\",\n\"worst\",\n\"worsted\",\n\"wort\",\n\"worth\",\n\"worthy\",\n\"wosbird\",\n\"wot\",\n\"wote\",\n\"wots\",\n\"wottest\",\n\"wotteth\",\n\"woubit\",\n\"wouch\",\n\"wouf\",\n\"wough\",\n\"would\",\n\"wouldnt\",\n\"wouldst\",\n\"wound\",\n\"wounded\",\n\"wounder\",\n\"wounds\",\n\"woundy\",\n\"wourali\",\n\"wourari\",\n\"wournil\",\n\"wove\",\n\"woven\",\n\"wow\",\n\"wowser\",\n\"wowsery\",\n\"wowt\",\n\"woy\",\n\"wrack\",\n\"wracker\",\n\"wraggle\",\n\"wraith\",\n\"wraithe\",\n\"wraithy\",\n\"wraitly\",\n\"wramp\",\n\"wran\",\n\"wrang\",\n\"wrangle\",\n\"wranny\",\n\"wrap\",\n\"wrapped\",\n\"wrapper\",\n\"wrasse\",\n\"wrastle\",\n\"wrath\",\n\"wrathy\",\n\"wraw\",\n\"wrawl\",\n\"wrawler\",\n\"wraxle\",\n\"wreak\",\n\"wreat\",\n\"wreath\",\n\"wreathe\",\n\"wreathy\",\n\"wreck\",\n\"wrecker\",\n\"wrecky\",\n\"wren\",\n\"wrench\",\n\"wrenlet\",\n\"wrest\",\n\"wrester\",\n\"wrestle\",\n\"wretch\",\n\"wricht\",\n\"wrick\",\n\"wride\",\n\"wried\",\n\"wrier\",\n\"wriest\",\n\"wrig\",\n\"wriggle\",\n\"wriggly\",\n\"wright\",\n\"wring\",\n\"wringer\",\n\"wrinkle\",\n\"wrinkly\",\n\"wrist\",\n\"wristed\",\n\"wrister\",\n\"writ\",\n\"write\",\n\"writee\",\n\"writer\",\n\"writh\",\n\"writhe\",\n\"writhed\",\n\"writhen\",\n\"writher\",\n\"writhy\",\n\"writing\",\n\"written\",\n\"writter\",\n\"wrive\",\n\"wro\",\n\"wrocht\",\n\"wroke\",\n\"wroken\",\n\"wrong\",\n\"wronged\",\n\"wronger\",\n\"wrongly\",\n\"wrossle\",\n\"wrote\",\n\"wroth\",\n\"wrothly\",\n\"wrothy\",\n\"wrought\",\n\"wrox\",\n\"wrung\",\n\"wry\",\n\"wrybill\",\n\"wryly\",\n\"wryneck\",\n\"wryness\",\n\"wrytail\",\n\"wud\",\n\"wuddie\",\n\"wudge\",\n\"wudu\",\n\"wugg\",\n\"wulk\",\n\"wull\",\n\"wullcat\",\n\"wulliwa\",\n\"wumble\",\n\"wumman\",\n\"wummel\",\n\"wun\",\n\"wungee\",\n\"wunna\",\n\"wunner\",\n\"wunsome\",\n\"wup\",\n\"wur\",\n\"wurley\",\n\"wurmal\",\n\"wurrus\",\n\"wurset\",\n\"wurzel\",\n\"wush\",\n\"wusp\",\n\"wuss\",\n\"wusser\",\n\"wust\",\n\"wut\",\n\"wuther\",\n\"wuzu\",\n\"wuzzer\",\n\"wuzzle\",\n\"wuzzy\",\n\"wy\",\n\"wyde\",\n\"wye\",\n\"wyke\",\n\"wyle\",\n\"wymote\",\n\"wyn\",\n\"wynd\",\n\"wyne\",\n\"wynn\",\n\"wype\",\n\"wyson\",\n\"wyss\",\n\"wyve\",\n\"wyver\",\n\"x\",\n\"xanthic\",\n\"xanthin\",\n\"xanthyl\",\n\"xarque\",\n\"xebec\",\n\"xenia\",\n\"xenial\",\n\"xenian\",\n\"xenium\",\n\"xenon\",\n\"xenyl\",\n\"xerafin\",\n\"xerarch\",\n\"xerasia\",\n\"xeric\",\n\"xeriff\",\n\"xerogel\",\n\"xeroma\",\n\"xeronic\",\n\"xerosis\",\n\"xerotes\",\n\"xerotic\",\n\"xi\",\n\"xiphias\",\n\"xiphiid\",\n\"xiphoid\",\n\"xoana\",\n\"xoanon\",\n\"xurel\",\n\"xyla\",\n\"xylan\",\n\"xylate\",\n\"xylem\",\n\"xylene\",\n\"xylenol\",\n\"xylenyl\",\n\"xyletic\",\n\"xylic\",\n\"xylidic\",\n\"xylinid\",\n\"xylite\",\n\"xylitol\",\n\"xylogen\",\n\"xyloid\",\n\"xylol\",\n\"xyloma\",\n\"xylon\",\n\"xylonic\",\n\"xylose\",\n\"xyloyl\",\n\"xylyl\",\n\"xylylic\",\n\"xyphoid\",\n\"xyrid\",\n\"xyst\",\n\"xyster\",\n\"xysti\",\n\"xystos\",\n\"xystum\",\n\"xystus\",\n\"y\",\n\"ya\",\n\"yaba\",\n\"yabber\",\n\"yabbi\",\n\"yabble\",\n\"yabby\",\n\"yabu\",\n\"yacal\",\n\"yacca\",\n\"yachan\",\n\"yacht\",\n\"yachter\",\n\"yachty\",\n\"yad\",\n\"yade\",\n\"yaff\",\n\"yaffle\",\n\"yagger\",\n\"yagi\",\n\"yagua\",\n\"yaguaza\",\n\"yah\",\n\"yahan\",\n\"yahoo\",\n\"yair\",\n\"yaird\",\n\"yaje\",\n\"yajeine\",\n\"yak\",\n\"yakalo\",\n\"yakamik\",\n\"yakin\",\n\"yakka\",\n\"yakman\",\n\"yalb\",\n\"yale\",\n\"yali\",\n\"yalla\",\n\"yallaer\",\n\"yallow\",\n\"yam\",\n\"yamamai\",\n\"yamanai\",\n\"yamen\",\n\"yamilke\",\n\"yammer\",\n\"yamp\",\n\"yampa\",\n\"yamph\",\n\"yamshik\",\n\"yan\",\n\"yander\",\n\"yang\",\n\"yangtao\",\n\"yank\",\n\"yanking\",\n\"yanky\",\n\"yaoort\",\n\"yaourti\",\n\"yap\",\n\"yapa\",\n\"yaply\",\n\"yapness\",\n\"yapok\",\n\"yapp\",\n\"yapped\",\n\"yapper\",\n\"yapping\",\n\"yappish\",\n\"yappy\",\n\"yapster\",\n\"yar\",\n\"yarak\",\n\"yaray\",\n\"yarb\",\n\"yard\",\n\"yardage\",\n\"yardang\",\n\"yardarm\",\n\"yarder\",\n\"yardful\",\n\"yarding\",\n\"yardman\",\n\"yare\",\n\"yareta\",\n\"yark\",\n\"yarke\",\n\"yarl\",\n\"yarly\",\n\"yarm\",\n\"yarn\",\n\"yarnen\",\n\"yarner\",\n\"yarpha\",\n\"yarr\",\n\"yarran\",\n\"yarrow\",\n\"yarth\",\n\"yarthen\",\n\"yarwhip\",\n\"yas\",\n\"yashiro\",\n\"yashmak\",\n\"yat\",\n\"yate\",\n\"yati\",\n\"yatter\",\n\"yaud\",\n\"yauld\",\n\"yaupon\",\n\"yautia\",\n\"yava\",\n\"yaw\",\n\"yawl\",\n\"yawler\",\n\"yawn\",\n\"yawner\",\n\"yawney\",\n\"yawnful\",\n\"yawnily\",\n\"yawning\",\n\"yawnups\",\n\"yawny\",\n\"yawp\",\n\"yawper\",\n\"yawroot\",\n\"yaws\",\n\"yawweed\",\n\"yawy\",\n\"yaxche\",\n\"yaya\",\n\"ycie\",\n\"yday\",\n\"ye\",\n\"yea\",\n\"yeah\",\n\"yealing\",\n\"yean\",\n\"year\",\n\"yeara\",\n\"yeard\",\n\"yearday\",\n\"yearful\",\n\"yearly\",\n\"yearn\",\n\"yearock\",\n\"yearth\",\n\"yeast\",\n\"yeasty\",\n\"yeat\",\n\"yeather\",\n\"yed\",\n\"yede\",\n\"yee\",\n\"yeel\",\n\"yees\",\n\"yegg\",\n\"yeggman\",\n\"yeguita\",\n\"yeld\",\n\"yeldrin\",\n\"yelk\",\n\"yell\",\n\"yeller\",\n\"yelling\",\n\"yelloch\",\n\"yellow\",\n\"yellows\",\n\"yellowy\",\n\"yelm\",\n\"yelmer\",\n\"yelp\",\n\"yelper\",\n\"yelt\",\n\"yen\",\n\"yender\",\n\"yeni\",\n\"yenite\",\n\"yeo\",\n\"yeoman\",\n\"yep\",\n\"yer\",\n\"yerb\",\n\"yerba\",\n\"yercum\",\n\"yerd\",\n\"yere\",\n\"yerga\",\n\"yerk\",\n\"yern\",\n\"yerth\",\n\"yes\",\n\"yese\",\n\"yeso\",\n\"yesso\",\n\"yest\",\n\"yester\",\n\"yestern\",\n\"yesty\",\n\"yet\",\n\"yeta\",\n\"yetapa\",\n\"yeth\",\n\"yether\",\n\"yetlin\",\n\"yeuk\",\n\"yeuky\",\n\"yeven\",\n\"yew\",\n\"yex\",\n\"yez\",\n\"yezzy\",\n\"ygapo\",\n\"yield\",\n\"yielden\",\n\"yielder\",\n\"yieldy\",\n\"yigh\",\n\"yill\",\n\"yilt\",\n\"yin\",\n\"yince\",\n\"yinst\",\n\"yip\",\n\"yird\",\n\"yirk\",\n\"yirm\",\n\"yirn\",\n\"yirr\",\n\"yirth\",\n\"yis\",\n\"yite\",\n\"ym\",\n\"yn\",\n\"ynambu\",\n\"yo\",\n\"yobi\",\n\"yocco\",\n\"yochel\",\n\"yock\",\n\"yockel\",\n\"yodel\",\n\"yodeler\",\n\"yodh\",\n\"yoe\",\n\"yoga\",\n\"yogh\",\n\"yoghurt\",\n\"yogi\",\n\"yogin\",\n\"yogism\",\n\"yogist\",\n\"yogoite\",\n\"yohimbe\",\n\"yohimbi\",\n\"yoi\",\n\"yoick\",\n\"yoicks\",\n\"yojan\",\n\"yojana\",\n\"yok\",\n\"yoke\",\n\"yokeage\",\n\"yokel\",\n\"yokelry\",\n\"yoker\",\n\"yoking\",\n\"yoky\",\n\"yolden\",\n\"yolk\",\n\"yolked\",\n\"yolky\",\n\"yom\",\n\"yomer\",\n\"yon\",\n\"yond\",\n\"yonder\",\n\"yonner\",\n\"yonside\",\n\"yont\",\n\"yook\",\n\"yoop\",\n\"yor\",\n\"yore\",\n\"york\",\n\"yorker\",\n\"yot\",\n\"yote\",\n\"you\",\n\"youd\",\n\"youden\",\n\"youdith\",\n\"youff\",\n\"youl\",\n\"young\",\n\"younger\",\n\"youngly\",\n\"youngun\",\n\"younker\",\n\"youp\",\n\"your\",\n\"yourn\",\n\"yours\",\n\"yoursel\",\n\"youse\",\n\"youth\",\n\"youthen\",\n\"youthy\",\n\"youve\",\n\"youward\",\n\"youze\",\n\"yoven\",\n\"yow\",\n\"yowie\",\n\"yowl\",\n\"yowler\",\n\"yowley\",\n\"yowt\",\n\"yox\",\n\"yoy\",\n\"yperite\",\n\"yr\",\n\"yttria\",\n\"yttric\",\n\"yttrium\",\n\"yuan\",\n\"yuca\",\n\"yucca\",\n\"yuck\",\n\"yuckel\",\n\"yucker\",\n\"yuckle\",\n\"yucky\",\n\"yuft\",\n\"yugada\",\n\"yuh\",\n\"yukkel\",\n\"yulan\",\n\"yule\",\n\"yummy\",\n\"yungan\",\n\"yurt\",\n\"yurta\",\n\"yus\",\n\"yusdrum\",\n\"yutu\",\n\"yuzlik\",\n\"yuzluk\",\n\"z\",\n\"za\",\n\"zabeta\",\n\"zabra\",\n\"zabti\",\n\"zabtie\",\n\"zac\",\n\"zacate\",\n\"zacaton\",\n\"zachun\",\n\"zad\",\n\"zadruga\",\n\"zaffar\",\n\"zaffer\",\n\"zafree\",\n\"zag\",\n\"zagged\",\n\"zain\",\n\"zak\",\n\"zakkeu\",\n\"zaman\",\n\"zamang\",\n\"zamarra\",\n\"zamarro\",\n\"zambo\",\n\"zamorin\",\n\"zamouse\",\n\"zander\",\n\"zanella\",\n\"zant\",\n\"zante\",\n\"zany\",\n\"zanyish\",\n\"zanyism\",\n\"zanze\",\n\"zapas\",\n\"zaphara\",\n\"zapota\",\n\"zaptiah\",\n\"zaptieh\",\n\"zapupe\",\n\"zaqqum\",\n\"zar\",\n\"zareba\",\n\"zarf\",\n\"zarnich\",\n\"zarp\",\n\"zat\",\n\"zati\",\n\"zattare\",\n\"zax\",\n\"zayat\",\n\"zayin\",\n\"zeal\",\n\"zealful\",\n\"zealot\",\n\"zealous\",\n\"zebra\",\n\"zebraic\",\n\"zebrass\",\n\"zebrine\",\n\"zebroid\",\n\"zebrula\",\n\"zebrule\",\n\"zebu\",\n\"zebub\",\n\"zeburro\",\n\"zechin\",\n\"zed\",\n\"zedoary\",\n\"zee\",\n\"zeed\",\n\"zehner\",\n\"zein\",\n\"zeism\",\n\"zeist\",\n\"zel\",\n\"zelator\",\n\"zemeism\",\n\"zemi\",\n\"zemmi\",\n\"zemni\",\n\"zemstvo\",\n\"zenana\",\n\"zendik\",\n\"zenick\",\n\"zenith\",\n\"zenu\",\n\"zeolite\",\n\"zephyr\",\n\"zephyry\",\n\"zequin\",\n\"zer\",\n\"zerda\",\n\"zero\",\n\"zeroize\",\n\"zest\",\n\"zestful\",\n\"zesty\",\n\"zeta\",\n\"zetetic\",\n\"zeugma\",\n\"ziamet\",\n\"ziara\",\n\"ziarat\",\n\"zibet\",\n\"zibetum\",\n\"ziega\",\n\"zieger\",\n\"ziffs\",\n\"zig\",\n\"ziganka\",\n\"zigzag\",\n\"zihar\",\n\"zikurat\",\n\"zillah\",\n\"zimarra\",\n\"zimb\",\n\"zimbi\",\n\"zimme\",\n\"zimmi\",\n\"zimmis\",\n\"zimocca\",\n\"zinc\",\n\"zincate\",\n\"zincic\",\n\"zincide\",\n\"zincify\",\n\"zincing\",\n\"zincite\",\n\"zincize\",\n\"zincke\",\n\"zincky\",\n\"zinco\",\n\"zincous\",\n\"zincum\",\n\"zing\",\n\"zingel\",\n\"zink\",\n\"zinsang\",\n\"zip\",\n\"ziphian\",\n\"zipper\",\n\"zipping\",\n\"zippy\",\n\"zira\",\n\"zirai\",\n\"zircite\",\n\"zircon\",\n\"zither\",\n\"zizz\",\n\"zloty\",\n\"zo\",\n\"zoa\",\n\"zoacum\",\n\"zoaria\",\n\"zoarial\",\n\"zoarium\",\n\"zobo\",\n\"zocco\",\n\"zoccolo\",\n\"zodiac\",\n\"zoea\",\n\"zoeal\",\n\"zoeform\",\n\"zoetic\",\n\"zogan\",\n\"zogo\",\n\"zoic\",\n\"zoid\",\n\"zoisite\",\n\"zoism\",\n\"zoist\",\n\"zoistic\",\n\"zokor\",\n\"zoll\",\n\"zolle\",\n\"zombi\",\n\"zombie\",\n\"zonal\",\n\"zonally\",\n\"zonar\",\n\"zonary\",\n\"zonate\",\n\"zonated\",\n\"zone\",\n\"zoned\",\n\"zonelet\",\n\"zonic\",\n\"zoning\",\n\"zonite\",\n\"zonitid\",\n\"zonoid\",\n\"zonular\",\n\"zonule\",\n\"zonulet\",\n\"zonure\",\n\"zonurid\",\n\"zoo\",\n\"zoocarp\",\n\"zoocyst\",\n\"zooecia\",\n\"zoogamy\",\n\"zoogene\",\n\"zoogeny\",\n\"zoogony\",\n\"zooid\",\n\"zooidal\",\n\"zooks\",\n\"zoolite\",\n\"zoolith\",\n\"zoology\",\n\"zoom\",\n\"zoon\",\n\"zoonal\",\n\"zoonic\",\n\"zoonist\",\n\"zoonite\",\n\"zoonomy\",\n\"zoons\",\n\"zoonule\",\n\"zoopery\",\n\"zoopsia\",\n\"zoosis\",\n\"zootaxy\",\n\"zooter\",\n\"zootic\",\n\"zootomy\",\n\"zootype\",\n\"zoozoo\",\n\"zorgite\",\n\"zoril\",\n\"zorilla\",\n\"zorillo\",\n\"zorro\",\n\"zoster\",\n\"zounds\",\n\"zowie\",\n\"zudda\",\n\"zuisin\",\n\"zumatic\",\n\"zunyite\",\n\"zuza\",\n\"zwitter\",\n\"zyga\",\n\"zygal\",\n\"zygion\",\n\"zygite\",\n\"zygoma\",\n\"zygon\",\n\"zygose\",\n\"zygosis\",\n\"zygote\",\n\"zygotic\",\n\"zygous\",\n\"zymase\",\n\"zyme\",\n\"zymic\",\n\"zymin\",\n\"zymite\",\n\"zymogen\",\n\"zymoid\",\n\"zymome\",\n\"zymomin\",\n\"zymosis\",\n\"zymotic\",\n\"zymurgy\",\n\"zythem\",\n\"zythum\" };\n\n\nconst uint32_t word_list_size = sizeof(word_list)/sizeof(word_list[0]);\n\nvoid hide_unused_warning() {\n  (void)word_list_size; \n  (void)word_list; \n}\n\n} } // graphene::words\n"
  },
  {
    "path": "libraries/wallet/CMakeLists.txt",
    "content": "file(GLOB HEADERS \"include/graphene/wallet/*.hpp\")\n\nfind_package( Perl )\nfind_package( Doxygen )\n\nif( PERL_FOUND AND DOXYGEN_FOUND AND NOT \"${CMAKE_GENERATOR}\" STREQUAL \"Ninja\" )\n  configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile )\n  add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/doxygen/perlmod/DoxyDocs.pm\n                      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n                      COMMAND ${DOXYGEN_EXECUTABLE}\n                      DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile include/graphene/wallet/wallet.hpp )\n  if(MSVC)\n    add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp\n                        COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new\n                        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp\n                        COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new\n                        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/doxygen/perlmod/DoxyDocs.pm )\n  else(MSVC)\n    add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp\n                        COMMAND PERLLIB=${CMAKE_CURRENT_BINARY_DIR} ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new\n                        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp\n                        COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new\n                        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/doxygen/perlmod/DoxyDocs.pm )\n  endif(MSVC)\nelse()\n  # no perl and doxygen, generate the best docs we can at runtime from reflection\n  add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp\n                      COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/api_documentation_standin.cpp ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp\n                      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/api_documentation_standin.cpp )\nendif()\n\nset( SOURCES\n     operation_printer.cpp\n     reflect_util.cpp\n     wallet.cpp\n     wallet_account.cpp\n     wallet_api_impl.cpp\n     wallet_asset.cpp\n     wallet_builder.cpp\n     wallet_debug.cpp\n     wallet_network.cpp\n     wallet_results.cpp\n     wallet_sign.cpp\n     wallet_transfer.cpp\n     wallet_voting.cpp\n   )\n\nadd_library( graphene_wallet ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp ${HEADERS} )\ntarget_link_libraries( graphene_wallet PRIVATE graphene_app graphene_net graphene_chain graphene_utilities fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\ntarget_include_directories( graphene_db PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\" )\n\nif(MSVC)\n  set_source_files_properties( wallet.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   graphene_wallet\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\ninstall( FILES ${HEADERS} DESTINATION \"include/graphene/wallet\" )\n"
  },
  {
    "path": "libraries/wallet/Doxyfile.in",
    "content": "# Doxyfile 1.8.9.1\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all text\n# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv\n# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv\n# for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"BitShares Wallet API\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = ${CMAKE_CURRENT_BINARY_DIR}/doxygen\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines.\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:\n# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:\n# Fortran. In the later case the parser tries to guess whether the code is fixed\n# or free formatted code, this is the default for Fortran type files), VHDL. For\n# instance to make doxygen treat .inc files as Fortran files (default is PHP),\n# and .f files as C (default is Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See http://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces.\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = ${CMAKE_CURRENT_SOURCE_DIR}/include/graphene/wallet/wallet.hpp\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: http://www.gnu.org/software/libiconv) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank the\n# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,\n# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,\n# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,\n# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,\n# *.qsf, *.as and *.js.\n\nFILE_PATTERNS          =\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = YES\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# function all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see http://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the config file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = NO\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# http://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: http://developer.apple.com/tools/xcode/), introduced with\n# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html\n# for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# http://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from http://www.mathjax.org before deployment.\n# The default value is: http://cdn.mathjax.org/mathjax/latest.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when enabling USE_PDFLATEX this option is only used for generating\n# bitmaps for formulas in the HTML output, but not in the Makefile that is\n# written to the output directory.\n# The default file is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. To get the times font for\n# instance you can specify\n# EXTRA_PACKAGES=times\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# http://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's config\n# file, i.e. a series of assignments. You only have to provide replacements,\n# missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's config file. A template extensions file can be generated\n# using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sf.net) file that captures the\n# structure of the code including all documentation. Note that this feature is\n# still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = YES\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot.\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif and svg.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "libraries/wallet/api_documentation_standin.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <iomanip>\n#include <boost/algorithm/string/join.hpp>\n#include <graphene/wallet/wallet.hpp>\n#include <graphene/wallet/api_documentation.hpp>\n\nnamespace graphene { namespace wallet {\n   namespace detail {\n      namespace\n      {\n         template <typename... Args>\n         struct types_to_string_list_helper;\n\n         template <typename First, typename... Args>\n         struct types_to_string_list_helper<First, Args...>\n         {\n            std::list<std::string> operator()() const\n            {\n               std::list<std::string> argsList = types_to_string_list_helper<Args...>()();\n               argsList.push_front(fc::get_typename<typename std::decay<First>::type>::name());\n               return argsList;\n            }\n         };\n\n         template <>\n         struct types_to_string_list_helper<>\n         {\n            std::list<std::string> operator()() const\n            {\n               return std::list<std::string>();\n            }\n         };\n\n         template <typename... Args>\n         std::list<std::string> types_to_string_list()\n         {\n            return types_to_string_list_helper<Args...>()();\n         }\n      } // end anonymous namespace\n\n      struct help_visitor\n      {\n         std::vector<method_description> method_descriptions;\n\n         template<typename R, typename... Args>\n         void operator()( const char* name, std::function<R(Args...)>& memb )\n         {\n            method_description this_method;\n            this_method.method_name = name;\n            std::ostringstream ss;\n            ss << std::setw(40) << std::left << fc::get_typename<R>::name() << \" \" << name << \"(\";\n            ss << boost::algorithm::join(types_to_string_list<Args...>(), \", \");\n            ss << \")\\n\";\n            this_method.brief_description = ss.str();\n            method_descriptions.push_back(this_method);\n         }\n      };\n   } // end namespace detail\n\n   api_documentation::api_documentation()\n   {\n      fc::api<wallet_api> tmp;\n      detail::help_visitor visitor;\n      tmp->visit(visitor);\n      std::copy(visitor.method_descriptions.begin(), visitor.method_descriptions.end(),\n                std::inserter(method_descriptions, method_descriptions.end()));\n   }\n\n} } // end namespace graphene::wallet\n"
  },
  {
    "path": "libraries/wallet/generate_api_documentation.pl",
    "content": "#! /usr/bin/perl\n\nuse Text::Wrap;\nuse IO::File;\n\nrequire 'doxygen/perlmod/DoxyDocs.pm';\n\nmy($outputFileName) = @ARGV;\ndie \"usage: $0 output_file_name\" unless $outputFileName;\nmy $outFile = new IO::File($outputFileName, \"w\")\n  or die \"Error opening output file: $!\";\n\nmy $fileHeader = <<'END';\n#include <set>\n#include <graphene/wallet/api_documentation.hpp>\n#include <graphene/wallet/wallet.hpp>\n\nnamespace graphene { namespace wallet {\n   namespace detail \n   {\n      struct api_method_name_collector_visitor\n      {\n         std::set<std::string> method_names;\n\n         template<typename R, typename... Args>\n         void operator()( const char* name, std::function<R(Args...)>& memb )\n         {\n            method_names.emplace(name);\n         }\n      };\n   }\n  \n   api_documentation::api_documentation()\n   {\nEND\n$outFile->print($fileHeader);\n\nfor my $class (@{$doxydocs->{classes}})\n{\n  if ($class->{name} eq 'graphene::wallet::wallet_api')\n  {\n    for my $member (@{$class->{public_methods}->{members}})\n    {\n      if ($member->{kind} eq 'function')\n      {\n        my @params = map { join(' ', cleanupDoxygenType($_->{type}), $_->{declaration_name}) } @{$member->{parameters}};\n        my $briefDescription = sprintf(\"%40s %s(%s)\\n\", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params));\n        my $escapedBriefDescription = \"\\\"\" . escapeStringForC($briefDescription) . \"\\\"\";\n        my %paramInfo = map { $_->{declaration_name} => { type => $_->{type}} } @{$member->{parameters}};\n        my $escapedDetailedDescription = \"\\\"\\\"\\n\";\n        if ($member->{detailed}->{doc})\n        {\n          my $docString = formatDocComment($member->{detailed}->{doc}, \\%paramInfo);\n          for my $line (split(/\\n/, $docString))\n          {\n            $escapedDetailedDescription .=  \"                \\\"\" .  escapeStringForC($line . \"\\n\") . \"\\\"\\n\";\n          }\n        }\n        my $codeFragment = <<\"END\";\n     {\n        method_description this_method;\n        this_method.method_name = \"$member->{name}\";\n        this_method.brief_description = $escapedBriefDescription;\n        this_method.detailed_description = $escapedDetailedDescription;\n        method_descriptions.insert(this_method);\n     }\n\nEND\n        $outFile->print($codeFragment);\n      }\n    }\n  }\n}\n\nmy $fileFooter = <<'END';\n      fc::api<wallet_api> tmp;\n      detail::api_method_name_collector_visitor visitor;\n      tmp->visit(visitor);\n      for (auto iter = method_descriptions.begin(); iter != method_descriptions.end();)\n        if (visitor.method_names.find(iter->method_name) == visitor.method_names.end())\n          iter = method_descriptions.erase(iter);\n        else\n          ++iter;\n   }\n\n} } // end namespace graphene::wallet\nEND\n$outFile->print($fileFooter);\n$outFile->close();\n\nsub cleanupDoxygenType\n{\n  my($type) = @_;\n  $type =~ s/< /</g;\n  $type =~ s/ >/>/g;\n  return $type;\n}\n\nsub formatDocComment\n{\n  my($doc, $paramInfo) = @_;\n\n  my $bodyDocs = '';\n  my $paramDocs = '';\n  my $returnDocs = '';\n\n  for (my $i = 0; $i < @{$doc}; ++$i)\n  {\n    if ($doc->[$i] eq 'params')\n    {\n      $paramDocs .= \"Parameters:\\n\";\n      @parametersList = @{$doc->[$i + 1]};\n      for my $parameter (@parametersList)\n      {\n        my $declname = $parameter->{parameters}->[0]->{name};\n        my $decltype = cleanupDoxygenType($paramInfo->{$declname}->{type});\n        $paramDocs .= Text::Wrap::fill('    ', '        ', \"$declname: \" . formatDocComment($parameter->{doc}) . \" (type: $decltype)\") . \"\\n\";\n      }\n      ++$i;\n    }\n    elsif ($doc->[$i]->{return})\n    {\n      $returnDocs .= \"Returns\\n\";\n      $returnDocs .= Text::Wrap::fill('    ','        ', formatDocComment($doc->[$i]->{return})) . \"\\n\";\n    }\n    else\n    {\n      my $docElement = $doc->[$i];\n      if ($docElement->{type} eq 'text' or $docElement->{type} eq 'url')\n      {\n        $bodyDocs .= $docElement->{content};\n      }\n      elsif ($docElement->{type} eq 'parbreak')\n      {\n        $bodyDocs .= \"\\n\\n\";\n      }\n      elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code')\n      {\n        $bodyDocs .= \"'\";\n      }\n    }\n  }\n\n  $bodyDocs =~ s/^\\s+|\\s+$//g;\n  $bodyDocs = Text::Wrap::fill('', '', $bodyDocs);\n\n  $paramDocs =~ s/^\\s+|\\s+$//g;\n  $returnDocs =~ s/^\\s+|\\s+$//g;\n\n  my $result = Text::Wrap::fill('', '', $bodyDocs);\n  $result .= \"\\n\\n\" . $paramDocs if $paramDocs;\n  $result .= \"\\n\\n\" . $returnDocs if $returnDocs;\n  \n  return $result;\n}\n\nsub escapeCharForCString\n{\n  my($char) = @_;\n  return \"\\\\a\" if $char eq \"\\x07\";\n  return \"\\\\b\" if $char eq \"\\x08\";\n  return \"\\\\f\" if $char eq \"\\x0c\";\n  return \"\\\\n\" if $char eq \"\\x0a\";\n  return \"\\\\r\" if $char eq \"\\x0d\";\n  return \"\\\\t\" if $char eq \"\\x09\";\n  return \"\\\\v\" if $char eq \"\\x0b\";\n  return \"\\\\\\\"\" if $char eq \"\\x22\";\n  return \"\\\\\\\\\" if $char eq \"\\x5c\";\n  return $char;\n}\n\nsub escapeStringForC\n{\n  my($str) = @_;\n  return join('', map { escapeCharForCString($_) } split('', $str));\n}\n\n\n"
  },
  {
    "path": "libraries/wallet/include/graphene/wallet/api_documentation.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <string>\n\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/member.hpp>\n\n#include <fc/exception/exception.hpp>\n\nnamespace graphene { namespace wallet {\n\n   struct method_description\n   {\n      std::string method_name;\n      std::string brief_description;\n      std::string detailed_description;\n   };\n\n   class api_documentation\n   {\n      typedef boost::multi_index::multi_index_container<method_description,\n         boost::multi_index::indexed_by<\n            boost::multi_index::ordered_unique<\n               boost::multi_index::member<method_description, std::string, &method_description::method_name> > > > method_description_set;\n      method_description_set method_descriptions;\n   public:\n      api_documentation();\n      std::string get_brief_description(const std::string& method_name) const\n      {\n         auto iter = method_descriptions.find(method_name);\n         if (iter != method_descriptions.end())\n            return iter->brief_description;\n         else\n            FC_THROW_EXCEPTION(fc::key_not_found_exception, \"No entry for method ${name}\", (\"name\", method_name));\n      }\n      std::string get_detailed_description(const std::string& method_name) const\n      {\n         auto iter = method_descriptions.find(method_name);\n         if (iter != method_descriptions.end())\n            return iter->detailed_description;\n         else\n            FC_THROW_EXCEPTION(fc::key_not_found_exception, \"No entry for method ${name}\", (\"name\", method_name));\n      }\n      std::vector<std::string> get_method_names() const\n      {\n         std::vector<std::string> method_names;\n         for (const method_description& method_description: method_descriptions)\n            method_names.emplace_back(method_description.method_name);\n         return method_names;\n      }\n   };\n\n} } // end namespace graphene::wallet\n"
  },
  {
    "path": "libraries/wallet/include/graphene/wallet/reflect_util.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n// This file contains various reflection methods that are used to\n// support the wallet, e.g. allow specifying operations by name\n// instead of ID.\n\n#include <string>\n#include <vector>\n#include <boost/container/flat_map.hpp>\n#include <fc/exception/exception.hpp>\n#include <fc/variant.hpp>\n\nnamespace graphene { namespace wallet {\n\nusing std::string;\nusing std::vector;\nusing boost::container::flat_map;\nusing fc::variant;\n\nstruct static_variant_map\n{\n   flat_map< string, int > name_to_which;\n   vector< string > which_to_name;\n};\n\nnamespace impl {\n\nstd::string clean_name( const std::string& name );\n\nstruct static_variant_map_visitor\n{\n   static_variant_map_visitor() {}\n\n   typedef void result_type;\n\n   template< typename T >\n   result_type operator()( const T& dummy )\n   {\n      FC_ASSERT( which == m.which_to_name.size(), \"This should not happen\" );\n      std::string name = clean_name( fc::get_typename<T>::name() );\n      m.name_to_which[ name ] = which;\n      m.which_to_name.push_back( name );\n   }\n\n   static_variant_map m;\n   uint16_t which; // 16 bit should be practically enough\n};\n\ntemplate< typename StaticVariant >\nstruct from_which_visitor\n{\n   typedef StaticVariant result_type;\n\n   template< typename Member >   // Member is member of static_variant\n   result_type operator()( const Member& dummy )\n   {\n      Member result;\n      from_variant( v, result, _max_depth );\n      return result;    // converted from StaticVariant to Result automatically due to return type\n   }\n\n   const variant& v;\n   const uint32_t _max_depth;\n\n   from_which_visitor( const variant& _v, uint32_t max_depth ) : v(_v), _max_depth(max_depth) {}\n};\n\n} // namespace impl\n\ntemplate< typename T >\nT from_which_variant( int which, const variant& v, uint32_t max_depth )\n{\n   // Parse a variant for a known which()\n   T dummy;\n   dummy.set_which( which );\n   impl::from_which_visitor< T > vtor(v, max_depth);\n   return dummy.visit( vtor );\n}\n\ntemplate<typename T>\nstatic_variant_map create_static_variant_map()\n{\n   T dummy;\n   int n = dummy.count();\n   FC_ASSERT( n <= std::numeric_limits<uint16_t>::max(), \"Too many items in this static_variant\" );\n   impl::static_variant_map_visitor vtor;\n   for( int i=0; i<n; i++ )\n   {\n      dummy.set_which(i);\n      vtor.which = i;\n      dummy.visit( vtor );\n   }\n   return vtor.m;\n}\n\n} } // namespace graphene::wallet\n"
  },
  {
    "path": "libraries/wallet/include/graphene/wallet/wallet.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/optional.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/app/api.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n#include \"wallet_structs.hpp\"\n\nnamespace fc\n{\n   void to_variant( const account_multi_index_type& accts, variant& vo, uint32_t max_depth );\n   void from_variant( const variant &var, account_multi_index_type &vo, uint32_t max_depth );\n}\n\nnamespace graphene { namespace wallet {\n\n/**\n * This class takes a variant and turns it into an object\n * of the given type, with the new operator.\n */\nobject* create_object( const variant& v );\n\n/**\n * This wallet assumes it is connected to the database server with a high-bandwidth, low-latency connection and\n * performs minimal caching. This API could be provided locally to be used by a web interface.\n */\nclass wallet_api\n{\n   public:\n      wallet_api( const wallet_data& initial_data, fc::api<login_api> rapi );\n      virtual ~wallet_api();\n\n      bool copy_wallet_file( string destination_filename );\n\n      fc::ecc::private_key derive_private_key(const std::string& prefix_string, int sequence_number) const;\n\n      /** Returns info about head block, chain_id, maintenance, participation, current active witnesses and\n       * committee members.\n       * @returns runtime info about the blockchain\n       */\n      variant                           info();\n      /** Returns info such as client version, git version of graphene/fc, version of boost, openssl.\n       * @returns compile time info and client and dependencies versions\n       */\n      variant_object                    about() const;\n      /** Returns info about a specified block.\n       * @param num height of the block to retrieve\n       * @returns info about the block, or null if not found\n       */\n      optional<signed_block_with_info>    get_block( uint32_t num );\n      /** Returns the number of accounts registered on the blockchain\n       * @returns the number of registered accounts\n       */\n      uint64_t                          get_account_count()const;\n      /** Lists all accounts controlled by this wallet.\n       * This returns a list of the full account objects for all accounts whose private keys\n       * we possess.\n       * @returns a list of account objects\n       */\n      vector<account_object>            list_my_accounts();\n      /** Lists all accounts registered in the blockchain.\n       * This returns a list of all account names and their account ids, sorted by account name.\n       *\n       * Use the \\c lowerbound and limit parameters to page through the list.  To retrieve all accounts,\n       * start by setting \\c lowerbound to the empty string \\c \"\", and then each iteration, pass\n       * the last account name returned as the \\c lowerbound for the next \\c list_accounts() call.\n       *\n       * @param lowerbound the name of the first account to return.  If the named account does not exist,\n       *                   the list will start at the account that comes after \\c lowerbound\n       * @param limit the maximum number of accounts to return (max: 1000)\n       * @returns a list of accounts mapping account names to account ids\n       */\n      map<string,account_id_type>       list_accounts(const string& lowerbound, uint32_t limit);\n      /** List the balances of an account.\n       * Each account can have multiple balances, one for each type of asset owned by that\n       * account.  The returned list will only contain assets for which the account has a\n       * nonzero balance\n       * @param id the name or id of the account whose balances you want\n       * @returns a list of the given account's balances\n       */\n      vector<asset>                     list_account_balances(const string& id);\n      /** Lists all assets registered on the blockchain.\n       *\n       * To list all assets, pass the empty string \\c \"\" for the lowerbound to start\n       * at the beginning of the list, and iterate as necessary.\n       *\n       * @param lowerbound  the symbol of the first asset to include in the list.\n       * @param limit the maximum number of assets to return (max: 100)\n       * @returns the list of asset objects, ordered by symbol\n       */\n      vector<extended_asset_object>     list_assets(const string& lowerbound, uint32_t limit)const;\n      /** Returns assets count registered on the blockchain.\n       *\n       * @returns assets count\n       */\n      uint64_t get_asset_count()const;\n\n      /** Returns the most recent operations on the named account.\n       *\n       * This returns a list of operation history objects, which describe activity on the account.\n       *\n       * @param name the name or id of the account\n       * @param limit the number of entries to return (starting from the most recent)\n       * @returns a list of \\c operation_history_objects\n       */\n      vector<operation_detail>  get_account_history(string name, int limit)const;\n\n      /** Returns the relative operations on the named account from start number.\n       *\n       * @param name the name or id of the account\n       * @param stop Sequence number of earliest operation.\n       * @param limit the number of entries to return\n       * @param start  the sequence number where to start looping back throw the history\n       * @returns a list of \\c operation_history_objects\n       */\n     vector<operation_detail>  get_relative_account_history( string name, uint32_t stop,\n                                                             int limit, uint32_t start )const;\n\n      /**\n       * @brief Fetch all objects relevant to the specified account\n       * @param name_or_id Must be the name or ID of an account to retrieve\n       * @return All info about the specified account\n       *\n       * This function fetches all relevant objects for the given account. If the string\n       * of \\c name_or_id cannot be tied to an account, that input will be ignored.\n       *\n       */\n      full_account                      get_full_account( const string& name_or_id );\n\n      /**\n       * @brief Get OHLCV data of a trading pair in a time range\n       * @param symbol name or ID of the base asset\n       * @param symbol2 name or ID of the quote asset\n       * @param bucket length of each time bucket in seconds.\n       * @param start the start of a time range, E.G. \"2018-01-01T00:00:00\"\n       * @param end the end of the time range\n       * @return A list of OHLCV data, in \"least recent first\" order.\n       */\n      vector<bucket_object>             get_market_history( string symbol, string symbol2, uint32_t bucket,\n                                                            fc::time_point_sec start, fc::time_point_sec end )const;\n\n      /**\n       * @brief Fetch all orders relevant to the specified account sorted descendingly by price\n       *\n       * @param name_or_id  The name or ID of an account to retrieve\n       * @param base  Base asset\n       * @param quote  Quote asset\n       * @param limit  The limitation of items each query can fetch (max: 101)\n       * @param ostart_id  Start order id, fetch orders which price are lower than or equal to this order\n       * @param ostart_price  Fetch orders with price lower than or equal to this price\n       *\n       * @return List of orders from \\c name_or_id to the corresponding account\n       *\n       * @note\n       * 1. if \\c name_or_id cannot be tied to an account, empty result will be returned\n       * 2. \\c ostart_id and \\c ostart_price can be \\c null, if so the api will return the \"first page\" of orders;\n       *    if \\c ostart_id is specified and valid, its price will be used to do page query preferentially,\n       *    otherwise the \\c ostart_price will be used\n       */\n      vector<limit_order_object>        get_account_limit_orders( const string& name_or_id,\n                                            const string &base,\n                                            const string &quote,\n                                            uint32_t limit = 101,\n                                            optional<limit_order_id_type> ostart_id = optional<limit_order_id_type>(),\n                                            optional<price> ostart_price = optional<price>());\n\n      /**\n       * @brief Get limit orders in a given market\n       * @param a symbol or ID of asset being sold\n       * @param b symbol or ID of asset being purchased\n       * @param limit Maximum number of orders to retrieve\n       * @return The limit orders, ordered from least price to greatest\n       */\n      vector<limit_order_object>        get_limit_orders(string a, string b, uint32_t limit)const;\n\n      /**\n       * @brief Get call orders (aka margin positions) for a given asset\n       * @param a symbol name or ID of the debt asset\n       * @param limit Maximum number of orders to retrieve\n       * @return The call orders, ordered from earliest to be called to latest\n       */\n      vector<call_order_object>         get_call_orders(string a, uint32_t limit)const;\n\n      /**\n       * @brief Get forced settlement orders in a given asset\n       * @param a Symbol or ID of asset being settled\n       * @param limit Maximum number of orders to retrieve\n       * @return The settle orders, ordered from earliest settlement date to latest\n       */\n      vector<force_settlement_object>   get_settle_orders(string a, uint32_t limit)const;\n\n      /** Returns the collateral_bid object for the given MPA\n       *\n       * @param asset the name or id of the asset\n       * @param limit the number of entries to return\n       * @param start the sequence number where to start looping back throw the history\n       * @returns a list of \\c collateral_bid_objects\n       */\n      vector<collateral_bid_object> get_collateral_bids(string asset, uint32_t limit = 100, uint32_t start = 0)const;\n\n      /** Returns the block chain's slowly-changing settings.\n       * This object contains all of the properties of the blockchain that are fixed\n       * or that change only once per maintenance interval (daily) such as the\n       * current list of witnesses, committee_members, block interval, etc.\n       * @see \\c get_dynamic_global_properties() for frequently changing properties\n       * @returns the global properties\n       */\n      global_property_object            get_global_properties() const;\n\n      /**\n       * Get operations relevant to the specified account filtering by operation type, with transaction id\n       *\n       * @param name the name or id of the account, whose history shoulde be queried\n       * @param operation_types The IDs of the operation we want to get operations in the account\n       *                        ( 0 = transfer , 1 = limit order create, ...)\n       * @param start the sequence number where to start looping back throw the history\n       * @param limit the max number of entries to return (from start number)\n       * @returns account_history_operation_detail\n       */\n      account_history_operation_detail get_account_history_by_operations( string name,\n                                                                          flat_set<uint16_t> operation_types,\n                                                                          uint32_t start, int limit);\n\n      /** Returns the block chain's rapidly-changing properties.\n       * The returned object contains information that changes every block interval\n       * such as the head block number, the next witness, etc.\n       * @see \\c get_global_properties() for less-frequently changing properties\n       * @returns the dynamic global properties\n       */\n      dynamic_global_property_object    get_dynamic_global_properties() const;\n\n      /** Returns information about the given account.\n       *\n       * @param account_name_or_id the name or ID of the account to provide information about\n       * @returns the public account data stored in the blockchain\n       */\n      account_object                    get_account(string account_name_or_id) const;\n\n      /** Returns information about the given asset.\n       * @param asset_name_or_id the symbol or id of the asset in question\n       * @returns the information about the asset stored in the block chain\n       */\n      extended_asset_object             get_asset(string asset_name_or_id) const;\n\n      /** Returns the BitAsset-specific data for a given asset.\n       * Market-issued assets's behavior are determined both by their \"BitAsset Data\" and\n       * their basic asset data, as returned by \\c get_asset().\n       * @param asset_name_or_id the symbol or id of the BitAsset in question\n       * @returns the BitAsset-specific data for this asset\n       */\n      asset_bitasset_data_object        get_bitasset_data(string asset_name_or_id)const;\n\n      /**\n       * Returns information about the given HTLC object.\n       * @param htlc_id the id of the HTLC object.\n       * @returns the information about the HTLC object\n       */\n      fc::optional<fc::variant>             get_htlc(string htlc_id) const;\n\n      /** Lookup the id of a named account.\n       * @param account_name_or_id the name or ID of the account to look up\n       * @returns the id of the named account\n       */\n      account_id_type                   get_account_id(string account_name_or_id) const;\n\n      /**\n       * Lookup the id of a named asset.\n       * @param asset_name_or_id the symbol or ID of an asset to look up\n       * @returns the id of the given asset\n       */\n      asset_id_type                     get_asset_id(string asset_name_or_id) const;\n\n      /**\n       * Returns the blockchain object corresponding to the given id.\n       *\n       * This generic function can be used to retrieve any object from the blockchain\n       * that is assigned an ID.  Certain types of objects have specialized convenience\n       * functions to return their objects -- e.g., assets have \\c get_asset(), accounts\n       * have \\c get_account(), but this function will work for any object.\n       *\n       * @param id the id of the object to return\n       * @returns the requested object\n       */\n      variant                           get_object(object_id_type id) const;\n\n      /** Returns the current wallet filename.\n       *\n       * This is the filename that will be used when automatically saving the wallet.\n       *\n       * @see set_wallet_filename()\n       * @return the wallet filename\n       */\n      string                            get_wallet_filename() const;\n\n      /**\n       * Get the WIF private key corresponding to a public key.  The\n       * private key must already be in the wallet.\n       * @param pubkey a public key in Base58 format\n       * @return the WIF private key\n       */\n      string                            get_private_key( public_key_type pubkey )const;\n\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Create a new transaction builder.\n       * @return handle of the new transaction builder\n       */\n      transaction_handle_type begin_builder_transaction();\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Append a new operation to a transaction builder.\n       * @param transaction_handle handle of the transaction builder\n       * @param op the operation in JSON format\n       */\n      void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op);\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Replace an operation in a transaction builder with a new operation.\n       * @param handle handle of the transaction builder\n       * @param operation_index the index of the old operation in the builder to be replaced\n       * @param new_op the new operation in JSON format\n       */\n      void replace_operation_in_builder_transaction(transaction_handle_type handle,\n                                                    unsigned operation_index,\n                                                    const operation& new_op);\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Calculate and update fees for the operations in a transaction builder.\n       * @param handle handle of the transaction builder\n       * @param fee_asset name or ID of an asset that to be used to pay fees\n       * @return total fees\n       */\n      asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL);\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Show content of a transaction builder.\n       * @param handle handle of the transaction builder\n       * @return a transaction\n       */\n      transaction preview_builder_transaction(transaction_handle_type handle);\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Sign the transaction in a transaction builder and optionally broadcast to the network.\n       * @param transaction_handle handle of the transaction builder\n       * @param broadcast whether to broadcast the signed transaction to the network\n       * @return a signed transaction\n       */\n      signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true);\n\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Sign the transaction in a transaction builder and optionally broadcast to the network.\n       * @param transaction_handle handle of the transaction builder\n       * @param signing_keys Keys that must be used when signing the transaction\n       * @param broadcast whether to broadcast the signed transaction to the network\n       * @return a signed transaction\n       */\n      signed_transaction sign_builder_transaction2(transaction_handle_type transaction_handle,\n                                                  const vector<public_key_type>& signing_keys = vector<public_key_type>(),\n                                                  bool broadcast = true);\n\n      /** Broadcast signed transaction\n       * @param tx signed transaction\n       * @returns the transaction ID along with the signed transaction.\n       */\n      pair<transaction_id_type,signed_transaction> broadcast_transaction(signed_transaction tx);\n\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Create a proposal containing the operations in a transaction builder (create a new proposal_create\n       * operation, then replace the transaction builder with the new operation), then sign the transaction\n       * and optionally broadcast to the network.\n       *\n       * Note: this command is buggy because unable to specify proposer. It will be deprecated in a future release.\n       *       Please use \\c propose_builder_transaction2() instead.\n       *\n       * @param handle handle of the transaction builder\n       * @param expiration when the proposal will expire\n       * @param review_period_seconds review period of the proposal in seconds\n       * @param broadcast whether to broadcast the signed transaction to the network\n       * @return a signed transaction\n       */\n      signed_transaction propose_builder_transaction(\n          transaction_handle_type handle,\n          time_point_sec expiration = time_point::now() + fc::minutes(1),\n          uint32_t review_period_seconds = 0,\n          bool broadcast = true\n         );\n\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Create a proposal containing the operations in a transaction builder (create a new proposal_create\n       * operation, then replace the transaction builder with the new operation), then sign the transaction\n       * and optionally broadcast to the network.\n       *\n       * @param handle handle of the transaction builder\n       * @param account_name_or_id name or ID of the account who would pay fees for creating the proposal\n       * @param expiration when the proposal will expire\n       * @param review_period_seconds review period of the proposal in seconds\n       * @param broadcast whether to broadcast the signed transaction to the network\n       * @return a signed transaction\n       */\n      signed_transaction propose_builder_transaction2(\n         transaction_handle_type handle,\n         string account_name_or_id,\n         time_point_sec expiration = time_point::now() + fc::minutes(1),\n         uint32_t review_period_seconds = 0,\n         bool broadcast = true\n        );\n\n      /**\n       * @ingroup Transaction Builder API\n       *\n       * Destroy a transaction builder.\n       * @param handle handle of the transaction builder\n       */\n      void remove_builder_transaction(transaction_handle_type handle);\n\n      /** Checks whether the wallet has just been created and has not yet had a password set.\n       *\n       * Calling \\c set_password will transition the wallet to the locked state.\n       * @return true if the wallet is new\n       * @ingroup Wallet Management\n       */\n      bool    is_new()const;\n\n      /** Checks whether the wallet is locked (is unable to use its private keys).\n       *\n       * This state can be changed by calling \\c lock() or \\c unlock().\n       * @return true if the wallet is locked\n       * @ingroup Wallet Management\n       */\n      bool    is_locked()const;\n\n      /** Locks the wallet immediately.\n       * @ingroup Wallet Management\n       */\n      void    lock();\n\n      /** Unlocks the wallet.\n       *\n       * The wallet remain unlocked until the \\c lock is called\n       * or the program exits.\n       *\n       * When used in command line, if typed \"unlock\" without a password followed, the user will be prompted\n       * to input a password without echo.\n       *\n       * @param password the password previously set with \\c set_password()\n       * @ingroup Wallet Management\n       */\n      void    unlock(string password);\n\n      /** Sets a new password on the wallet.\n       *\n       * The wallet must be either 'new' or 'unlocked' to\n       * execute this command.\n       *\n       * When used in command line, if typed \"set_password\" without a password followed, the user will be prompted\n       * to input a password without echo.\n       *\n       * @param password a new password\n       * @ingroup Wallet Management\n       */\n      void    set_password(string password);\n\n      /** Dumps all private keys owned by the wallet.\n       *\n       * The keys are printed in WIF format.  You can import these keys into another wallet\n       * using \\c import_key()\n       * @returns a map containing the private keys, indexed by their public key\n       */\n      map<public_key_type, string> dump_private_keys();\n\n      /** Returns a list of all commands supported by the wallet API.\n       *\n       * This lists each command, along with its arguments and return types.\n       * For more detailed help on a single command, use \\c gethelp()\n       *\n       * @returns a multi-line string suitable for displaying on a terminal\n       */\n      string  help()const;\n\n      /** Returns detailed help on a single API command.\n       * @param method the name of the API command you want help with\n       * @returns a multi-line string suitable for displaying on a terminal\n       */\n      string  gethelp(const string& method)const;\n\n      /** Loads a specified BitShares wallet.\n       *\n       * The current wallet is closed before the new wallet is loaded.\n       *\n       * @warning This does not change the filename that will be used for future\n       * wallet writes, so this may cause you to overwrite your original\n       * wallet unless you also call \\c set_wallet_filename()\n       *\n       * @param wallet_filename the filename of the wallet JSON file to load.\n       *                        If \\c wallet_filename is empty, it reloads the\n       *                        existing wallet file\n       * @returns true if the specified wallet is loaded\n       */\n      bool    load_wallet_file(string wallet_filename = \"\");\n\n      /** Quit from the wallet.\n       *\n       * The current wallet will be closed and saved.\n       */\n      void    quit();\n\n      /** Saves the current wallet to the given filename.\n       *\n       * @warning This does not change the wallet filename that will be used for future\n       * writes, so think of this function as 'Save a Copy As...' instead of\n       * 'Save As...'.  Use \\c set_wallet_filename() to make the filename\n       * persist.\n       * @param wallet_filename the filename of the new wallet JSON file to create\n       *                        or overwrite.  If \\c wallet_filename is empty,\n       *                        save to the current filename.\n       */\n      void    save_wallet_file(string wallet_filename = \"\");\n\n      /** Sets the wallet filename used for future writes.\n       *\n       * This does not trigger a save, it only changes the default filename\n       * that will be used the next time a save is triggered.\n       *\n       * @param wallet_filename the new filename to use for future saves\n       */\n      void    set_wallet_filename(string wallet_filename);\n\n      /** Suggests a safe brain key to use for creating your account.\n       * \\c create_account_with_brain_key() requires you to specify a 'brain key',\n       * a long passphrase that provides enough entropy to generate cyrptographic\n       * keys.  This function will suggest a suitably random string that should\n       * be easy to write down (and, with effort, memorize).\n       * @returns a suggested brain_key\n       */\n      brain_key_info suggest_brain_key()const;\n\n     /**\n      * Derive any number of *possible* owner keys from a given brain key.\n      *\n      * NOTE: These keys may or may not match with the owner keys of any account.\n      * This function is merely intended to assist with account or key recovery.\n      *\n      * @see suggest_brain_key()\n      *\n      * @param brain_key    Brain key\n      * @param number_of_desired_keys  Number of desired keys\n      * @return A list of keys that are deterministically derived from the brainkey\n      */\n     vector<brain_key_info> derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys = 1) const;\n\n     /**\n      * Determine whether a textual representation of a public key\n      * (in Base-58 format) is *currently* linked\n      * to any *registered* (i.e. non-stealth) account on the blockchain\n      * @param public_key Public key\n      * @return Whether a public key is known\n      */\n     bool is_public_key_registered(string public_key) const;\n\n      /** Converts a signed_transaction in JSON form to its binary representation.\n       *\n       * @param tx the transaction to serialize\n       * @returns the binary form of the transaction.  It will not be hex encoded,\n       *          this returns a raw string that may have null characters embedded\n       *          in it\n       */\n      string serialize_transaction(signed_transaction tx) const;\n\n      /** Imports the private key for an existing account.\n       *\n       * The private key must match either an owner key or an active key for the\n       * named account.\n       *\n       * @see dump_private_keys()\n       *\n       * @param account_name_or_id the account owning the key\n       * @param wif_key the private key in WIF format\n       * @returns true if the key was imported\n       */\n      bool import_key(string account_name_or_id, string wif_key);\n\n      /** Imports accounts from a BitShares 0.x wallet file.\n       * Current wallet file must be unlocked to perform the import.\n       *\n       * @param filename the BitShares 0.x wallet file to import\n       * @param password the password to encrypt the BitShares 0.x wallet file\n       * @returns a map containing the accounts found and whether imported\n       */\n      map<string, bool> import_accounts( string filename, string password );\n\n      /** Imports from a BitShares 0.x wallet file, find keys that were bound to a given account name on the\n       * BitShares 0.x chain, rebind them to an account name on the 2.0 chain.\n       * Current wallet file must be unlocked to perform the import.\n       *\n       * @param filename the BitShares 0.x wallet file to import\n       * @param password the password to encrypt the BitShares 0.x wallet file\n       * @param src_account_name name of the account on BitShares 0.x chain\n       * @param dest_account_name name of the account on BitShares 2.0 chain,\n       *                          can be same or different to \\c src_account_name\n       * @returns whether the import has succeeded\n       */\n      bool import_account_keys( string filename, string password, string src_account_name, string dest_account_name );\n\n      /**\n       * This call will construct transaction(s) that will claim all balances controled\n       * by wif_keys and deposit them into the given account.\n       *\n       * @param account_name_or_id name or ID of an account that to claim balances to\n       * @param wif_keys private WIF keys of balance objects to claim balances from\n       * @param broadcast true to broadcast the transaction on the network\n       */\n      vector< signed_transaction > import_balance( string account_name_or_id, const vector<string>& wif_keys,\n                                                   bool broadcast );\n\n      /** Transforms a brain key to reduce the chance of errors when re-entering the key from memory.\n       *\n       * This takes a user-supplied brain key and normalizes it into the form used\n       * for generating private keys.  In particular, this upper-cases all ASCII characters\n       * and collapses multiple spaces into one.\n       * @param s the brain key as supplied by the user\n       * @returns the brain key in its normalized form\n       */\n      string normalize_brain_key(string s) const;\n\n      /** Registers a third party's account on the blockckain.\n       *\n       * This function is used to register an account for which you do not own the private keys.\n       * When acting as a registrar, an end user will generate their own private keys and send\n       * you the public keys.  The registrar will use this function to register the account\n       * on behalf of the end user.\n       *\n       * @see create_account_with_brain_key()\n       *\n       * @param name the name of the account, must be unique on the blockchain.  Shorter names\n       *             are more expensive to register; the rules are still in flux, but in general\n       *             names of more than 8 characters with at least one digit will be cheap.\n       * @param owner the owner key for the new account\n       * @param active the active key for the new account\n       * @param registrar_account the account which will pay the fee to register the user\n       * @param referrer_account the account who is acting as a referrer, and may receive a\n       *                         portion of the user's transaction fees.  This can be the\n       *                         same as the registrar_account if there is no referrer.\n       * @param referrer_percent the percentage (0 - 100) of the new user's transaction fees\n       *                         not claimed by the blockchain that will be distributed to the\n       *                         referrer; the rest will be sent to the registrar.  Will be\n       *                         multiplied by GRAPHENE_1_PERCENT when constructing the transaction.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction registering the account\n       */\n      signed_transaction register_account(string name,\n                                          public_key_type owner,\n                                          public_key_type active,\n                                          string  registrar_account,\n                                          string  referrer_account,\n                                          uint32_t referrer_percent,\n                                          bool broadcast = false);\n\n      /**\n       *  Upgrades an account to prime status.\n       *  This makes the account holder a 'lifetime member'.\n       *\n       * @param name the name or id of the account to upgrade\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction upgrading the account\n       */\n      signed_transaction upgrade_account(string name, bool broadcast);\n\n      /** Creates a new account and registers it on the blockchain.\n       *\n       * @todo why no referrer_percent here?\n       *\n       * @see suggest_brain_key()\n       * @see register_account()\n       *\n       * @param brain_key the brain key used for generating the account's private keys\n       * @param account_name the name of the account, must be unique on the blockchain.  Shorter names\n       *                     are more expensive to register; the rules are still in flux, but in general\n       *                     names of more than 8 characters with at least one digit will be cheap.\n       * @param registrar_account the account which will pay the fee to register the user\n       * @param referrer_account the account who is acting as a referrer, and may receive a\n       *                         portion of the user's transaction fees.  This can be the\n       *                         same as the registrar_account if there is no referrer.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction registering the account\n       */\n      signed_transaction create_account_with_brain_key(string brain_key,\n                                                       string account_name,\n                                                       string registrar_account,\n                                                       string referrer_account,\n                                                       bool broadcast = false);\n\n      /** Transfer an amount from one account to another.\n       * @param from the name or id of the account sending the funds\n       * @param to the name or id of the account receiving the funds\n       * @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5)\n       * @param asset_symbol the symbol or id of the asset to send\n       * @param memo a memo to attach to the transaction.  The memo will be encrypted in the\n       *             transaction and readable for the receiver.  There is no length limit\n       *             other than the limit imposed by maximum transaction size, but transaction\n       *             increase with transaction size\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction transferring funds\n       */\n      signed_transaction transfer(string from,\n                                  string to,\n                                  string amount,\n                                  string asset_symbol,\n                                  string memo,\n                                  bool broadcast = false);\n\n      /**\n       *  This method works just like transfer, except it always broadcasts and\n       *  returns the transaction ID (hash) along with the signed transaction.\n       * @param from the name or id of the account sending the funds\n       * @param to the name or id of the account receiving the funds\n       * @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5)\n       * @param asset_symbol the symbol or id of the asset to send\n       * @param memo a memo to attach to the transaction.  The memo will be encrypted in the\n       *             transaction and readable for the receiver.  There is no length limit\n       *             other than the limit imposed by maximum transaction size, but transaction\n       *             increase with transaction size\n       * @returns the transaction ID (hash) along with the signed transaction transferring funds\n       */\n      pair<transaction_id_type,signed_transaction> transfer2(string from,\n                                                             string to,\n                                                             string amount,\n                                                             string asset_symbol,\n                                                             string memo ) {\n         auto trx = transfer( from, to, amount, asset_symbol, memo, true );\n         return std::make_pair(trx.id(),trx);\n      }\n\n\n      /**\n       *  This method is used to convert a JSON transaction to its transactin ID.\n       * @param trx a JSON transaction\n       * @return the ID (hash) of the transaction\n       */\n      transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); }\n\n\n      /** Sign a memo message.\n       *\n       * @param from the name or id of signing account; or a public key\n       * @param to the name or id of receiving account; or a public key\n       * @param memo text to sign\n       * @return the signed memo data\n       */\n      memo_data sign_memo(string from, string to, string memo);\n\n      /** Read a memo.\n       *\n       * @param memo JSON-enconded memo.\n       * @returns string with decrypted message.\n       */\n      string read_memo(const memo_data& memo);\n\n\n      /** Sign a message using an account's memo key. The signature is generated as in\n       *   in https://github.com/xeroc/python-graphenelib/blob/d9634d74273ebacc92555499eca7c444217ecba0/graphenecommon/message.py#L64 .\n       *\n       * @param signer the name or id of signing account\n       * @param message text to sign\n       * @return the signed message in an abstract format\n       */\n      signed_message sign_message(string signer, string message);\n\n      /** Verify a message signed with sign_message using the given account's memo key.\n       *\n       * @param message the message text\n       * @param account the account name of the message\n       * @param block the block number of the message\n       * @param time the timestamp of the message\n       * @param sig the message signature\n       * @return true if signature matches\n       */\n      bool verify_message( string message, string account, int block, const string& time, compact_signature sig );\n\n      /** Verify a message signed with sign_message\n       *\n       * @param message the signed_message structure containing message, meta data and signature\n       * @return true if signature matches\n       */\n      bool verify_signed_message( signed_message message );\n\n      /** Verify a message signed with sign_message, in its encapsulated form.\n       *\n       * @param message the complete encapsulated message string including separators and line feeds\n       * @return true if signature matches\n       */\n      bool verify_encapsulated_message( string message );\n\n      /** These methods are used for stealth transfers */\n      ///@{\n      /**\n       * This method can be used to set a label for a public key\n       *\n       * @note No two keys can have the same label.\n       * @param key a public key\n       * @param label a user-defined string as label\n       * @return true if the label was set, otherwise false\n       */\n      bool                        set_key_label( public_key_type key, string label );\n\n      /**\n       * Get label of a public key.\n       * @param key a public key\n       * @return the label if already set by \\c set_key_label(), or an empty string if not set\n       */\n      string                      get_key_label( public_key_type key )const;\n\n      /**\n       * Generates a new blind account for the given brain key and assigns it the given label.\n       * @param label a label\n       * @param brain_key the brain key to be used to generate a new blind account\n       * @return the public key of the new account\n       */\n      public_key_type             create_blind_account( string label, string brain_key  );\n\n      /**\n       * Return the total balances of all blinded commitments that can be claimed by the\n       * given account key or label.\n       * @param key_or_label a public key in Base58 format or a label\n       * @return the total balances of all blinded commitments that can be claimed by the\n       * given account key or label\n       */\n      vector<asset>                get_blind_balances( string key_or_label );\n      /**\n       * Get all blind accounts.\n       * @return all blind accounts\n       */\n      map<string,public_key_type> get_blind_accounts()const;\n      /**\n       * Get all blind accounts for which this wallet has the private key.\n       * @return all blind accounts for which this wallet has the private key\n       */\n      map<string,public_key_type> get_my_blind_accounts()const;\n      /**\n       * Get the public key associated with a given label.\n       * @param label a label\n       * @return the public key associated with the given label\n       */\n      public_key_type             get_public_key( string label )const;\n      ///@}\n\n      /**\n       * Get all blind receipts to/form a particular account\n       * @param key_or_account a public key in Base58 format or an account\n       * @return all blind receipts to/form the account\n       */\n      vector<blind_receipt> blind_history( string key_or_account );\n\n      /**\n       * Given a confirmation receipt, this method will parse it for a blinded balance and confirm\n       * that it exists in the blockchain.  If it exists then it will report the amount received and\n       * who sent it.\n       *\n       * @param confirmation_receipt a base58 encoded stealth confirmation\n       * @param opt_from if not empty and the sender is a unknown public key,\n       *                 then the unknown public key will be given the label \\c opt_from\n       * @param opt_memo a self-defined label for this transfer to be saved in local wallet file\n       * @return a blind receipt\n       */\n      blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo );\n\n      /**\n       * Transfers a public balance from \\c from_account_id_or_name to one or more blinded balances using a\n       * stealth transfer.\n       * @param from_account_id_or_name ID or name of an account to transfer from\n       * @param asset_symbol symbol or ID of the asset to be transferred\n       * @param to_amounts map from key or label to amount\n       * @param broadcast true to broadcast the transaction on the network\n       * @return a blind confirmation\n       */\n      blind_confirmation transfer_to_blind( string from_account_id_or_name,\n                                            string asset_symbol,\n                                            vector<pair<string, string>> to_amounts,\n                                            bool broadcast = false );\n\n      /**\n       * Transfers funds from a set of blinded balances to a public account balance.\n       * @param from_blind_account_key_or_label a public key in Base58 format or a label to transfer from\n       * @param to_account_id_or_name ID or name of an account to transfer to\n       * @param amount the amount to be transferred\n       * @param asset_symbol symbol or ID of the asset to be transferred\n       * @param broadcast true to broadcast the transaction on the network\n       * @return a blind confirmation\n       */\n      blind_confirmation transfer_from_blind(\n                                            string from_blind_account_key_or_label,\n                                            string to_account_id_or_name,\n                                            string amount,\n                                            string asset_symbol,\n                                            bool broadcast = false );\n\n      /**\n       * Transfer from one set of blinded balances to another.\n       * @param from_key_or_label a public key in Base58 format or a label to transfer from\n       * @param to_key_or_label a public key in Base58 format or a label to transfer to\n       * @param amount the amount to be transferred\n       * @param symbol symbol or ID of the asset to be transferred\n       * @param broadcast true to broadcast the transaction on the network\n       * @return a blind confirmation\n       */\n      blind_confirmation blind_transfer( string from_key_or_label,\n                                         string to_key_or_label,\n                                         string amount,\n                                         string symbol,\n                                         bool broadcast = false );\n\n      /** Place a limit order attempting to sell one asset for another.\n       *\n       * Buying and selling are the same operation on BitShares; if you want to buy BTS\n       * with USD, you should sell USD for BTS.\n       *\n       * The blockchain will attempt to sell the \\c symbol_to_sell for as\n       * much \\c symbol_to_receive as possible, as long as the price is at\n       * least \\c min_to_receive / \\c amount_to_sell.\n       *\n       * In addition to the transaction fees, market fees will apply as specified\n       * by the issuer of both the selling asset and the receiving asset as\n       * a percentage of the amount exchanged.\n       *\n       * If either the selling asset or the receiving asset is whitelist\n       * restricted, the order will only be created if the seller is on\n       * the whitelist of the restricted asset type.\n       *\n       * Market orders are matched in the order they are included\n       * in the block chain.\n       *\n       * @todo Document default/max expiration time\n       *\n       * @param seller_account the account providing the asset being sold, and which will\n       *                       receive the proceeds of the sale.\n       * @param amount_to_sell the amount of the asset being sold to sell (in nominal units)\n       * @param symbol_to_sell the name or id of the asset to sell\n       * @param min_to_receive the minimum amount you are willing to receive in return for\n       *                       selling the entire amount_to_sell\n       * @param symbol_to_receive the name or id of the asset you wish to receive\n       * @param timeout_sec if the order does not fill immediately, this is the length of\n       *                    time the order will remain on the order books before it is\n       *                    cancelled and the un-spent funds are returned to the seller's\n       *                    account\n       * @param fill_or_kill if true, the order will only be included in the blockchain\n       *                     if it is filled immediately; if false, an open order will be\n       *                     left on the books to fill any amount that cannot be filled\n       *                     immediately.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction selling the funds\n       */\n      signed_transaction sell_asset(string seller_account,\n                                    string amount_to_sell,\n                                    string   symbol_to_sell,\n                                    string min_to_receive,\n                                    string   symbol_to_receive,\n                                    uint32_t timeout_sec = 0,\n                                    bool     fill_or_kill = false,\n                                    bool     broadcast = false);\n\n      /** Borrow an asset or update the debt/collateral ratio for the loan.\n       *\n       * This is the first step in shorting an asset.  Call \\c sell_asset() to complete the short.\n       *\n       * @param borrower_name the name or id of the account associated with the transaction.\n       * @param amount_to_borrow the amount of the asset being borrowed.  Make this value\n       *                         negative to pay back debt.\n       * @param asset_symbol the symbol or id of the asset being borrowed.\n       * @param amount_of_collateral the amount of the backing asset to add to your collateral\n       *        position.  Make this negative to claim back some of your collateral.\n       *        The backing asset is defined in the \\c bitasset_options for the asset being borrowed.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction borrowing the asset\n       */\n      signed_transaction borrow_asset(string borrower_name, string amount_to_borrow, string asset_symbol,\n                                      string amount_of_collateral, bool broadcast = false);\n\n      /** Borrow an asset or update the debt/collateral ratio for the loan, with additional options.\n       *\n       * This is the first step in shorting an asset.  Call \\c sell_asset() to complete the short.\n       *\n       * @param borrower_name the name or id of the account associated with the transaction.\n       * @param amount_to_borrow the amount of the asset being borrowed.  Make this value\n       *                         negative to pay back debt.\n       * @param asset_symbol the symbol or id of the asset being borrowed.\n       * @param amount_of_collateral the amount of the backing asset to add to your collateral\n       *        position.  Make this negative to claim back some of your collateral.\n       *        The backing asset is defined in the \\c bitasset_options for the asset being borrowed.\n       * @param extensions additional options\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction borrowing the asset\n       */\n      signed_transaction borrow_asset_ext( string borrower_name, string amount_to_borrow, string asset_symbol,\n                                           string amount_of_collateral,\n                                           call_order_update_operation::extensions_type extensions,\n                                           bool broadcast = false );\n\n      /** Cancel an existing order\n       *\n       * @param order_id the id of order to be cancelled\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction canceling the order\n       */\n      signed_transaction cancel_order(object_id_type order_id, bool broadcast = false);\n\n      /** Creates a new user-issued or market-issued asset.\n       *\n       * Many options can be changed later using \\c update_asset()\n       *\n       * Right now this function is difficult to use because you must provide raw JSON data\n       * structures for the options objects, and those include prices and asset ids.\n       *\n       * @param issuer the name or id of the account who will pay the fee and become the\n       *               issuer of the new asset.  This can be updated later\n       * @param symbol the ticker symbol of the new asset\n       * @param precision the number of digits of precision to the right of the decimal point,\n       *                  must be less than or equal to 12\n       * @param common asset options required for all new assets.\n       *               Note that core_exchange_rate technically needs to store the asset ID of\n       *               this new asset. Since this ID is not known at the time this operation is\n       *               created, create this price as though the new asset has instance ID 1, and\n       *               the chain will overwrite it with the new asset's ID.\n       * @param bitasset_opts options specific to BitAssets.  This may be null unless the\n       *               \\c market_issued flag is set in common.flags\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction creating a new asset\n       */\n      signed_transaction create_asset(string issuer,\n                                      string symbol,\n                                      uint8_t precision,\n                                      asset_options common,\n                                      fc::optional<bitasset_options> bitasset_opts,\n                                      bool broadcast = false);\n\n      /** Issue new shares of an asset.\n       *\n       * @param to_account the name or id of the account to receive the new shares\n       * @param amount the amount to issue, in nominal units\n       * @param symbol the ticker symbol of the asset to issue\n       * @param memo a memo to include in the transaction, readable by the recipient\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction issuing the new shares\n       */\n      signed_transaction issue_asset(string to_account, string amount,\n                                     string symbol,\n                                     string memo,\n                                     bool broadcast = false);\n\n      /** Update the core options on an asset.\n       * There are a number of options which all assets in the network use. These options are\n       * enumerated in the asset_object::asset_options struct. This command is used to update\n       * these options for an existing asset.\n       *\n       * @note This operation cannot be used to update BitAsset-specific options. For these options,\n       * \\c update_bitasset() instead.\n       *\n       * @param symbol the name or id of the asset to update\n       * @param new_issuer if changing the asset's issuer, the name or id of the new issuer.\n       *                   null if you wish to remain the issuer of the asset\n       * @param new_options the new asset_options object, which will entirely replace the existing\n       *                    options.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction updating the asset\n       */\n      signed_transaction update_asset(string symbol,\n                                      optional<string> new_issuer,\n                                      asset_options new_options,\n                                      bool broadcast = false);\n\n      /** Update the issuer of an asset\n       * Since this call requires the owner authority of the current issuer to sign the transaction,\n       * a separated operation is used to change the issuer. This call simplifies the use of this action.\n       *\n       * @note This operation requires the owner key to be available in the wallet.\n       *\n       * @param symbol the name or id of the asset to update\n       * @param new_issuer if changing the asset's issuer, the name or id of the new issuer.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction updating the asset\n       */\n      signed_transaction update_asset_issuer(string symbol,\n                                             string new_issuer,\n                                             bool broadcast = false);\n\n      /** Update the options specific to a BitAsset.\n       *\n       * BitAssets have some options which are not relevant to other asset types. This operation is used to update those\n       * options an an existing BitAsset.\n       *\n       * @see update_asset()\n       *\n       * @param symbol the name or id of the asset to update, which must be a market-issued asset\n       * @param new_options the new bitasset_options object, which will entirely replace the existing\n       *                    options.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction updating the bitasset\n       */\n      signed_transaction update_bitasset(string symbol,\n                                         bitasset_options new_options,\n                                         bool broadcast = false);\n\n      /** Update the set of feed-producing accounts for a BitAsset.\n       *\n       * BitAssets have price feeds selected by taking the median values of recommendations from a set of feed producers.\n       * This command is used to specify which accounts may produce feeds for a given BitAsset.\n       * @param symbol the name or id of the asset to update\n       * @param new_feed_producers a list of account names or ids which are authorized to produce feeds for the asset.\n       *                           this list will completely replace the existing list\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction updating the bitasset's feed producers\n       */\n      signed_transaction update_asset_feed_producers(string symbol,\n                                                     flat_set<string> new_feed_producers,\n                                                     bool broadcast = false);\n\n      /** Publishes a price feed for the named asset.\n       *\n       * Price feed providers use this command to publish their price feeds for market-issued assets. A price feed is\n       * used to tune the market for a particular market-issued asset. For each value in the feed, the median across all\n       * committee_member feeds for that asset is calculated and the market for the asset is configured with the median of that\n       * value.\n       *\n       * The feed object in this command contains three prices: a call price limit, a short price limit, and a settlement price.\n       * The call limit price is structured as (collateral asset) / (debt asset) and the short limit price is structured\n       * as (asset for sale) / (collateral asset). Note that the asset IDs are opposite to eachother, so if we're\n       * publishing a feed for USD, the call limit price will be CORE/USD and the short limit price will be USD/CORE. The\n       * settlement price may be flipped either direction, as long as it is a ratio between the market-issued asset and\n       * its collateral.\n       *\n       * @param publishing_account the account publishing the price feed\n       * @param symbol the name or id of the asset whose feed we're publishing\n       * @param feed the price_feed object containing the three prices making up the feed\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction updating the price feed for the given asset\n       */\n      signed_transaction publish_asset_feed(string publishing_account,\n                                            string symbol,\n                                            price_feed feed,\n                                            bool broadcast = false);\n\n      /** Pay into the fee pool for the given asset.\n       *\n       * User-issued assets can optionally have a pool of the core asset which is\n       * automatically used to pay transaction fees for any transaction using that\n       * asset (using the asset's core exchange rate).\n       *\n       * This command allows anyone to deposit the core asset into this fee pool.\n       *\n       * @param from the name or id of the account sending the core asset\n       * @param symbol the name or id of the asset whose fee pool you wish to fund\n       * @param amount the amount of the core asset to deposit\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction funding the fee pool\n       */\n      signed_transaction fund_asset_fee_pool(string from,\n                                             string symbol,\n                                             string amount,\n                                             bool broadcast = false);\n\n      /** Claim funds from the fee pool for the given asset.\n       *\n       * User-issued assets can optionally have a pool of the core asset which is\n       * automatically used to pay transaction fees for any transaction using that\n       * asset (using the asset's core exchange rate).\n       *\n       * This command allows the issuer to withdraw those funds from the fee pool.\n       *\n       * @param symbol the name or id of the asset whose fee pool you wish to claim\n       * @param amount the amount of the core asset to withdraw\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction claiming from the fee pool\n       */\n      signed_transaction claim_asset_fee_pool(string symbol,\n                                              string amount,\n                                              bool broadcast = false);\n\n      /** Burns an amount of given asset.\n       *\n       * This command burns an amount of given asset to reduce the amount in circulation.\n       * @note you cannot burn market-issued assets.\n       * @param from the account containing the asset you wish to burn\n       * @param amount the amount to burn, in nominal units\n       * @param symbol the name or id of the asset to burn\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction burning the asset\n       */\n      signed_transaction reserve_asset(string from,\n                                    string amount,\n                                    string symbol,\n                                    bool broadcast = false);\n\n      /** Forces a global settling of the given asset (black swan or prediction markets).\n       *\n       * In order to use this operation, asset_to_settle must have the global_settle flag set\n       *\n       * When this operation is executed all open margin positions are called at the settle price.\n       * A pool will be formed containing the collateral got from the margin positions.\n       * Users owning an amount of the asset may use \\c settle_asset() to claim collateral instantly\n       * at the settle price from the pool.\n       * If this asset is used as backing for other bitassets, those bitassets will not be affected.\n       *\n       * @note this operation is used only by the asset issuer.\n       *\n       * @param symbol the name or id of the asset to globally settle\n       * @param settle_price the price at which to settle\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction settling the named asset\n       */\n      signed_transaction global_settle_asset(string symbol,\n                                             price settle_price,\n                                             bool broadcast = false);\n\n      /** Schedules a market-issued asset for automatic settlement.\n       *\n       * Holders of market-issued assests may request a forced settlement for some amount of their asset.\n       * This means that the specified sum will be locked by the chain and held for the settlement period,\n       * after which time the chain will\n       * choose a margin posision holder and buy the settled asset using the margin's collateral.\n       * The price of this sale\n       * will be based on the feed price for the market-issued asset being settled.\n       * The exact settlement price will be the\n       * feed price at the time of settlement with an offset in favor of the margin position, where the offset is a\n       * blockchain parameter set in the global_property_object.\n       *\n       * @param account_to_settle the name or id of the account owning the asset\n       * @param amount_to_settle the amount of the named asset to schedule for settlement\n       * @param symbol the name or id of the asset to settle\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction settling the named asset\n       */\n      signed_transaction settle_asset(string account_to_settle,\n                                      string amount_to_settle,\n                                      string symbol,\n                                      bool broadcast = false);\n\n      /** Creates or updates a bid on an MPA after global settlement.\n       *\n       * In order to revive a market-pegged asset after global settlement (aka\n       * black swan), investors can bid collateral in order to take over part of\n       * the debt and the settlement fund, see BSIP-0018. Updating an existing\n       * bid to cover 0 debt will delete the bid.\n       *\n       * @param bidder_name the name or id of the account making the bid\n       * @param debt_amount the amount of debt of the named asset to bid for\n       * @param debt_symbol the name or id of the MPA to bid for\n       * @param additional_collateral the amount of additional collateral to bid\n       *        for taking over debt_amount. The asset type of this amount is\n       *        determined automatically from debt_symbol.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction creating/updating the bid\n       */\n      signed_transaction bid_collateral(string bidder_name, string debt_amount, string debt_symbol,\n                                        string additional_collateral, bool broadcast = false);\n\n      /** Whitelist and blacklist accounts, primarily for transacting in whitelisted assets.\n       *\n       * Accounts can freely specify opinions about other accounts, in the form of either whitelisting or blacklisting\n       * them. This information is used in chain validation only to determine whether an account is authorized to\n       * transact in an asset type which enforces a whitelist, but third parties can use this information for other\n       * uses as well, as long as it does not conflict with the use of whitelisted assets.\n       *\n       * An asset which enforces a whitelist specifies a list of accounts to maintain its whitelist, and a list of\n       * accounts to maintain its blacklist. In order for a given account A to hold and transact in a whitelisted\n       * asset S, A must be whitelisted by at least one of S's whitelist_authorities and blacklisted by none of S's\n       * blacklist_authorities. If A receives a balance of S, and is later removed from the whitelist(s) which allowed\n       * it to hold S, or added to any blacklist S specifies as authoritative, A's balance of S will be frozen until\n       * A's authorization is reinstated.\n       *\n       * @param authorizing_account the account who is doing the whitelisting\n       * @param account_to_list the account being whitelisted\n       * @param new_listing_status the new whitelisting status\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction changing the whitelisting status\n       */\n      signed_transaction whitelist_account(string authorizing_account,\n                                           string account_to_list,\n                                           account_whitelist_operation::account_listing new_listing_status,\n                                           bool broadcast = false);\n\n      /** Creates a committee_member object owned by the given account.\n       *\n       * An account can have at most one committee_member object.\n       *\n       * @param owner_account the name or id of the account which is creating the committee_member\n       * @param url a URL to include in the committee_member record in the blockchain.  Clients may\n       *            display this when showing a list of committee_members.  May be blank.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction registering a committee_member\n       */\n      signed_transaction create_committee_member(string owner_account,\n                                         string url,\n                                         bool broadcast = false);\n\n      /** Lists all witnesses registered in the blockchain.\n       * This returns a list of all account names that own witnesses, and the associated witness id,\n       * sorted by name.  This lists witnesses whether they are currently voted in or not.\n       *\n       * Use the \\c lowerbound and limit parameters to page through the list.  To retrieve all witnesss,\n       * start by setting \\c lowerbound to the empty string \\c \"\", and then each iteration, pass\n       * the last witness name returned as the \\c lowerbound for the next \\c list_witnesss() call.\n       *\n       * @param lowerbound the name of the first witness to return.  If the named witness does not exist,\n       *                   the list will start at the witness that comes after \\c lowerbound\n       * @param limit the maximum number of witnesss to return (max: 1000)\n       * @returns a list of witnesss mapping witness names to witness ids\n       */\n      map<string,witness_id_type>       list_witnesses(const string& lowerbound, uint32_t limit);\n\n      /** Lists all committee_members registered in the blockchain.\n       * This returns a list of all account names that own committee_members, and the associated committee_member id,\n       * sorted by name.  This lists committee_members whether they are currently voted in or not.\n       *\n       * Use the \\c lowerbound and limit parameters to page through the list.  To retrieve all committee_members,\n       * start by setting \\c lowerbound to the empty string \\c \"\", and then each iteration, pass\n       * the last committee_member name returned as the \\c lowerbound for the next \\c list_committee_members() call.\n       *\n       * @param lowerbound the name of the first committee_member to return.  If the named committee_member does not\n       *                   exist, the list will start at the committee_member that comes after \\c lowerbound\n       * @param limit the maximum number of committee_members to return (max: 1000)\n       * @returns a list of committee_members mapping committee_member names to committee_member ids\n       */\n      map<string, committee_member_id_type>       list_committee_members(const string& lowerbound, uint32_t limit);\n\n      /** Returns information about the given witness.\n       * @param owner_account the name or id of the witness account owner, or the id of the witness\n       * @returns the information about the witness stored in the block chain\n       */\n      witness_object get_witness(string owner_account);\n\n      /** Returns information about the given committee_member.\n       * @param owner_account the name or id of the committee_member account owner, or the id of the committee_member\n       * @returns the information about the committee_member stored in the block chain\n       */\n      committee_member_object get_committee_member(string owner_account);\n\n      /** Creates a witness object owned by the given account.\n       *\n       * An account can have at most one witness object.\n       *\n       * @param owner_account the name or id of the account which is creating the witness\n       * @param url a URL to include in the witness record in the blockchain.  Clients may\n       *            display this when showing a list of witnesses.  May be blank.\n       * @param broadcast true to broadcast the transaction on the network\n       * @returns the signed transaction registering a witness\n       */\n      signed_transaction create_witness(string owner_account,\n                                        string url,\n                                        bool broadcast = false);\n\n      /**\n       * Update a witness object owned by the given account.\n       *\n       * @param witness_name The name of the witness's owner account.\n       *                     Also accepts the ID of the owner account or the ID of the witness.\n       * @param url Same as for create_witness.  The empty string makes it remain the same.\n       * @param block_signing_key The new block signing public key.  The empty string makes it remain the same.\n       * @param broadcast true if you wish to broadcast the transaction.\n       * @return the signed transaction\n       */\n      signed_transaction update_witness(string witness_name,\n                                        string url,\n                                        string block_signing_key,\n                                        bool broadcast = false);\n\n\n      /**\n       * Create a worker object.\n       *\n       * @param owner_account The account which owns the worker and will be paid\n       * @param work_begin_date When the work begins\n       * @param work_end_date When the work ends\n       * @param daily_pay Amount of pay per day (NOT per maint interval)\n       * @param name Any text\n       * @param url Any text\n       * @param worker_settings {\"type\" : \"burn\"|\"refund\"|\"vesting\", \"pay_vesting_period_days\" : x}\n       * @param broadcast true if you wish to broadcast the transaction.\n       * @return the signed transaction\n       */\n      signed_transaction create_worker(\n         string owner_account,\n         time_point_sec work_begin_date,\n         time_point_sec work_end_date,\n         share_type daily_pay,\n         string name,\n         string url,\n         variant worker_settings,\n         bool broadcast = false\n         );\n\n      /**\n       * Update your votes for workers\n       *\n       * @param account The account which will pay the fee and update votes.\n       * @param delta {\"vote_for\" : [...], \"vote_against\" : [...], \"vote_abstain\" : [...]}\n       * @param broadcast true if you wish to broadcast the transaction.\n       * @return the signed transaction\n       */\n      signed_transaction update_worker_votes(\n         string account,\n         worker_vote_delta delta,\n         bool broadcast = false\n         );\n\n      /**\n       * Create a hashed time lock contract\n       *\n       * @param source The account that will reserve the funds (and pay the fee)\n       * @param destination The account that will receive the funds if the preimage is presented\n       * @param amount the amount of the asset that is to be traded\n       * @param asset_symbol The asset that is to be traded\n       * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160, SHA1 or SHA256.\n       * @param preimage_hash the hash of the preimage\n       * @param preimage_size the size of the preimage in bytes\n       * @param claim_period_seconds how long after creation until the lock expires\n       * @param memo the memo\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed transaction\n       */\n      signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol,\n            string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size,\n            const uint32_t claim_period_seconds, const std::string& memo, bool broadcast = false );\n\n      /****\n       * Update a hashed time lock contract\n       *\n       * @param htlc_id The object identifier of the HTLC on the blockchain\n       * @param issuer Who is performing this operation (and paying the fee)\n       * @param preimage the preimage that should evaluate to the preimage_hash\n       * @return the signed transaction\n       */\n      signed_transaction htlc_redeem( string htlc_id, string issuer, const std::string& preimage,\n            bool broadcast = false );\n\n      /*****\n       * Increase the timelock on an existing HTLC\n       *\n       * @param htlc_id The object identifier of the HTLC on the blockchain\n       * @param issuer Who is performing this operation (and paying the fee)\n       * @param seconds_to_add how many seconds to add to the existing timelock\n       * @param broadcast true to broadcast to the network\n       * @return the signed transaction\n       */\n      signed_transaction htlc_extend(string htlc_id, string issuer, const uint32_t seconds_to_add,\n            bool broadcast = false);\n\n      /**\n       * Get information about a vesting balance object or vesting balance objects owned by an account.\n       *\n       * @param account_name An account name, account ID, or vesting balance object ID.\n       * @return a list of vesting balance objects with additional info\n       */\n      vector< vesting_balance_object_with_info > get_vesting_balances( string account_name );\n\n      /**\n       * Withdraw a vesting balance.\n       *\n       * @param witness_name The account name of the witness, also accepts account ID or vesting balance ID type.\n       * @param amount The amount to withdraw.\n       * @param asset_symbol The symbol of the asset to withdraw.\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed transaction\n       */\n      signed_transaction withdraw_vesting(\n         string witness_name,\n         string amount,\n         string asset_symbol,\n         bool broadcast = false);\n\n      /** Vote for a given committee_member.\n       *\n       * An account can publish a list of all committee_members they approve of.  This\n       * command allows you to add or remove committee_members from this list.\n       * Each account's vote is weighted according to the number of shares of the\n       * core asset owned by that account at the time the votes are tallied.\n       *\n       * @note you cannot vote against a committee_member, you can only vote for the committee_member\n       *       or not vote for the committee_member.\n       *\n       * @param voting_account the name or id of the account who is voting with their shares\n       * @param committee_member the name or id of the committee_member' owner account\n       * @param approve true if you wish to vote in favor of that committee_member, false to\n       *                remove your vote in favor of that committee_member\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed transaction changing your vote for the given committee_member\n       */\n      signed_transaction vote_for_committee_member(string voting_account,\n                                           string committee_member,\n                                           bool approve,\n                                           bool broadcast = false);\n\n      /** Vote for a given witness.\n       *\n       * An account can publish a list of all witnesses they approve of.  This\n       * command allows you to add or remove witnesses from this list.\n       * Each account's vote is weighted according to the number of shares of the\n       * core asset owned by that account at the time the votes are tallied.\n       *\n       * @note you cannot vote against a witness, you can only vote for the witness\n       *       or not vote for the witness.\n       *\n       * @param voting_account the name or id of the account who is voting with their shares\n       * @param witness the name or id of the witness' owner account\n       * @param approve true if you wish to vote in favor of that witness, false to\n       *                remove your vote in favor of that witness\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed transaction changing your vote for the given witness\n       */\n      signed_transaction vote_for_witness(string voting_account,\n                                          string witness,\n                                          bool approve,\n                                          bool broadcast = false);\n\n      /** Set the voting proxy for an account.\n       *\n       * If a user does not wish to take an active part in voting, they can choose\n       * to allow another account to vote their stake.\n       *\n       * Setting a vote proxy does not remove your previous votes from the blockchain,\n       * they remain there but are ignored.  If you later null out your vote proxy,\n       * your previous votes will take effect again.\n       *\n       * This setting can be changed at any time.\n       *\n       * @param account_to_modify the name or id of the account to update\n       * @param voting_account the name or id of an account authorized to vote account_to_modify's shares,\n       *                       or null to vote your own shares\n       *\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed transaction changing your vote proxy settings\n       */\n      signed_transaction set_voting_proxy(string account_to_modify,\n                                          optional<string> voting_account,\n                                          bool broadcast = false);\n\n      /** Set your vote for the number of witnesses and committee_members in the system.\n       *\n       * Each account can voice their opinion on how many committee_members and how many\n       * witnesses there should be in the active committee_member/active witness list.  These\n       * are independent of each other.  You must vote your approval of at least as many\n       * committee_members or witnesses as you claim there should be (you can't say that there should\n       * be 20 committee_members but only vote for 10).\n       *\n       * There are maximum values for each set in the blockchain parameters (currently\n       * defaulting to 1001).\n       *\n       * This setting can be changed at any time.  If your account has a voting proxy\n       * set, your preferences will be ignored.\n       *\n       * @param account_to_modify the name or id of the account to update\n       * @param desired_number_of_witnesses desired number of active witnesses\n       * @param desired_number_of_committee_members desired number of active committee members\n       *\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed transaction changing your vote proxy settings\n       */\n      signed_transaction set_desired_witness_and_committee_member_count(string account_to_modify,\n                                                                uint16_t desired_number_of_witnesses,\n                                                                uint16_t desired_number_of_committee_members,\n                                                                bool broadcast = false);\n\n      /** Signs a transaction.\n       *\n       * Given a fully-formed transaction that is only lacking signatures, this signs\n       * the transaction with the necessary keys and optionally broadcasts the transaction\n       * @param tx the unsigned transaction\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed version of the transaction\n       */\n      signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false);\n\n      /** Signs a transaction.\n       *\n       * Given a fully-formed transaction that is only lacking signatures, this signs\n       * the transaction with the inferred necessary keys and the explicitly provided keys,\n       * and optionally broadcasts the transaction\n       * @param tx the unsigned transaction\n       * @param signing_keys Keys that must be used when signing the transaction\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed version of the transaction\n       */\n      signed_transaction sign_transaction2(signed_transaction tx,\n                                           const vector<public_key_type>& signing_keys = vector<public_key_type>(),\n                                           bool broadcast = true);\n\n\n      /** Get transaction signers.\n       *\n       * Returns information about who signed the transaction, specifically,\n       * the corresponding public keys of the private keys used to sign the transaction.\n       * @param tx the signed transaction\n       * @return the set of public_keys\n       */\n      flat_set<public_key_type> get_transaction_signers(const signed_transaction &tx) const;\n\n      /** Get key references.\n       *\n       * Returns accounts related to given public keys.\n       * @param keys public keys to search for related accounts\n       * @return the set of related accounts\n       */\n      vector<flat_set<account_id_type>> get_key_references(const vector<public_key_type> &keys) const;\n\n      /** Returns an uninitialized object representing a given blockchain operation.\n       *\n       * This returns a default-initialized object of the given type; it can be used\n       * during early development of the wallet when we don't yet have custom commands for\n       * creating all of the operations the blockchain supports.\n       *\n       * Any operation the blockchain supports can be created using the transaction builder's\n       * \\c add_operation_to_builder_transaction() , but to do that from the CLI you need to\n       * know what the JSON form of the operation looks like.  This will give you a template\n       * you can fill in.  It's better than nothing.\n       *\n       * @param operation_type the type of operation to return, must be one of the\n       *                       operations defined in `graphene/protocol/operations.hpp`\n       *                       (e.g., \"global_parameters_update_operation\")\n       * @return a default-constructed operation of the given type\n       */\n      operation get_prototype_operation(string operation_type);\n\n      /** Creates a transaction to propose a parameter change.\n       *\n       * Multiple parameters can be specified if an atomic change is\n       * desired.\n       *\n       * @param proposing_account The account paying the fee to propose the tx\n       * @param expiration_time Timestamp specifying when the proposal will either take effect or expire.\n       * @param changed_values The values to change; all other chain parameters are filled in with default values\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed version of the transaction\n       */\n      signed_transaction propose_parameter_change(\n         const string& proposing_account,\n         fc::time_point_sec expiration_time,\n         const variant_object& changed_values,\n         bool broadcast = false);\n\n      /** Propose a fee change.\n       *\n       * @param proposing_account The account paying the fee to propose the tx\n       * @param expiration_time Timestamp specifying when the proposal will either take effect or expire.\n       * @param changed_values Map of operation type to new fee.  Operations may be specified by name or ID.\n       *    The \"scale\" key changes the scale.  All other operations will maintain current values.\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed version of the transaction\n       */\n      signed_transaction propose_fee_change(\n         const string& proposing_account,\n         fc::time_point_sec expiration_time,\n         const variant_object& changed_values,\n         bool broadcast = false);\n\n      /** Approve or disapprove a proposal.\n       *\n       * @param fee_paying_account The account paying the fee for the op.\n       * @param proposal_id The proposal to modify.\n       * @param delta Members contain approvals to create or remove.  In JSON you can leave empty members undefined.\n       * @param broadcast true if you wish to broadcast the transaction\n       * @return the signed version of the transaction\n       */\n      signed_transaction approve_proposal(\n         const string& fee_paying_account,\n         const string& proposal_id,\n         const approval_delta& delta,\n         bool broadcast /* = false */\n         );\n\n      /**\n       * Returns the order book for the market base:quote.\n       * @param base symbol name or ID of the base asset\n       * @param quote symbol name or ID of the quote asset\n       * @param limit depth of the order book to retrieve, for bids and asks each, capped at 50\n       * @return Order book of the market\n       */\n      order_book get_order_book( const string& base, const string& quote, unsigned limit = 50);\n\n      /** Signs a transaction.\n       *\n       * Given a fully-formed transaction with or without signatures, signs\n       * the transaction with the owned keys and optionally broadcasts the\n       * transaction.\n       *\n       * @param tx the unsigned transaction\n       * @param broadcast true if you wish to broadcast the transaction\n       *\n       * @return the signed transaction\n       */\n      signed_transaction add_transaction_signature( signed_transaction tx,\n                                                    bool broadcast = false );\n\n      void dbg_make_uia(string creator, string symbol);\n      void dbg_make_mia(string creator, string symbol);\n      void dbg_push_blocks( std::string src_filename, uint32_t count );\n      void dbg_generate_blocks( std::string debug_wif_key, uint32_t count );\n      void dbg_stream_json_objects( const std::string& filename );\n      void dbg_update_object( fc::variant_object update );\n\n      void flood_network(string prefix, uint32_t number_of_transactions);\n\n      void network_add_nodes( const vector<string>& nodes );\n      vector< variant > network_get_connected_peers();\n\n      /**\n       *  Used to transfer from one set of blinded balances to another\n       */\n      blind_confirmation blind_transfer_help( string from_key_or_label,\n                                         string to_key_or_label,\n                                         string amount,\n                                         string symbol,\n                                         bool broadcast = false,\n                                         bool to_temp = false );\n\n\n      std::map<string,std::function<string(fc::variant,const fc::variants&)>> get_result_formatters() const;\n\n      fc::signal<void(bool)> lock_changed;\n      std::shared_ptr<detail::wallet_api_impl> my;\n      void encrypt_keys();\n\n      /**\n       * Manage account storage map(key->value) by using the custom operations plugin.\n       *\n       * Each account can optionally add random information in the form of a key-value map\n       * to be retrieved by any interested party.\n       *\n       * @param account The account ID or name that we are adding additional information to.\n       * @param catalog The name of the catalog the operation will insert data to.\n       * @param remove true if you want to remove stuff from a catalog.\n       * @param key_values The map to be inserted/removed to/from the catalog\n       * @param broadcast true if you wish to broadcast the transaction\n       *\n       * @return The signed transaction\n       */\n      signed_transaction account_store_map(string account, string catalog, bool remove,\n            flat_map<string, optional<string>> key_values, bool broadcast);\n\n      /**\n       * Get \\c account_storage_object of an account by using the custom operations plugin.\n       *\n       * Storage data added to the map with @ref account_store_map will be returned.\n       *\n       * @param account Account ID or name to get contact data from.\n       * @param catalog The catalog to retrieve.\n       *\n       * @return An \\c account_storage_object or empty.\n       */\n      vector<account_storage_object> get_account_storage(string account, string catalog);\n\n};\n\n} }\n\nextern template class fc::api<graphene::wallet::wallet_api>;\n\nFC_API( graphene::wallet::wallet_api,\n        (help)\n        (gethelp)\n        (info)\n        (about)\n        (begin_builder_transaction)\n        (add_operation_to_builder_transaction)\n        (replace_operation_in_builder_transaction)\n        (set_fees_on_builder_transaction)\n        (preview_builder_transaction)\n        (sign_builder_transaction)\n        (sign_builder_transaction2)\n        (broadcast_transaction)\n        (propose_builder_transaction)\n        (propose_builder_transaction2)\n        (remove_builder_transaction)\n        (is_new)\n        (is_locked)\n        (lock)(unlock)(set_password)\n        (dump_private_keys)\n        (list_my_accounts)\n        (list_accounts)\n        (list_account_balances)\n        (list_assets)\n        (get_asset_count)\n        (import_key)\n        (import_accounts)\n        (import_account_keys)\n        (import_balance)\n        (suggest_brain_key)\n        (derive_owner_keys_from_brain_key)\n        (register_account)\n        (upgrade_account)\n        (create_account_with_brain_key)\n        (sell_asset)\n        (borrow_asset)\n        (borrow_asset_ext)\n        (cancel_order)\n        (transfer)\n        (transfer2)\n        (get_transaction_id)\n        (create_asset)\n        (update_asset)\n        (update_asset_issuer)\n        (update_bitasset)\n        (get_htlc)\n        (update_asset_feed_producers)\n        (publish_asset_feed)\n        (issue_asset)\n        (get_asset)\n        (get_bitasset_data)\n        (fund_asset_fee_pool)\n        (claim_asset_fee_pool)\n        (reserve_asset)\n        (global_settle_asset)\n        (settle_asset)\n        (bid_collateral)\n        (whitelist_account)\n        (create_committee_member)\n        (get_witness)\n        (get_committee_member)\n        (list_witnesses)\n        (list_committee_members)\n        (create_witness)\n        (update_witness)\n        (create_worker)\n        (update_worker_votes)\n        (htlc_create)\n        (htlc_redeem)\n        (htlc_extend)\n        (get_vesting_balances)\n        (withdraw_vesting)\n        (vote_for_committee_member)\n        (vote_for_witness)\n        (set_voting_proxy)\n        (set_desired_witness_and_committee_member_count)\n        (get_account)\n        (get_account_id)\n        (get_block)\n        (get_account_count)\n        (get_account_history)\n        (get_relative_account_history)\n        (get_account_history_by_operations)\n        (get_collateral_bids)\n        (is_public_key_registered)\n        (get_full_account)\n        (get_market_history)\n        (get_global_properties)\n        (get_dynamic_global_properties)\n        (get_object)\n        (get_private_key)\n        (load_wallet_file)\n        (normalize_brain_key)\n        (get_account_limit_orders)\n        (get_limit_orders)\n        (get_call_orders)\n        (get_settle_orders)\n        (save_wallet_file)\n        (serialize_transaction)\n        (sign_transaction)\n        (sign_transaction2)\n        (add_transaction_signature)\n        (get_transaction_signers)\n        (get_key_references)\n        (get_prototype_operation)\n        (propose_parameter_change)\n        (propose_fee_change)\n        (approve_proposal)\n        (dbg_make_uia)\n        (dbg_make_mia)\n        (dbg_push_blocks)\n        (dbg_generate_blocks)\n        (dbg_stream_json_objects)\n        (dbg_update_object)\n        (flood_network)\n        (network_add_nodes)\n        (network_get_connected_peers)\n        (sign_memo)\n        (read_memo)\n        (sign_message)\n        (verify_message)\n        (verify_signed_message)\n        (verify_encapsulated_message)\n        (set_key_label)\n        (get_key_label)\n        (get_public_key)\n        (get_blind_accounts)\n        (get_my_blind_accounts)\n        (get_blind_balances)\n        (create_blind_account)\n        (transfer_to_blind)\n        (transfer_from_blind)\n        (blind_transfer)\n        (blind_history)\n        (receive_blind_transfer)\n        (get_order_book)\n        (account_store_map)\n        (get_account_storage)\n        (quit)\n      )\n"
  },
  {
    "path": "libraries/wallet/include/graphene/wallet/wallet_structs.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <string>\n#include <vector>\n\nusing namespace graphene::app;\nusing namespace graphene::chain;\nusing namespace graphene::utilities;\nusing std::string;\nusing std::vector;\n\nnamespace graphene { namespace wallet { \n\ntypedef uint16_t transaction_handle_type;\n\nstruct plain_keys\n{\n   map<public_key_type, string>  keys;\n   fc::sha512                    checksum;\n};\n\nstruct brain_key_info\n{\n   string brain_priv_key;\n   string wif_priv_key;\n   public_key_type pub_key;\n};\n\n\n/**\n *  Contains the confirmation receipt the sender must give the receiver and\n *  the meta data about the receipt that helps the sender identify which receipt is\n *  for the receiver and which is for the change address.\n */\nstruct blind_confirmation\n{\n   struct output\n   {\n      string                          label;\n      public_key_type                 pub_key;\n      stealth_confirmation::memo_data decrypted_memo;\n      stealth_confirmation            confirmation;\n      authority                       auth;\n      string                          confirmation_receipt;\n   };\n\n   signed_transaction     trx;\n   vector<output>         outputs;\n};\n\nstruct blind_balance\n{\n   asset                     amount;\n   public_key_type           from; ///< the account this balance came from\n   public_key_type           to; ///< the account this balance is logically associated with\n   public_key_type           one_time_key; ///< used to derive the authority key and blinding factor\n   fc::sha256                blinding_factor;\n   fc::ecc::commitment_type  commitment;\n   bool                      used = false;\n};\n\nstruct blind_receipt\n{\n   std::pair<public_key_type,fc::time_point>        from_date()const { return std::make_pair(from_key,date); }\n   std::pair<public_key_type,fc::time_point>        to_date()const   { return std::make_pair(to_key,date);   }\n   std::tuple<public_key_type,asset_id_type,bool>   to_asset_used()const\n   { return std::make_tuple(to_key,amount.asset_id,used);   }\n\n   const commitment_type& commitment()const        { return data.commitment; }\n\n   fc::time_point                  date;\n   public_key_type                 from_key;\n   string                          from_label;\n   public_key_type                 to_key;\n   string                          to_label;\n   asset                           amount;\n   string                          memo;\n   authority                       control_authority;\n   stealth_confirmation::memo_data data;\n   bool                            used = false;\n   stealth_confirmation            conf;\n};\n\nstruct by_from;\nstruct by_to;\nstruct by_to_asset_used;\nstruct by_commitment;\n\ntypedef multi_index_container< blind_receipt,\n   indexed_by<\n      ordered_unique< tag<by_commitment>,\n                      const_mem_fun< blind_receipt, const commitment_type&, &blind_receipt::commitment > >,\n      ordered_unique< tag<by_to>,\n                      const_mem_fun< blind_receipt,\n                                     std::pair<public_key_type,fc::time_point>,\n                                     &blind_receipt::to_date > >,\n      ordered_non_unique< tag<by_to_asset_used>,\n                          const_mem_fun< blind_receipt,\n                                         std::tuple<public_key_type,asset_id_type,bool>,\n                                         &blind_receipt::to_asset_used > >,\n      ordered_unique< tag<by_from>,\n                      const_mem_fun< blind_receipt,\n                                     std::pair<public_key_type,fc::time_point>,\n                                     &blind_receipt::from_date > >\n   >\n> blind_receipt_index_type;\n\n\nstruct key_label\n{\n   string          label;\n   public_key_type key;\n};\n\n\nstruct by_label;\nstruct by_key;\ntypedef multi_index_container<\n   key_label,\n   indexed_by<\n      ordered_unique< tag<by_label>, member< key_label, string, &key_label::label > >,\n      ordered_unique< tag<by_key>, member< key_label, public_key_type, &key_label::key > >\n   >\n> key_label_index_type;\n\n\nstruct wallet_data\n{\n   /** Chain ID this wallet is used with */\n   chain_id_type chain_id;\n   account_multi_index_type my_accounts;\n   /// @return IDs of all accounts in @ref my_accounts\n   vector<object_id_type> my_account_ids()const\n   {\n      vector<object_id_type> ids;\n      ids.reserve(my_accounts.size());\n      std::transform(my_accounts.begin(), my_accounts.end(), std::back_inserter(ids),\n                     [](const account_object& ao) { return ao.id; });\n      return ids;\n   }\n   /// Add acct to @ref my_accounts, or update it if it is already in @ref my_accounts\n   /// @return true if the account was newly inserted; false if it was only updated\n   bool update_account(const account_object& acct)\n   {\n      auto& idx = my_accounts.get<by_id>();\n      auto itr = idx.find(acct.get_id());\n      if( itr != idx.end() )\n      {\n         idx.replace(itr, acct);\n         return false;\n      } else {\n         idx.insert(acct);\n         return true;\n      }\n   }\n\n   /** encrypted keys */\n   vector<char>              cipher_keys;\n\n   /** map an account to a set of extra keys that have been imported for that account */\n   map<account_id_type, set<public_key_type> >  extra_keys;\n\n   // map of account_name -> base58_private_key for\n   //    incomplete account regs\n   map<string, vector<string> > pending_account_registrations;\n   map<string, string> pending_witness_registrations;\n\n   key_label_index_type                                              labeled_keys;\n   blind_receipt_index_type                                          blind_receipts;\n\n   string                    ws_server = \"ws://localhost:8090\";\n   string                    ws_user;\n   string                    ws_password;\n};\n\nstruct exported_account_keys\n{\n    string account_name;\n    vector<vector<char>> encrypted_private_keys;\n    vector<public_key_type> public_keys;\n};\n\nstruct exported_keys\n{\n    fc::sha512 password_checksum;\n    vector<exported_account_keys> account_keys;\n};\n\nstruct approval_delta\n{\n   vector<string> active_approvals_to_add;\n   vector<string> active_approvals_to_remove;\n   vector<string> owner_approvals_to_add;\n   vector<string> owner_approvals_to_remove;\n   vector<string> key_approvals_to_add;\n   vector<string> key_approvals_to_remove;\n};\n\nstruct worker_vote_delta\n{\n   flat_set<worker_id_type> vote_for;\n   flat_set<worker_id_type> vote_against;\n   flat_set<worker_id_type> vote_abstain;\n};\n\nstruct signed_block_with_info : public signed_block\n{\n   signed_block_with_info( const signed_block& block );\n   signed_block_with_info( const signed_block_with_info& block ) = default;\n\n   block_id_type block_id;\n   public_key_type signing_key;\n   vector< transaction_id_type > transaction_ids;\n};\n\nstruct vesting_balance_object_with_info : public vesting_balance_object\n{\n   vesting_balance_object_with_info( const vesting_balance_object& vbo, fc::time_point_sec now );\n   vesting_balance_object_with_info( const vesting_balance_object_with_info& vbo ) = default;\n\n   /**\n    * How much is allowed to be withdrawn.\n    */\n   asset allowed_withdraw;\n\n   /**\n    * The time at which allowed_withdrawal was calculated.\n    */\n   fc::time_point_sec allowed_withdraw_time;\n};\n\nstruct signed_message_meta {\n   string account;\n   public_key_type memo_key;\n   uint32_t block;\n   string time;\n};\n\nclass signed_message {\npublic:\n   string message;\n   signed_message_meta meta;\n   fc::optional<fc::ecc::compact_signature> signature;\n\n   fc::sha256 digest()const;\n};\n\nnamespace detail {\nclass wallet_api_impl;\n}\n\n/***\n * A utility class for performing various state-less actions that are related to wallets\n */\nclass utility {\n   public:\n      /**\n       * Derive any number of *possible* owner keys from a given brain key.\n       *\n       * NOTE: These keys may or may not match with the owner keys of any account.\n       * This function is merely intended to assist with account or key recovery.\n       *\n       * @see suggest_brain_key()\n       *\n       * @param brain_key    Brain key\n       * @param number_of_desired_keys  Number of desired keys\n       * @return A list of keys that are deterministically derived from the brainkey\n       */\n      static vector<brain_key_info> derive_owner_keys_from_brain_key( string brain_key,\n                                                                      int number_of_desired_keys = 1 );\n\n      /** Suggests a safe brain key to use for creating your account.\n       * \\c create_account_with_brain_key() requires you to specify a 'brain key',\n       * a long passphrase that provides enough entropy to generate cyrptographic\n       * keys.  This function will suggest a suitably random string that should\n       * be easy to write down (and, with effort, memorize).\n       * @returns a suggested brain_key\n       */\n      static brain_key_info suggest_brain_key();\n};\n\nstruct operation_detail {\n   string                   memo;\n   string                   description;\n   operation_history_object op;\n};\n\nstruct operation_detail_ex {\n    string                   memo;\n    string                   description;\n    operation_history_object op;\n    transaction_id_type      transaction_id;\n};\n\nstruct account_history_operation_detail {\n   uint32_t                     total_count = 0;\n   uint32_t                     result_count = 0;\n   vector<operation_detail_ex>  details;\n};\n\n}} // namespace graphene::wallet\n\nFC_REFLECT( graphene::wallet::key_label, (label)(key) )\nFC_REFLECT( graphene::wallet::blind_balance, (amount)(from)(to)(one_time_key)(blinding_factor)(commitment)(used) )\nFC_REFLECT( graphene::wallet::blind_confirmation::output,\n            (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) )\nFC_REFLECT( graphene::wallet::blind_confirmation, (trx)(outputs) )\n\nFC_REFLECT( graphene::wallet::plain_keys, (keys)(checksum) )\n\nFC_REFLECT( graphene::wallet::wallet_data,\n            (chain_id)\n            (my_accounts)\n            (cipher_keys)\n            (extra_keys)\n            (pending_account_registrations)(pending_witness_registrations)\n            (labeled_keys)\n            (blind_receipts)\n            (ws_server)\n            (ws_user)\n            (ws_password)\n          )\n\nFC_REFLECT( graphene::wallet::brain_key_info,\n            (brain_priv_key)\n            (wif_priv_key)\n            (pub_key)\n          )\n\nFC_REFLECT( graphene::wallet::exported_account_keys, (account_name)(encrypted_private_keys)(public_keys) )\n\nFC_REFLECT( graphene::wallet::exported_keys, (password_checksum)(account_keys) )\n\nFC_REFLECT( graphene::wallet::blind_receipt,\n            (date)(from_key)(from_label)(to_key)(to_label)(amount)(memo)(control_authority)(data)(used)(conf) )\n\nFC_REFLECT( graphene::wallet::approval_delta,\n   (active_approvals_to_add)\n   (active_approvals_to_remove)\n   (owner_approvals_to_add)\n   (owner_approvals_to_remove)\n   (key_approvals_to_add)\n   (key_approvals_to_remove)\n)\n\nFC_REFLECT( graphene::wallet::worker_vote_delta,\n   (vote_for)\n   (vote_against)\n   (vote_abstain)\n)\n\nFC_REFLECT_DERIVED( graphene::wallet::signed_block_with_info, (graphene::chain::signed_block),\n   (block_id)(signing_key)(transaction_ids) )\n\nFC_REFLECT_DERIVED( graphene::wallet::vesting_balance_object_with_info, (graphene::chain::vesting_balance_object),\n   (allowed_withdraw)(allowed_withdraw_time) )\n\nFC_REFLECT( graphene::wallet::operation_detail,\n            (memo)(description)(op) )\n\nFC_REFLECT(graphene::wallet::operation_detail_ex,\n            (memo)(description)(op)(transaction_id))\n\nFC_REFLECT( graphene::wallet::account_history_operation_detail,\n        (total_count)(result_count)(details))\n\nFC_REFLECT( graphene::wallet::signed_message_meta, (account)(memo_key)(block)(time) )\nFC_REFLECT( graphene::wallet::signed_message, (message)(meta)(signature) )\n"
  },
  {
    "path": "libraries/wallet/operation_printer.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"operation_printer.hpp\"\n#include <graphene/protocol/base.hpp>\n#include \"wallet_api_impl.hpp\"\n#include <graphene/utilities/key_conversion.hpp>\n\nnamespace graphene { namespace wallet { namespace detail {\n\nclass htlc_hash_to_string_visitor\n{\npublic:\n   typedef std::string result_type;\n\n   result_type operator()( const fc::ripemd160& hash )const\n   {\n      return \"RIPEMD160 \" + hash.str();\n   }\n\n   result_type operator()( const fc::sha1& hash )const\n   {\n      return \"SHA1 \" + hash.str();\n   }\n\n   result_type operator()( const fc::sha256& hash )const\n   {\n      return \"SHA256 \" + hash.str();\n   }\n   result_type operator()( const fc::hash160& hash )const\n   {\n      return \"HASH160 \" + hash.str();\n   }\n};\n\nstd::string operation_printer::fee(const graphene::protocol::asset& a)const {\n   out << \"   (Fee: \" << wallet.get_asset(a.asset_id).amount_to_pretty_string(a) << \")\";\n   return \"\";\n}\n\nstring operation_printer::print_memo( const fc::optional<graphene::protocol::memo_data>& memo )const\n{\n   std::string outstr;\n   if( memo )\n   {\n      if( wallet.is_locked() )\n      {\n         out << \" -- Unlock wallet to see memo.\";\n      } else {\n         try {\n            FC_ASSERT( wallet._keys.count(memo->to) || wallet._keys.count(memo->from),\n                       \"Memo is encrypted to a key ${to} or ${from} not in this wallet.\",\n                       (\"to\", memo->to)(\"from\",memo->from) );\n            if( wallet._keys.count(memo->to) ) {\n               auto my_key = wif_to_key(wallet._keys.at(memo->to));\n               FC_ASSERT(my_key, \"Unable to recover private key to decrypt memo. Wallet may be corrupted.\");\n               outstr = memo->get_message(*my_key, memo->from);\n               out << \" -- Memo: \" << outstr;\n            } else {\n               auto my_key = wif_to_key(wallet._keys.at(memo->from));\n               FC_ASSERT(my_key, \"Unable to recover private key to decrypt memo. Wallet may be corrupted.\");\n               outstr = memo->get_message(*my_key, memo->to);\n               out << \" -- Memo: \" << outstr;\n            }\n         } catch (const fc::exception& e) {\n            out << \" -- could not decrypt memo\";\n         }\n      }\n   } \n   return outstr;  \n}\n\nvoid operation_printer::print_preimage(const std::vector<char>& preimage)const\n{\n   if (preimage.size() == 0)\n      return;\n   out << \" with preimage \\\"\";\n   // cut it at 300 bytes max\n   auto flags = out.flags();\n   out << std::hex << setw(2) << setfill('0');\n   for (size_t i = 0; i < std::min<size_t>(300, preimage.size()); i++)\n      out << +preimage[i];\n   out.flags(flags);\n   if (preimage.size() > 300)\n      out << \"...(truncated due to size)\";\n   out << \"\\\"\";\n}\n\nstring operation_printer::print_redeem(const graphene::protocol::htlc_id_type& id,\n      const std::string& redeemer, const std::vector<char>& preimage, \n      const graphene::protocol::asset& op_fee)const\n{\n   out << redeemer << \" redeemed HTLC with id \"\n         << std::string( static_cast<object_id_type>(id));\n   print_preimage( preimage );\n   return fee(op_fee);\n}\n\nstd::string operation_printer::operator()(const transfer_from_blind_operation& op)const\n{\n   auto a = wallet.get_asset( op.fee.asset_id );\n   auto receiver = wallet.get_account( op.to );\n\n   out <<  receiver.name\n       << \" received \" << a.amount_to_pretty_string( op.amount ) << \" from blinded balance\";\n   return \"\";\n}\nstd::string operation_printer::operator()(const transfer_to_blind_operation& op)const\n{\n   auto fa = wallet.get_asset( op.fee.asset_id );\n   auto a = wallet.get_asset( op.amount.asset_id );\n   auto sender = wallet.get_account( op.from );\n\n   out <<  sender.name\n       << \" sent \" << a.amount_to_pretty_string( op.amount ) << \" to \" << op.outputs.size()\n       << \" blinded balance\" << (op.outputs.size()>1?\"s\":\"\")\n       << \" fee: \" << fa.amount_to_pretty_string( op.fee );\n   return \"\";\n}\n\nstring operation_printer::operator()(const transfer_operation& op) const\n{\n   out << \"Transfer \" << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount)\n       << \" from \" << wallet.get_account(op.from).name << \" to \" << wallet.get_account(op.to).name;\n   std::string memo = print_memo( op.memo );\n   fee(op.fee);\n   return memo;\n}\n\nstd::string operation_printer::operator()(const account_create_operation& op) const\n{\n   out << \"Create Account '\" << op.name << \"'\";\n   return fee(op.fee);\n}\n\nstd::string operation_printer::operator()(const account_update_operation& op) const\n{\n   out << \"Update Account '\" << wallet.get_account(op.account).name << \"'\";\n   return fee(op.fee);\n}\n\nstd::string operation_printer::operator()(const asset_create_operation& op) const\n{\n   out << \"Create \";\n   if( op.bitasset_opts.valid() )\n      out << \"BitAsset \";\n   else\n      out << \"User-Issue Asset \";\n   out << \"'\" << op.symbol << \"' with issuer \" << wallet.get_account(op.issuer).name;\n   return fee(op.fee);\n}\n\nstd::string operation_printer::operator()(const htlc_redeem_operation& op) const\n{\n   return print_redeem(op.htlc_id, wallet.get_account(op.redeemer).name, op.preimage, op.fee);\n}\n\nstd::string operation_printer::operator()(const htlc_redeemed_operation& op) const\n{\n   return print_redeem(op.htlc_id, wallet.get_account(op.redeemer).name, op.preimage, op.fee);\n}\n\nstd::string operation_printer::operator()(const htlc_create_operation& op) const\n{\n   static htlc_hash_to_string_visitor vtor;\n\n   auto fee_asset = wallet.get_asset( op.fee.asset_id );\n   auto to = wallet.get_account( op.to );\n   auto from = wallet.get_account( op.from );\n   operation_result_printer rprinter(wallet);\n   std::string database_id = result.visit(rprinter);\n\n   out << \"Create HTLC from \" << from.name << \" to \" << to.name \n         << \" with id \" << database_id\n         << \" preimage hash: [\" << op.preimage_hash.visit( vtor ) << \"] \";\n   print_memo( op.extensions.value.memo );\n   // determine if the block that the HTLC is in is before or after LIB\n   int32_t pending_blocks = hist.block_num - wallet.get_dynamic_global_properties().last_irreversible_block_num;\n   if (pending_blocks > 0)\n      out << \" (pending \" << std::to_string(pending_blocks) << \" blocks)\";\n   return fee(op.fee);\n}\n\nstd::string operation_result_printer::operator()(const void_result& x) const\n{\n   return \"\";\n}\n\nstd::string operation_result_printer::operator()(const object_id_type& oid)\n{\n   return std::string(oid);\n}\n\nstd::string operation_result_printer::operator()(const asset& a)\n{\n   return _wallet.get_asset(a.asset_id).amount_to_pretty_string(a);\n}\n\nstd::string operation_result_printer::operator()(const generic_operation_result& r)\n{\n   return fc::json::to_string(r);\n}\n\nstd::string operation_result_printer::operator()(const generic_exchange_operation_result& r)\n{\n   // TODO show pretty amounts instead of raw json\n   return fc::json::to_string(r);\n}\n\n}}} // graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/operation_printer.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <graphene/app/api_objects.hpp>\n#include <graphene/protocol/base.hpp>\n#include <graphene/protocol/asset.hpp>\n#include <graphene/protocol/operations.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n\n#include <string>\n#include <ostream>\n\n#include \"wallet_api_impl.hpp\"\n\nnamespace graphene { namespace wallet { namespace detail {\n\nstruct operation_result_printer\n{\npublic:\n   explicit operation_result_printer( const wallet_api_impl& w )\n      : _wallet(w) {}\n   const wallet_api_impl& _wallet;\n   typedef std::string result_type;\n\n   std::string operator()(const graphene::protocol::void_result& x) const;\n   std::string operator()(const graphene::protocol::object_id_type& oid);\n   std::string operator()(const graphene::protocol::asset& a);\n   std::string operator()(const graphene::protocol::generic_operation_result& r);\n   std::string operator()(const graphene::protocol::generic_exchange_operation_result& r);\n};\n\n// BLOCK  TRX  OP  VOP\nstruct operation_printer\n{\nprivate:\n   std::ostream& out;\n   const wallet_api_impl& wallet;\n   graphene::protocol::operation_result result;\n   graphene::chain::operation_history_object hist;\n\n   std::string fee(const graphene::protocol::asset& a) const;\n\npublic:\n   operation_printer( std::ostream& out, const wallet_api_impl& wallet, \n         const graphene::chain::operation_history_object& obj )\n      : out(out),\n        wallet(wallet),\n        result(obj.result),\n        hist(obj)\n   {}\n   typedef std::string result_type;\n\n   template<typename T>\n   std::string operator()(const T& op)const\n   {\n      auto a = wallet.get_asset( op.fee.asset_id );\n      auto payer = wallet.get_account( op.fee_payer() );\n\n      std::string op_name = fc::get_typename<T>::name();\n      if( op_name.find_last_of(':') != std::string::npos )\n         op_name.erase(0, op_name.find_last_of(':')+1);\n      out << op_name <<\" \";\n      out << payer.name << \" fee: \" << a.amount_to_pretty_string( op.fee );\n      operation_result_printer rprinter(wallet);\n      std::string str_result = result.visit(rprinter);\n      if( str_result != \"\" )\n      {\n         out << \"   result: \" << str_result;\n      }\n      return \"\";\n   }\n\n   std::string operator()(const graphene::protocol::transfer_operation& op)const;\n   std::string operator()(const graphene::protocol::transfer_from_blind_operation& op)const;\n   std::string operator()(const graphene::protocol::transfer_to_blind_operation& op)const;\n   std::string operator()(const graphene::protocol::account_create_operation& op)const;\n   std::string operator()(const graphene::protocol::account_update_operation& op)const;\n   std::string operator()(const graphene::protocol::asset_create_operation& op)const;\n   std::string operator()(const graphene::protocol::htlc_create_operation& op)const;\n   std::string operator()(const graphene::protocol::htlc_redeem_operation& op)const;\n   std::string operator()(const graphene::protocol::htlc_redeemed_operation& op)const;\n   protected:\n   std::string print_memo( const fc::optional<graphene::protocol::memo_data>& memo)const;\n   void print_preimage( const std::vector<char>& preimage)const;\n   std::string print_redeem(const graphene::protocol::htlc_id_type& id, \n         const std::string& redeemer, const std::vector<char>& preimage, \n         const graphene::protocol::asset& op_fee)const;\n};\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/reflect_util.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/wallet/reflect_util.hpp>\n\nnamespace graphene { namespace wallet { namespace impl {\n\nstd::string clean_name( const std::string& name )\n{\n   const static std::string prefix = \"graphene::protocol::\";\n   const static std::string suffix = \"_operation\";\n   // graphene::protocol::.*_operation\n   if(    (name.size() >= prefix.size() + suffix.size())\n       && (name.substr( 0, prefix.size() ) == prefix)\n       && (name.substr( name.size()-suffix.size(), suffix.size() ) == suffix )\n     )\n        return name.substr( prefix.size(), name.size() - prefix.size() - suffix.size() );\n\n   wlog( \"don't know how to clean name: ${name}\", (\"name\", name) );\n   return name;\n}\n\n}}} // namespace graphene::wallet::impl\n"
  },
  {
    "path": "libraries/wallet/wallet.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <algorithm>\n#include <cctype>\n#include <iomanip>\n#include <iostream>\n#include <iterator>\n#include <sstream>\n#include <string>\n#include <list>\n\n#include <boost/version.hpp>\n#include <boost/lexical_cast.hpp>\n#include <boost/algorithm/string/replace.hpp>\n\n#include <boost/range/adaptor/map.hpp>\n#include <boost/range/algorithm_ext/erase.hpp>\n#include <boost/range/algorithm/unique.hpp>\n#include <boost/range/algorithm/sort.hpp>\n\n#include <boost/multi_index_container.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index/mem_fun.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/random_access_index.hpp>\n#include <boost/multi_index/tag.hpp>\n#include <boost/multi_index/sequenced_index.hpp>\n#include <boost/multi_index/hashed_index.hpp>\n\n#include <fc/git_revision.hpp>\n#include <fc/io/fstream.hpp>\n#include <fc/io/json.hpp>\n#include <fc/io/stdio.hpp>\n#include <fc/network/http/websocket.hpp>\n#include <fc/rpc/cli.hpp>\n#include <fc/rpc/websocket_api.hpp>\n#include <fc/crypto/hex.hpp>\n#include <fc/thread/mutex.hpp>\n#include <fc/thread/scoped_lock.hpp>\n#include <fc/crypto/base58.hpp>\n#include <fc/crypto/aes.hpp>\n\n#include <graphene/app/api.hpp>\n#include <graphene/app/util.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/protocol/pts_address.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/utilities/git_revision.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n#include <graphene/utilities/words.hpp>\n#include <graphene/wallet/wallet.hpp>\n#include <graphene/wallet/api_documentation.hpp>\n#include \"wallet_api_impl.hpp\"\n#include <graphene/debug_witness/debug_api.hpp>\n\n#include \"operation_printer.hpp\"\n#include <graphene/wallet/reflect_util.hpp>\n\n#define BRAIN_KEY_WORD_COUNT 16\n#define RANGE_PROOF_MANTISSA 49 // Minimum mantissa bits to \"hide\" in the range proof.\n                                // If this number is set too low, then for large value\n                                // commitments the length of the range proof will hint\n                                // strongly at the value amount that is being hidden.\n\nnamespace graphene { namespace wallet {\n   fc::sha256 signed_message::digest()const\n   {\n      fc::stringstream to_sign;\n      to_sign << message << '\\n';\n      to_sign << \"account=\" << meta.account << '\\n';\n      to_sign << \"memokey=\" << std::string( meta.memo_key ) << '\\n';\n      to_sign << \"block=\" << meta.block << '\\n';\n      to_sign << \"timestamp=\" << meta.time;\n\n      return fc::sha256::hash( to_sign.str() );\n   }\n   vector<brain_key_info> utility::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys)\n   {\n      // Safety-check\n      FC_ASSERT( number_of_desired_keys >= 1 );\n\n      // Create as many derived owner keys as requested\n      vector<brain_key_info> results;\n      brain_key = graphene::wallet::detail::normalize_brain_key(brain_key);\n      for (int i = 0; i < number_of_desired_keys; ++i) {\n        fc::ecc::private_key priv_key = graphene::wallet::detail::derive_private_key( brain_key, i );\n\n        brain_key_info result;\n        result.brain_priv_key = brain_key;\n        result.wif_priv_key = key_to_wif( priv_key );\n        result.pub_key = priv_key.get_public_key();\n\n        results.push_back(result);\n      }\n\n      return results;\n   }\n\n   brain_key_info utility::suggest_brain_key()\n   {\n      brain_key_info result;\n      // create a private key for secure entropy\n      fc::sha256 sha_entropy1 = fc::ecc::private_key::generate().get_secret();\n      fc::sha256 sha_entropy2 = fc::ecc::private_key::generate().get_secret();\n      fc::bigint entropy1(sha_entropy1.data(), sha_entropy1.data_size());\n      fc::bigint entropy2(sha_entropy2.data(), sha_entropy2.data_size());\n      fc::bigint entropy(entropy1);\n      entropy <<= 8 * sha_entropy1.data_size();\n      entropy += entropy2;\n      string brain_key = \"\";\n\n      for (int i = 0; i < BRAIN_KEY_WORD_COUNT; i++)\n      {\n         fc::bigint choice = entropy % graphene::words::word_list_size;\n         entropy /= graphene::words::word_list_size;\n         if (i > 0)\n            brain_key += \" \";\n         brain_key += graphene::words::word_list[choice.to_int64()];\n      }\n\n      brain_key = detail::normalize_brain_key(brain_key);\n      fc::ecc::private_key priv_key = detail::derive_private_key(brain_key, 0);\n      result.brain_priv_key = brain_key;\n      result.wif_priv_key = key_to_wif(priv_key);\n      result.pub_key = priv_key.get_public_key();\n      return result;\n   }\n}}\n\nnamespace graphene { namespace wallet {\n\nwallet_api::wallet_api(const wallet_data& initial_data, fc::api<login_api> rapi)\n   : my(new detail::wallet_api_impl(*this, initial_data, rapi))\n{\n}\n\nwallet_api::~wallet_api()\n{\n}\n\nbool wallet_api::copy_wallet_file(string destination_filename)\n{\n   return my->copy_wallet_file(destination_filename);\n}\n\noptional<signed_block_with_info> wallet_api::get_block(uint32_t num)\n{\n   return my->_remote_db->get_block(num);\n}\n\nuint64_t wallet_api::get_account_count() const\n{\n   return my->_remote_db->get_account_count();\n}\n\nvector<account_object> wallet_api::list_my_accounts()\n{\n   return vector<account_object>(my->_wallet.my_accounts.begin(), my->_wallet.my_accounts.end());\n}\n\nmap<string,account_id_type> wallet_api::list_accounts(const string& lowerbound, uint32_t limit)\n{\n   return my->_remote_db->lookup_accounts(lowerbound, limit, {});\n}\n\nvector<asset> wallet_api::list_account_balances(const string& id)\n{\n   return my->_remote_db->get_account_balances(id, flat_set<asset_id_type>());\n}\n\nvector<extended_asset_object> wallet_api::list_assets(const string& lowerbound, uint32_t limit)const\n{\n   return my->_remote_db->list_assets( lowerbound, limit );\n}\n\nuint64_t wallet_api::get_asset_count()const\n{\n   return my->_remote_db->get_asset_count();\n}\n\nsigned_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol,\n         string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size,\n         const uint32_t claim_period_seconds, const std::string& memo, bool broadcast)\n{\n   return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size,\n         claim_period_seconds, memo, broadcast);\n}\n\nfc::optional<fc::variant> wallet_api::get_htlc(std::string htlc_id) const\n{\n   fc::optional<htlc_object> optional_obj = my->get_htlc(htlc_id);\n   if ( optional_obj.valid() )\n   {\n      const htlc_object& obj = *optional_obj;\n      // convert to formatted variant\n      fc::mutable_variant_object transfer;\n      const auto& from = my->get_account( obj.transfer.from );\n      transfer[\"from\"] = from.name;\n      const auto& to = my->get_account( obj.transfer.to );\n      transfer[\"to\"] = to.name;\n      const auto& asset = my->get_asset( obj.transfer.asset_id );\n      transfer[\"asset\"] = asset.symbol;\n      transfer[\"amount\"] = graphene::app::uint128_amount_to_string( obj.transfer.amount.value, asset.precision );\n      if (obj.memo.valid())\n         transfer[\"memo\"] = my->read_memo( *obj.memo );\n      class htlc_hash_to_variant_visitor\n      {\n         public:\n         typedef fc::mutable_variant_object result_type;\n\n         result_type operator()(const fc::ripemd160& obj)const\n         { return convert(\"RIPEMD160\", obj.str()); }\n         result_type operator()(const fc::sha1& obj)const\n         { return convert(\"SHA1\", obj.str()); }\n         result_type operator()(const fc::sha256& obj)const\n         { return convert(\"SHA256\", obj.str()); }\n         result_type operator()(const fc::hash160& obj)const\n         { return convert(\"HASH160\", obj.str()); }\n         private:\n         result_type convert(const std::string& type, const std::string& hash)const\n         {\n            fc::mutable_variant_object ret_val;\n            ret_val[\"hash_algo\"] = type;\n            ret_val[\"preimage_hash\"] = hash;\n            return ret_val;\n         }\n      };\n      static htlc_hash_to_variant_visitor hash_visitor;\n      fc::mutable_variant_object htlc_lock = obj.conditions.hash_lock.preimage_hash.visit(hash_visitor);\n      htlc_lock[\"preimage_size\"] = obj.conditions.hash_lock.preimage_size;\n      fc::mutable_variant_object time_lock;\n      time_lock[\"expiration\"] = obj.conditions.time_lock.expiration;\n      time_lock[\"time_left\"] = fc::get_approximate_relative_time_string(obj.conditions.time_lock.expiration);\n      fc::mutable_variant_object conditions;\n      conditions[\"htlc_lock\"] = htlc_lock;\n      conditions[\"time_lock\"] = time_lock;\n      fc::mutable_variant_object result;\n      result[\"transfer\"] = transfer;\n      result[\"conditions\"] = conditions;\n      return fc::optional<fc::variant>(result);\n   }\n   return fc::optional<fc::variant>();\n}\n\nsigned_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage,\n      bool broadcast)\n{\n\n   return my->htlc_redeem(htlc_id, issuer, std::vector<char>(preimage.begin(), preimage.end()), broadcast);\n}\n\nsigned_transaction wallet_api::htlc_extend ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add,\n      bool broadcast)\n{\n   return my->htlc_extend(htlc_id, issuer, seconds_to_add, broadcast);\n}\n\nvector<operation_detail> wallet_api::get_account_history(string name, int limit)const\n{\n   vector<operation_detail> result;\n\n   while( limit > 0 )\n   {\n      bool skip_first_row = false;\n      operation_history_id_type start;\n      if( result.size() )\n      {\n         start = result.back().op.id;\n         if( start == operation_history_id_type() ) // no more data\n            break;\n         start = start + (-1);\n         if( start == operation_history_id_type() ) // will return most recent history if\n                                                    // directly call remote API with this\n         {\n            start = start + 1;\n            skip_first_row = true;\n         }\n      }\n\n      int page_limit = skip_first_row ? std::min( 100, limit + 1 ) : std::min( 100, limit );\n\n      vector<operation_history_object> current = my->_remote_hist->get_account_history(\n            name,\n            operation_history_id_type(),\n            page_limit,\n            start );\n      bool first_row = true;\n      for( auto& o : current )\n      {\n         if( first_row )\n         {\n            first_row = false;\n            if( skip_first_row )\n            {\n               continue;\n            }\n         }\n         std::stringstream ss;\n         auto memo = o.op.visit(detail::operation_printer(ss, *my, o));\n         result.push_back( operation_detail{ memo, ss.str(), o } );\n      }\n\n      if( int(current.size()) < page_limit )\n         break;\n\n      limit -= current.size();\n      if( skip_first_row )\n         ++limit;\n   }\n\n   return result;\n}\n\nvector<operation_detail> wallet_api::get_relative_account_history(\n      string name,\n      uint32_t stop,\n      int limit,\n      uint32_t start)const\n{\n   vector<operation_detail> result;\n   auto account_id = get_account(name).get_id();\n\n   const account_object& account = my->get_account(account_id);\n   const account_statistics_object& stats = my->get_object(account.statistics);\n\n   if(start == 0)\n       start = stats.total_ops;\n   else\n      start = std::min<uint32_t>(start, stats.total_ops);\n\n   while( limit > 0 )\n   {\n      vector <operation_history_object> current = my->_remote_hist->get_relative_account_history(\n            name,\n            stop,\n            std::min<uint32_t>(100, limit),\n            start);\n      for (auto &o : current) {\n         std::stringstream ss;\n         auto memo = o.op.visit(detail::operation_printer(ss, *my, o));\n         result.push_back(operation_detail{memo, ss.str(), o});\n      }\n      if (current.size() < std::min<uint32_t>(100, limit))\n         break;\n      limit -= current.size();\n      start -= 100;\n      if( start == 0 ) break;\n   }\n   return result;\n}\n\naccount_history_operation_detail wallet_api::get_account_history_by_operations(\n      string name,\n      flat_set<uint16_t> operation_types,\n      uint32_t start,\n      int limit)\n{\n    account_history_operation_detail result;\n    auto account_id = get_account(name).get_id();\n\n    const auto& account = my->get_account(account_id);\n    const auto& stats = my->get_object(account.statistics);\n\n    // sequence of account_transaction_history_object start with 1\n    start = start == 0 ? 1 : start;\n\n    if (start <= stats.removed_ops) {\n        start = stats.removed_ops;\n        result.total_count =stats.removed_ops;\n    }\n\n    while (limit > 0 && start <= stats.total_ops) {\n        uint32_t min_limit = std::min<uint32_t> (100, limit);\n        auto current = my->_remote_hist->get_account_history_by_operations(name, operation_types, start, min_limit);\n        for (auto& obj : current.operation_history_objs) {\n            std::stringstream ss;\n            auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj));\n\n            transaction_id_type transaction_id;\n            auto block = get_block(obj.block_num);\n            if (block.valid() && obj.trx_in_block < block->transaction_ids.size()) {\n                transaction_id = block->transaction_ids[obj.trx_in_block];\n            }\n            result.details.push_back(operation_detail_ex{memo, ss.str(), obj, transaction_id});\n        }\n        result.result_count += current.operation_history_objs.size();\n        result.total_count += current.total_count;\n\n        start += current.total_count > 0 ? current.total_count : min_limit;\n        limit -= current.operation_history_objs.size();\n    }\n\n    return result;\n}\n\nfull_account wallet_api::get_full_account( const string& name_or_id)\n{\n    return my->_remote_db->get_full_accounts({name_or_id}, false)[name_or_id];\n}\n\nvector<bucket_object> wallet_api::get_market_history(\n      string symbol1,\n      string symbol2,\n      uint32_t bucket,\n      fc::time_point_sec start,\n      fc::time_point_sec end )const\n{\n   return my->_remote_hist->get_market_history( symbol1, symbol2, bucket, start, end );\n}\n\nvector<limit_order_object> wallet_api::get_account_limit_orders(\n      const string& name_or_id,\n      const string &base,\n      const string &quote,\n      uint32_t limit,\n      optional<limit_order_id_type> ostart_id,\n      optional<price> ostart_price)\n{\n   return my->_remote_db->get_account_limit_orders(name_or_id, base, quote, limit, ostart_id, ostart_price);\n}\n\nvector<limit_order_object> wallet_api::get_limit_orders(std::string a, std::string b, uint32_t limit)const\n{\n   return my->_remote_db->get_limit_orders(a, b, limit);\n}\n\nvector<call_order_object> wallet_api::get_call_orders(std::string a, uint32_t limit)const\n{\n   return my->_remote_db->get_call_orders(a, limit);\n}\n\nvector<force_settlement_object> wallet_api::get_settle_orders(std::string a, uint32_t limit)const\n{\n   return my->_remote_db->get_settle_orders(a, limit);\n}\n\nvector<collateral_bid_object> wallet_api::get_collateral_bids(std::string asset, uint32_t limit, uint32_t start)const\n{\n   return my->_remote_db->get_collateral_bids(asset, limit, start);\n}\n\nbrain_key_info wallet_api::suggest_brain_key()const\n{\n   return graphene::wallet::utility::suggest_brain_key();\n}\n\nvector<brain_key_info> wallet_api::derive_owner_keys_from_brain_key(\n      string brain_key,\n      int number_of_desired_keys) const\n{\n   return graphene::wallet::utility::derive_owner_keys_from_brain_key(brain_key, number_of_desired_keys);\n}\n\nbool wallet_api::is_public_key_registered(string public_key) const\n{\n   bool is_known = my->_remote_db->is_public_key_registered(public_key);\n   return is_known;\n}\n\n\nstring wallet_api::serialize_transaction( signed_transaction tx )const\n{\n   return fc::to_hex(fc::raw::pack(tx));\n}\n\nvariant wallet_api::get_object( object_id_type id ) const\n{\n   return my->_remote_db->get_objects({id}, {});\n}\n\nstring wallet_api::get_wallet_filename() const\n{\n   return my->get_wallet_filename();\n}\n\ntransaction_handle_type wallet_api::begin_builder_transaction()\n{\n   return my->begin_builder_transaction();\n}\n\nvoid wallet_api::add_operation_to_builder_transaction(\n      transaction_handle_type transaction_handle,\n      const operation& op)\n{\n   my->add_operation_to_builder_transaction(transaction_handle, op);\n}\n\nvoid wallet_api::replace_operation_in_builder_transaction(\n      transaction_handle_type handle,\n      unsigned operation_index,\n      const operation& new_op)\n{\n   my->replace_operation_in_builder_transaction(handle, operation_index, new_op);\n}\n\nasset wallet_api::set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset)\n{\n   return my->set_fees_on_builder_transaction(handle, fee_asset);\n}\n\ntransaction wallet_api::preview_builder_transaction(transaction_handle_type handle)\n{\n   return my->preview_builder_transaction(handle);\n}\n\nsigned_transaction wallet_api::sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast)\n{\n   return my->sign_builder_transaction(transaction_handle, broadcast);\n}\n\nsigned_transaction wallet_api::sign_builder_transaction2(transaction_handle_type transaction_handle,\n                                                        const vector<public_key_type>& explicit_keys,\n                                                        bool broadcast)\n{\n   return my->sign_builder_transaction2(transaction_handle, explicit_keys, broadcast);\n}\n\npair<transaction_id_type,signed_transaction> wallet_api::broadcast_transaction(signed_transaction tx)\n{\n    return my->broadcast_transaction(tx);\n}\n\nsigned_transaction wallet_api::propose_builder_transaction(\n      transaction_handle_type handle,\n      time_point_sec expiration,\n      uint32_t review_period_seconds,\n      bool broadcast)\n{\n   return my->propose_builder_transaction(handle, expiration, review_period_seconds, broadcast);\n}\n\nsigned_transaction wallet_api::propose_builder_transaction2(\n      transaction_handle_type handle,\n      string account_name_or_id,\n      time_point_sec expiration,\n      uint32_t review_period_seconds,\n      bool broadcast)\n{\n   return my->propose_builder_transaction2(handle, account_name_or_id, expiration, review_period_seconds, broadcast);\n}\n\nvoid wallet_api::remove_builder_transaction(transaction_handle_type handle)\n{\n   return my->remove_builder_transaction(handle);\n}\n\naccount_object wallet_api::get_account(string account_name_or_id) const\n{\n   return my->get_account(account_name_or_id);\n}\n\nextended_asset_object wallet_api::get_asset(string asset_name_or_id) const\n{\n   auto found_asset = my->find_asset(asset_name_or_id);\n   FC_ASSERT( found_asset, \"Unable to find asset '${a}'\", (\"a\",asset_name_or_id) );\n   return *found_asset;\n}\n\nasset_bitasset_data_object wallet_api::get_bitasset_data(string asset_name_or_id) const\n{\n   auto asset = get_asset(asset_name_or_id);\n   FC_ASSERT(asset.is_market_issued() && asset.bitasset_data_id);\n   return my->get_object(*asset.bitasset_data_id);\n}\n\naccount_id_type wallet_api::get_account_id(string account_name_or_id) const\n{\n   return my->get_account_id(account_name_or_id);\n}\n\nasset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const\n{\n   return my->get_asset_id(asset_symbol_or_id);\n}\n\nbool wallet_api::import_key(string account_name_or_id, string wif_key)\n{\n   FC_ASSERT(!is_locked());\n   // backup wallet\n   fc::optional<fc::ecc::private_key> optional_private_key = wif_to_key(wif_key);\n   if (!optional_private_key)\n      FC_THROW(\"Invalid private key\");\n   string shorthash = detail::address_to_shorthash(optional_private_key->get_public_key());\n   copy_wallet_file( \"before-import-key-\" + shorthash );\n\n   if( my->import_key(account_name_or_id, wif_key) )\n   {\n      save_wallet_file();\n      copy_wallet_file( \"after-import-key-\" + shorthash );\n      return true;\n   }\n   return false;\n}\n\nmap<string, bool> wallet_api::import_accounts( string filename, string password )\n{\n   FC_ASSERT( !is_locked() );\n   FC_ASSERT( fc::exists( filename ) );\n\n   const auto imported_keys = fc::json::from_file<exported_keys>( filename );\n\n   const auto password_hash = fc::sha512::hash( password );\n   FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum );\n\n   map<string, bool> result;\n   for( const auto& item : imported_keys.account_keys )\n   {\n       const auto import_this_account = [ & ]() -> bool\n       {\n           try\n           {\n               const account_object account = get_account( item.account_name );\n               const auto& owner_keys = account.owner.get_keys();\n               const auto& active_keys = account.active.get_keys();\n\n               for( const auto& public_key : item.public_keys )\n               {\n                   if( std::find( owner_keys.begin(), owner_keys.end(), public_key ) != owner_keys.end() )\n                       return true;\n\n                   if( std::find( active_keys.begin(), active_keys.end(), public_key ) != active_keys.end() )\n                       return true;\n               }\n           }\n           catch( ... )\n           {\n           }\n\n           return false;\n       };\n\n       const auto should_proceed = import_this_account();\n       result[ item.account_name ] = should_proceed;\n\n       if( should_proceed )\n       {\n           uint32_t import_successes = 0;\n           uint32_t import_failures = 0;\n           // TODO: First check that all private keys match public keys\n           for( const auto& encrypted_key : item.encrypted_private_keys )\n           {\n               try\n               {\n                  const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key );\n                  const auto private_key = fc::raw::unpack<private_key_type>( plain_text );\n\n                  import_key( item.account_name, string( graphene::utilities::key_to_wif( private_key ) ) );\n                  ++import_successes;\n               }\n               catch( const fc::exception& e )\n               {\n                  elog( \"Couldn't import key due to exception ${e}\", (\"e\", e.to_detail_string()) );\n                  ++import_failures;\n               }\n           }\n           ilog( \"successfully imported ${n} keys for account ${name}\",\n                 (\"n\", import_successes)(\"name\", item.account_name) );\n           if( import_failures > 0 )\n              elog( \"failed to import ${n} keys for account ${name}\",\n                    (\"n\", import_failures)(\"name\", item.account_name) );\n       }\n   }\n\n   return result;\n}\n\nbool wallet_api::import_account_keys(\n      string filename,\n      string password,\n      string src_account_name,\n      string dest_account_name )\n{\n   FC_ASSERT( !is_locked() );\n   FC_ASSERT( fc::exists( filename ) );\n\n   bool is_my_account = false;\n   const auto accounts = list_my_accounts();\n   for( const auto& account : accounts )\n   {\n       if( account.name == dest_account_name )\n       {\n           is_my_account = true;\n           break;\n       }\n   }\n   FC_ASSERT( is_my_account );\n\n   const auto imported_keys = fc::json::from_file<exported_keys>( filename );\n\n   const auto password_hash = fc::sha512::hash( password );\n   FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum );\n\n   bool found_account = false;\n   for( const auto& item : imported_keys.account_keys )\n   {\n       if( item.account_name != src_account_name )\n           continue;\n\n       found_account = true;\n\n       for( const auto& encrypted_key : item.encrypted_private_keys )\n       {\n           const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key );\n           const auto private_key = fc::raw::unpack<private_key_type>( plain_text );\n\n           my->import_key( dest_account_name, string( graphene::utilities::key_to_wif( private_key ) ) );\n       }\n\n       return true;\n   }\n   save_wallet_file();\n\n   FC_ASSERT( found_account );\n\n   return false;\n}\n\nstring wallet_api::normalize_brain_key(string s) const\n{\n   return detail::normalize_brain_key( s );\n}\n\nvariant wallet_api::info()\n{\n   return my->info();\n}\n\nvariant_object wallet_api::about() const\n{\n    return my->about();\n}\n\nfc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_string, int sequence_number) const\n{\n   return detail::derive_private_key( prefix_string, sequence_number );\n}\n\nsigned_transaction wallet_api::register_account(string name,\n                                                public_key_type owner_pubkey,\n                                                public_key_type active_pubkey,\n                                                string  registrar_account,\n                                                string  referrer_account,\n                                                uint32_t referrer_percent,\n                                                bool broadcast)\n{\n   return my->register_account( name, owner_pubkey, active_pubkey,\n                                registrar_account, referrer_account, referrer_percent, broadcast );\n}\nsigned_transaction wallet_api::create_account_with_brain_key(string brain_key, string account_name,\n                                                             string registrar_account, string referrer_account,\n                                                             bool broadcast /* = false */)\n{\n   return my->create_account_with_brain_key(\n            brain_key, account_name, registrar_account,\n            referrer_account, broadcast\n            );\n}\nsigned_transaction wallet_api::issue_asset(string to_account, string amount, string symbol,\n                                           string memo, bool broadcast)\n{\n   return my->issue_asset(to_account, amount, symbol, memo, broadcast);\n}\n\nsigned_transaction wallet_api::transfer(string from, string to, string amount,\n                                        string asset_symbol, string memo, bool broadcast /* = false */)\n{\n   return my->transfer(from, to, amount, asset_symbol, memo, broadcast);\n}\nsigned_transaction wallet_api::create_asset(string issuer,\n                                            string symbol,\n                                            uint8_t precision,\n                                            asset_options common,\n                                            fc::optional<bitasset_options> bitasset_opts,\n                                            bool broadcast)\n\n{\n   return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast);\n}\n\nsigned_transaction wallet_api::update_asset(string symbol,\n                                            optional<string> new_issuer,\n                                            asset_options new_options,\n                                            bool broadcast /* = false */)\n{\n   return my->update_asset(symbol, new_issuer, new_options, broadcast);\n}\n\nsigned_transaction wallet_api::update_asset_issuer(string symbol,\n                                            string new_issuer,\n                                            bool broadcast /* = false */)\n{\n   return my->update_asset_issuer(symbol, new_issuer, broadcast);\n}\n\nsigned_transaction wallet_api::update_bitasset(string symbol,\n                                               bitasset_options new_options,\n                                               bool broadcast /* = false */)\n{\n   return my->update_bitasset(symbol, new_options, broadcast);\n}\n\nsigned_transaction wallet_api::update_asset_feed_producers(string symbol,\n                                                           flat_set<string> new_feed_producers,\n                                                           bool broadcast /* = false */)\n{\n   return my->update_asset_feed_producers(symbol, new_feed_producers, broadcast);\n}\n\nsigned_transaction wallet_api::publish_asset_feed(string publishing_account,\n                                                  string symbol,\n                                                  price_feed feed,\n                                                  bool broadcast /* = false */)\n{\n   return my->publish_asset_feed(publishing_account, symbol, feed, broadcast);\n}\n\nsigned_transaction wallet_api::fund_asset_fee_pool(string from,\n                                                   string symbol,\n                                                   string amount,\n                                                   bool broadcast /* = false */)\n{\n   return my->fund_asset_fee_pool(from, symbol, amount, broadcast);\n}\n\nsigned_transaction wallet_api::claim_asset_fee_pool(string symbol,\n                                                    string amount,\n                                                    bool broadcast /* = false */)\n{\n   return my->claim_asset_fee_pool(symbol, amount, broadcast);\n}\n\nsigned_transaction wallet_api::reserve_asset(string from,\n                                          string amount,\n                                          string symbol,\n                                          bool broadcast /* = false */)\n{\n   return my->reserve_asset(from, amount, symbol, broadcast);\n}\n\nsigned_transaction wallet_api::global_settle_asset(string symbol,\n                                                   price settle_price,\n                                                   bool broadcast /* = false */)\n{\n   return my->global_settle_asset(symbol, settle_price, broadcast);\n}\n\nsigned_transaction wallet_api::settle_asset(string account_to_settle,\n                                            string amount_to_settle,\n                                            string symbol,\n                                            bool broadcast /* = false */)\n{\n   return my->settle_asset(account_to_settle, amount_to_settle, symbol, broadcast);\n}\n\nsigned_transaction wallet_api::bid_collateral(string bidder_name,\n                                              string debt_amount, string debt_symbol,\n                                              string additional_collateral,\n                                              bool broadcast )\n{\n   return my->bid_collateral(bidder_name, debt_amount, debt_symbol, additional_collateral, broadcast);\n}\n\nsigned_transaction wallet_api::whitelist_account(string authorizing_account,\n                                                 string account_to_list,\n                                                 account_whitelist_operation::account_listing new_listing_status,\n                                                 bool broadcast /* = false */)\n{\n   return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast);\n}\n\nsigned_transaction wallet_api::create_committee_member(string owner_account, string url,\n                                               bool broadcast /* = false */)\n{\n   return my->create_committee_member(owner_account, url, broadcast);\n}\n\nmap<string,witness_id_type> wallet_api::list_witnesses(const string& lowerbound, uint32_t limit)\n{\n   return my->_remote_db->lookup_witness_accounts(lowerbound, limit);\n}\n\nmap<string,committee_member_id_type> wallet_api::list_committee_members(const string& lowerbound, uint32_t limit)\n{\n   return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit);\n}\n\nwitness_object wallet_api::get_witness(string owner_account)\n{\n   return my->get_witness(owner_account);\n}\n\ncommittee_member_object wallet_api::get_committee_member(string owner_account)\n{\n   return my->get_committee_member(owner_account);\n}\n\nsigned_transaction wallet_api::create_witness(string owner_account,\n                                              string url,\n                                              bool broadcast /* = false */)\n{\n   return my->create_witness(owner_account, url, broadcast);\n}\n\nsigned_transaction wallet_api::create_worker(\n   string owner_account,\n   time_point_sec work_begin_date,\n   time_point_sec work_end_date,\n   share_type daily_pay,\n   string name,\n   string url,\n   variant worker_settings,\n   bool broadcast /* = false */)\n{\n   return my->create_worker( owner_account, work_begin_date, work_end_date,\n      daily_pay, name, url, worker_settings, broadcast );\n}\n\nsigned_transaction wallet_api::update_worker_votes(\n   string owner_account,\n   worker_vote_delta delta,\n   bool broadcast /* = false */)\n{\n   return my->update_worker_votes( owner_account, delta, broadcast );\n}\n\nsigned_transaction wallet_api::update_witness(\n   string witness_name,\n   string url,\n   string block_signing_key,\n   bool broadcast /* = false */)\n{\n   return my->update_witness(witness_name, url, block_signing_key, broadcast);\n}\n\nvector< vesting_balance_object_with_info > wallet_api::get_vesting_balances( string account_name )\n{\n   return my->get_vesting_balances( account_name );\n}\n\nsigned_transaction wallet_api::withdraw_vesting(\n   string witness_name,\n   string amount,\n   string asset_symbol,\n   bool broadcast /* = false */)\n{\n   return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast );\n}\n\nsigned_transaction wallet_api::vote_for_committee_member(string voting_account,\n                                                 string witness,\n                                                 bool approve,\n                                                 bool broadcast /* = false */)\n{\n   return my->vote_for_committee_member(voting_account, witness, approve, broadcast);\n}\n\nsigned_transaction wallet_api::vote_for_witness(string voting_account,\n                                                string witness,\n                                                bool approve,\n                                                bool broadcast /* = false */)\n{\n   return my->vote_for_witness(voting_account, witness, approve, broadcast);\n}\n\nsigned_transaction wallet_api::set_voting_proxy(string account_to_modify,\n                                                optional<string> voting_account,\n                                                bool broadcast /* = false */)\n{\n   return my->set_voting_proxy(account_to_modify, voting_account, broadcast);\n}\n\nsigned_transaction wallet_api::set_desired_witness_and_committee_member_count(string account_to_modify,\n                                                                      uint16_t desired_number_of_witnesses,\n                                                                      uint16_t desired_number_of_committee_members,\n                                                                      bool broadcast /* = false */)\n{\n   return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses,\n                                                     desired_number_of_committee_members, broadcast);\n}\n\nvoid wallet_api::set_wallet_filename(string wallet_filename)\n{\n   my->_wallet_filename = wallet_filename;\n}\n\nsigned_transaction wallet_api::sign_transaction(signed_transaction tx, bool broadcast /* = false */)\n{ try {\n   return my->sign_transaction( tx, broadcast);\n} FC_CAPTURE_AND_RETHROW( (tx) ) }\n\nsigned_transaction wallet_api::sign_transaction2(signed_transaction tx, const vector<public_key_type>& signing_keys,\n                                                 bool broadcast /* = false */)\n{ try {\n   return my->sign_transaction2( tx, signing_keys, broadcast);\n} FC_CAPTURE_AND_RETHROW( (tx) ) }\n\nflat_set<public_key_type> wallet_api::get_transaction_signers(const signed_transaction &tx) const\n{ try {\n   return my->get_transaction_signers(tx);\n} FC_CAPTURE_AND_RETHROW( (tx) ) }\n\nvector<flat_set<account_id_type>> wallet_api::get_key_references(const vector<public_key_type> &keys) const\n{ try {\n   return my->get_key_references(keys);\n} FC_CAPTURE_AND_RETHROW( (keys) ) }\n\noperation wallet_api::get_prototype_operation(string operation_name)\n{\n   return my->get_prototype_operation( operation_name );\n}\n\nvoid wallet_api::dbg_make_uia(string creator, string symbol)\n{\n   FC_ASSERT(!is_locked());\n   my->dbg_make_uia(creator, symbol);\n}\n\nvoid wallet_api::dbg_make_mia(string creator, string symbol)\n{\n   FC_ASSERT(!is_locked());\n   my->dbg_make_mia(creator, symbol);\n}\n\nvoid wallet_api::dbg_push_blocks( std::string src_filename, uint32_t count )\n{\n   my->dbg_push_blocks( src_filename, count );\n}\n\nvoid wallet_api::dbg_generate_blocks( std::string debug_wif_key, uint32_t count )\n{\n   my->dbg_generate_blocks( debug_wif_key, count );\n}\n\nvoid wallet_api::dbg_stream_json_objects( const std::string& filename )\n{\n   my->dbg_stream_json_objects( filename );\n}\n\nvoid wallet_api::dbg_update_object( fc::variant_object update )\n{\n   my->dbg_update_object( update );\n}\n\nvoid wallet_api::network_add_nodes( const vector<string>& nodes )\n{\n   my->network_add_nodes( nodes );\n}\n\nvector< variant > wallet_api::network_get_connected_peers()\n{\n   return my->network_get_connected_peers();\n}\n\nvoid wallet_api::flood_network(string prefix, uint32_t number_of_transactions)\n{\n   FC_ASSERT(!is_locked());\n   my->flood_network(prefix, number_of_transactions);\n}\n\nsigned_transaction wallet_api::propose_parameter_change(\n   const string& proposing_account,\n   fc::time_point_sec expiration_time,\n   const variant_object& changed_values,\n   bool broadcast /* = false */\n   )\n{\n   return my->propose_parameter_change( proposing_account, expiration_time, changed_values, broadcast );\n}\n\nsigned_transaction wallet_api::propose_fee_change(\n   const string& proposing_account,\n   fc::time_point_sec expiration_time,\n   const variant_object& changed_fees,\n   bool broadcast /* = false */\n   )\n{\n   return my->propose_fee_change( proposing_account, expiration_time, changed_fees, broadcast );\n}\n\nsigned_transaction wallet_api::approve_proposal(\n   const string& fee_paying_account,\n   const string& proposal_id,\n   const approval_delta& delta,\n   bool broadcast /* = false */\n   )\n{\n   return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast );\n}\n\nglobal_property_object wallet_api::get_global_properties() const\n{\n   return my->get_global_properties();\n}\n\ndynamic_global_property_object wallet_api::get_dynamic_global_properties() const\n{\n   return my->get_dynamic_global_properties();\n}\n\nsigned_transaction wallet_api::add_transaction_signature( signed_transaction tx,\n                                                          bool broadcast )\n{\n   return my->add_transaction_signature( tx, broadcast );\n}\n\nstring wallet_api::help()const\n{\n   std::vector<std::string> method_names = my->method_documentation.get_method_names();\n   std::stringstream ss;\n   for (const std::string method_name : method_names)\n   {\n      try\n      {\n         ss << my->method_documentation.get_brief_description(method_name);\n      }\n      catch (const fc::key_not_found_exception&)\n      {\n         ss << method_name << \" (no help available)\\n\";\n      }\n   }\n   return ss.str();\n}\n\nstring wallet_api::gethelp(const string& method)const\n{\n   fc::api<wallet_api> tmp;\n   std::stringstream ss;\n   ss << \"\\n\";\n\n   std::string doxygenHelpString = my->method_documentation.get_detailed_description(method);\n   if (!doxygenHelpString.empty())\n      ss << doxygenHelpString << \"\\n\";\n\n   if( method == \"import_key\" )\n   {\n      ss << \"usage: import_key ACCOUNT_NAME_OR_ID  WIF_PRIVATE_KEY\\n\\n\";\n      ss << \"example: import_key \\\"1.3.11\\\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\\n\";\n      ss << \"example: import_key \\\"usera\\\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\\n\";\n   }\n   else if( method == \"transfer\" )\n   {\n      ss << \"usage: transfer FROM TO AMOUNT SYMBOL \\\"memo\\\" BROADCAST\\n\\n\";\n      ss << \"example: transfer \\\"1.3.11\\\" \\\"1.3.4\\\" 1000.03 CORE \\\"memo\\\" true\\n\";\n      ss << \"example: transfer \\\"usera\\\" \\\"userb\\\" 1000.123 CORE \\\"memo\\\" true\\n\";\n   }\n   else if( method == \"create_account_with_brain_key\" )\n   {\n      ss << \"usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\\n\\n\";\n      ss << \"example: create_account_with_brain_key \"\n         << \"\\\"my really long brain key\\\" \\\"newaccount\\\" \\\"1.3.11\\\" \\\"1.3.11\\\" true\\n\";\n      ss << \"example: create_account_with_brain_key \"\n         << \"\\\"my really long brain key\\\" \\\"newaccount\\\" \\\"someaccount\\\" \\\"otheraccount\\\" true\\n\";\n      ss << \"\\n\";\n      ss << \"This method should be used if you would like the wallet to generate new keys derived from the \"\n         << \"brain key.\\n\";\n      ss << \"The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY.  \"\n         << \"Use\\n\";\n      ss << \"register_account if you already know the keys you know the public keys that you would like to \"\n         << \"register.\\n\";\n\n   }\n   else if( method == \"register_account\" )\n   {\n      ss << \"usage: register_account ACCOUNT_NAME OWNER_PUBLIC_KEY ACTIVE_PUBLIC_KEY REGISTRAR \"\n         << \"REFERRER REFERRER_PERCENT BROADCAST\\n\\n\";\n      ss << \"example: register_account \\\"newaccount\\\" \\\"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\\\" \"\n         << \"\\\"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\\\" \\\"1.3.11\\\" \\\"1.3.11\\\" 50 true\\n\";\n      ss << \"\\n\";\n      ss << \"Use this method to register an account for which you do not know the private keys.\";\n   }\n   else if( method == \"create_asset\" )\n   {\n      ss << \"usage: ISSUER SYMBOL PRECISION_DIGITS OPTIONS BITASSET_OPTIONS BROADCAST\\n\\n\";\n      ss << \"PRECISION_DIGITS: the number of digits after the decimal point\\n\\n\";\n      ss << \"Example value of OPTIONS: \\n\";\n      ss << fc::json::to_pretty_string( graphene::chain::asset_options() );\n      ss << \"\\nExample value of BITASSET_OPTIONS: \\n\";\n      ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() );\n      ss << \"\\nBITASSET_OPTIONS may be null\\n\";\n   }\n   else if (doxygenHelpString.empty())\n      ss << \"No help defined for method \" << method << \"\\n\";\n\n   return ss.str();\n}\n\nbool wallet_api::load_wallet_file( string wallet_filename )\n{\n   return my->load_wallet_file( wallet_filename );\n}\n\nvoid wallet_api::quit()\n{\n   my->quit();\n}\n\nvoid wallet_api::save_wallet_file( string wallet_filename )\n{\n   my->save_wallet_file( wallet_filename );\n}\n\nstd::map<string,std::function<string(fc::variant,const fc::variants&)> >\nwallet_api::get_result_formatters() const\n{\n   return my->get_result_formatters();\n}\n\nbool wallet_api::is_locked()const\n{\n   return my->is_locked();\n}\nbool wallet_api::is_new()const\n{\n   return my->_wallet.cipher_keys.size() == 0;\n}\n\nvoid wallet_api::encrypt_keys()\n{\n   my->encrypt_keys();\n}\n\nvoid wallet_api::lock()\n{ try {\n   FC_ASSERT( !is_locked() );\n   encrypt_keys();\n   for( auto key : my->_keys )\n      key.second = key_to_wif(fc::ecc::private_key());\n   my->_keys.clear();\n   my->_checksum = fc::sha512();\n   my->self.lock_changed(true);\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid wallet_api::unlock(string password)\n{ try {\n   FC_ASSERT(password.size() > 0);\n   auto pw = fc::sha512::hash(password.c_str(), password.size());\n   vector<char> decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys);\n   auto pk = fc::raw::unpack<plain_keys>(decrypted);\n   FC_ASSERT(pk.checksum == pw);\n   my->_keys = std::move(pk.keys);\n   my->_checksum = pk.checksum;\n   my->self.lock_changed(false);\n} FC_CAPTURE_AND_RETHROW() }\n\nvoid wallet_api::set_password( string password )\n{\n   if( !is_new() )\n      FC_ASSERT( !is_locked(), \"The wallet must be unlocked before the password can be set\" );\n   my->_checksum = fc::sha512::hash( password.c_str(), password.size() );\n   lock();\n}\n\nvector< signed_transaction > wallet_api::import_balance(\n      string name_or_id,\n      const vector<string>& wif_keys,\n      bool broadcast )\n{\n   return my->import_balance( name_or_id, wif_keys, broadcast );\n}\n\nmap<public_key_type, string> wallet_api::dump_private_keys()\n{\n   FC_ASSERT(!is_locked());\n   return my->_keys;\n}\n\nsigned_transaction wallet_api::upgrade_account( string name, bool broadcast )\n{\n   return my->upgrade_account(name,broadcast);\n}\n\nsigned_transaction wallet_api::sell_asset(string seller_account,\n                                          string amount_to_sell,\n                                          string symbol_to_sell,\n                                          string min_to_receive,\n                                          string symbol_to_receive,\n                                          uint32_t expiration,\n                                          bool   fill_or_kill,\n                                          bool   broadcast)\n{\n   return my->sell_asset(seller_account, amount_to_sell, symbol_to_sell, min_to_receive,\n                         symbol_to_receive, expiration, fill_or_kill, broadcast);\n}\n\nsigned_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell,\n                                                string asset_symbol, string amount_of_collateral, bool broadcast)\n{\n   FC_ASSERT(!is_locked());\n   return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast);\n}\n\nsigned_transaction wallet_api::borrow_asset_ext( string seller_name, string amount_to_sell,\n                                                 string asset_symbol, string amount_of_collateral,\n                                                 call_order_update_operation::extensions_type extensions,\n                                                 bool broadcast)\n{\n   FC_ASSERT(!is_locked());\n   return my->borrow_asset_ext(seller_name, amount_to_sell, asset_symbol,\n                               amount_of_collateral, extensions, broadcast);\n}\n\nsigned_transaction wallet_api::cancel_order(object_id_type order_id, bool broadcast)\n{\n   FC_ASSERT(!is_locked());\n   return my->cancel_order(order_id, broadcast);\n}\n\nmemo_data wallet_api::sign_memo(string from, string to, string memo)\n{\n   FC_ASSERT(!is_locked());\n   return my->sign_memo(from, to, memo);\n}\n\nstring wallet_api::read_memo(const memo_data& memo)\n{\n   FC_ASSERT(!is_locked());\n   return my->read_memo(memo);\n}\n\nsigned_message wallet_api::sign_message(string signer, string message)\n{\n   FC_ASSERT(!is_locked());\n   return my->sign_message(signer, message);\n}\n\nbool wallet_api::verify_message( string message, string account, int block, const string& time, compact_signature sig )\n{\n   return my->verify_message( message, account, block, time, sig );\n}\n\n/** Verify a message signed with sign_message\n *\n * @param message the signed_message structure containing message, meta data and signature\n * @return true if signature matches\n */\nbool wallet_api::verify_signed_message( signed_message message )\n{\n   return my->verify_signed_message( message );\n}\n\n/** Verify a message signed with sign_message, in its encapsulated form.\n *\n * @param message the complete encapsulated message string including separators and line feeds\n * @return true if signature matches\n */\nbool wallet_api::verify_encapsulated_message( string message )\n{\n   return my->verify_encapsulated_message( message );\n}\n\n\nstring wallet_api::get_key_label( public_key_type key )const\n{\n   auto key_itr   = my->_wallet.labeled_keys.get<by_key>().find(key);\n   if( key_itr != my->_wallet.labeled_keys.get<by_key>().end() )\n      return key_itr->label;\n   return string();\n}\n\nstring wallet_api::get_private_key( public_key_type pubkey )const\n{\n   return key_to_wif( my->get_private_key( pubkey ) );\n}\n\npublic_key_type  wallet_api::get_public_key( string label )const\n{\n   try { return fc::variant(label, 1).as<public_key_type>( 1 ); } catch ( ... ){}\n\n   auto key_itr   = my->_wallet.labeled_keys.get<by_label>().find(label);\n   if( key_itr != my->_wallet.labeled_keys.get<by_label>().end() )\n      return key_itr->key;\n   return public_key_type();\n}\n\nbool               wallet_api::set_key_label( public_key_type key, string label )\n{\n   auto result = my->_wallet.labeled_keys.insert( key_label{label,key} );\n   if( result.second  ) return true;\n\n   auto key_itr   = my->_wallet.labeled_keys.get<by_key>().find(key);\n   auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);\n   if( label_itr == my->_wallet.labeled_keys.get<by_label>().end() )\n   {\n      if( key_itr != my->_wallet.labeled_keys.get<by_key>().end() )\n         return my->_wallet.labeled_keys.get<by_key>().modify( key_itr, [&]( key_label& obj ){ obj.label = label; } );\n   }\n   return false;\n}\nmap<string,public_key_type> wallet_api::get_blind_accounts()const\n{\n   map<string,public_key_type> result;\n   for( const auto& item : my->_wallet.labeled_keys )\n      result[item.label] = item.key;\n   return result;\n}\nmap<string,public_key_type> wallet_api::get_my_blind_accounts()const\n{\n   FC_ASSERT( !is_locked() );\n   map<string,public_key_type> result;\n   for( const auto& item : my->_wallet.labeled_keys )\n   {\n      if( my->_keys.find(item.key) != my->_keys.end() )\n         result[item.label] = item.key;\n   }\n   return result;\n}\n\npublic_key_type    wallet_api::create_blind_account( string label, string brain_key  )\n{\n   FC_ASSERT( !is_locked() );\n\n   auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);\n   if( label_itr !=  my->_wallet.labeled_keys.get<by_label>().end() )\n      FC_ASSERT( !\"Key with label already exists\" );\n   brain_key = fc::trim_and_normalize_spaces( brain_key );\n   auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() );\n   auto priv_key = fc::ecc::private_key::regenerate( secret );\n   public_key_type pub_key  = priv_key.get_public_key();\n\n   FC_ASSERT( set_key_label( pub_key, label ) );\n\n   my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key );\n\n   save_wallet_file();\n   return pub_key;\n}\n\nvector<asset>   wallet_api::get_blind_balances( string key_or_label )\n{\n   vector<asset> result;\n   map<asset_id_type, share_type> balances;\n\n   vector<commitment_type> used;\n\n   auto pub_key = get_public_key( key_or_label );\n   auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();\n   auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false)  );\n   auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true)  );\n   while( start != end )\n   {\n      if( !start->used  )\n      {\n         auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} );\n         if( answer.size() )\n            balances[start->amount.asset_id] += start->amount.amount;\n         else\n            used.push_back( start->commitment() );\n      }\n      ++start;\n   }\n   for( const auto& u : used )\n   {\n      auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );\n      my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );\n   }\n   for( auto item : balances )\n      result.push_back( asset( item.second, item.first ) );\n   return result;\n}\n\nblind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label,\n                                                    string to_account_id_or_name,\n                                                    string amount_in,\n                                                    string symbol,\n                                                    bool broadcast )\n{ try {\n   transfer_from_blind_operation from_blind;\n\n\n   auto fees  = my->_remote_db->get_global_properties().parameters.get_current_fees();\n   fc::optional<asset_object> asset_obj = get_asset(symbol);\n   FC_ASSERT(asset_obj.valid(), \"Could not find asset matching ${asset}\", (\"asset\", symbol));\n   auto amount = asset_obj->amount_from_string(amount_in);\n\n   from_blind.fee  = fees.calculate_fee( from_blind, asset_obj->options.core_exchange_rate );\n\n   auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount );\n\n\n   auto conf = blind_transfer_help( from_blind_account_key_or_label,\n                               from_blind_account_key_or_label,\n                               blind_in, symbol, false, true/*to_temp*/ );\n   FC_ASSERT( conf.outputs.size() > 0 );\n\n   auto to_account = my->get_account( to_account_id_or_name );\n   from_blind.to = to_account.id;\n   from_blind.amount = amount;\n   from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor;\n   from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } );\n   from_blind.fee  = fees.calculate_fee( from_blind, asset_obj->options.core_exchange_rate );\n\n   idump( (from_blind) );\n   conf.trx.operations.push_back(from_blind);\n   ilog( \"about to validate\" );\n   conf.trx.validate();\n\n   ilog( \"about to broadcast\" );\n   conf.trx = sign_transaction( conf.trx, broadcast );\n\n   if( broadcast && conf.outputs.size() == 2 ) {\n\n       // Save the change\n       blind_confirmation::output conf_output;\n       blind_confirmation::output change_output = conf.outputs[0];\n\n       // The wallet must have a private key for confirmation.to, this is used to decrypt the memo\n       public_key_type from_key = get_public_key(from_blind_account_key_or_label);\n       conf_output.confirmation.to = from_key;\n       conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key;\n       conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo;\n       conf_output.confirmation_receipt = conf_output.confirmation;\n       //try {\n       receive_blind_transfer( conf_output.confirmation_receipt,\n                               from_blind_account_key_or_label,\n                               \"@\"+to_account.name );\n       //} catch ( ... ){}\n   }\n\n   return conf;\n} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) }\n\nblind_confirmation wallet_api::blind_transfer( string from_key_or_label,\n                                               string to_key_or_label,\n                                               string amount_in,\n                                               string symbol,\n                                               bool broadcast )\n{\n   return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false );\n}\nblind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,\n                                               string to_key_or_label,\n                                               string amount_in,\n                                               string symbol,\n                                               bool broadcast,\n                                               bool to_temp )\n{\n   blind_confirmation confirm;\n   try {\n\n   FC_ASSERT( !is_locked() );\n   public_key_type from_key = get_public_key(from_key_or_label);\n   public_key_type to_key   = get_public_key(to_key_or_label);\n\n   fc::optional<asset_object> asset_obj = get_asset(symbol);\n   FC_ASSERT(asset_obj.valid(), \"Could not find asset matching ${asset}\", (\"asset\", symbol));\n\n   blind_transfer_operation blind_tr;\n   blind_tr.outputs.resize(2);\n\n   auto fees  = my->_remote_db->get_global_properties().parameters.get_current_fees();\n\n   auto amount = asset_obj->amount_from_string(amount_in);\n\n   asset total_amount = asset_obj->amount(0);\n\n   vector<fc::sha256> blinding_factors;\n\n   //auto from_priv_key = my->get_private_key( from_key );\n\n   blind_tr.fee  = fees.calculate_fee( blind_tr, asset_obj->options.core_exchange_rate );\n\n   vector<commitment_type> used;\n\n   auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();\n   auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false)  );\n   auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true)  );\n   while( start != end )\n   {\n      auto result = my->_remote_db->get_blinded_balances( {start->commitment() } );\n      if( result.size() == 0 )\n      {\n         used.push_back( start->commitment() );\n      }\n      else\n      {\n         blind_tr.inputs.push_back({start->commitment(), start->control_authority});\n         blinding_factors.push_back( start->data.blinding_factor );\n         total_amount += start->amount;\n\n         if( total_amount >= amount + blind_tr.fee )\n            break;\n      }\n      ++start;\n   }\n   for( const auto& u : used )\n   {\n      auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );\n      my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );\n   }\n\n   FC_ASSERT( total_amount >= amount+blind_tr.fee,\n              \"Insufficient Balance\",\n              (\"available\",total_amount)(\"amount\",amount)(\"fee\",blind_tr.fee) );\n\n   auto one_time_key = fc::ecc::private_key::generate();\n   auto secret       = one_time_key.get_shared_secret( to_key );\n   auto child        = fc::sha256::hash( secret );\n   auto nonce        = fc::sha256::hash( one_time_key.get_secret() );\n   auto blind_factor = fc::sha256::hash( child );\n\n   auto from_secret  = one_time_key.get_shared_secret( from_key );\n   auto from_child   = fc::sha256::hash( from_secret );\n   auto from_nonce   = fc::sha256::hash( nonce );\n\n   auto change = total_amount - amount - blind_tr.fee;\n   fc::sha256 change_blind_factor;\n   fc::sha256 to_blind_factor;\n   if( change.amount > 0 )\n   {\n      idump((\"to_blind_factor\")(blind_factor) );\n      blinding_factors.push_back( blind_factor );\n      change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 );\n      wdump((\"change_blind_factor\")(change_blind_factor) );\n   }\n   else // change == 0\n   {\n      blind_tr.outputs.resize(1);\n      blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );\n      idump((\"to_sum_blind_factor\")(blind_factor) );\n      blinding_factors.push_back( blind_factor );\n      idump((\"nochange to_blind_factor\")(blind_factor) );\n   }\n   fc::ecc::public_key from_pub_key = from_key;\n   fc::ecc::public_key to_pub_key = to_key;\n\n   blind_output to_out;\n   to_out.owner       = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );\n   to_out.commitment  = fc::ecc::blind( blind_factor, amount.amount.value );\n   idump((\"to_out.blind\")(blind_factor)(to_out.commitment) );\n\n\n   if( blind_tr.outputs.size() > 1 )\n   {\n      to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce,\n                                                      0, RANGE_PROOF_MANTISSA, amount.amount.value );\n\n      blind_output change_out;\n      change_out.owner       = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 );\n      change_out.commitment  = fc::ecc::blind( change_blind_factor, change.amount.value );\n      change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce,\n                                                          0, RANGE_PROOF_MANTISSA, change.amount.value );\n      blind_tr.outputs[1] = change_out;\n\n\n      blind_confirmation::output conf_output;\n      conf_output.label = from_key_or_label;\n      conf_output.pub_key = from_key;\n      conf_output.decrypted_memo.from = from_key;\n      conf_output.decrypted_memo.amount = change;\n      conf_output.decrypted_memo.blinding_factor = change_blind_factor;\n      conf_output.decrypted_memo.commitment = change_out.commitment;\n      conf_output.decrypted_memo.check   = from_secret._hash[0].value();\n      conf_output.confirmation.one_time_key = one_time_key.get_public_key();\n      conf_output.confirmation.to = from_key;\n      conf_output.confirmation.encrypted_memo =\n            fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) );\n      conf_output.auth = change_out.owner;\n      conf_output.confirmation_receipt = conf_output.confirmation;\n\n      confirm.outputs.push_back( conf_output );\n   }\n   blind_tr.outputs[0] = to_out;\n\n   blind_confirmation::output conf_output;\n   conf_output.label = to_key_or_label;\n   conf_output.pub_key = to_key;\n   conf_output.decrypted_memo.from = from_key;\n   conf_output.decrypted_memo.amount = amount;\n   conf_output.decrypted_memo.blinding_factor = blind_factor;\n   conf_output.decrypted_memo.commitment = to_out.commitment;\n   conf_output.decrypted_memo.check   = secret._hash[0].value();\n   conf_output.confirmation.one_time_key = one_time_key.get_public_key();\n   conf_output.confirmation.to = to_key;\n   conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );\n   conf_output.auth = to_out.owner;\n   conf_output.confirmation_receipt = conf_output.confirmation;\n\n   confirm.outputs.push_back( conf_output );\n\n   /** commitments must be in sorted order */\n   std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(),\n              [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );\n   std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(),\n              [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } );\n\n   confirm.trx.operations.emplace_back( std::move(blind_tr) );\n   ilog( \"validate before\" );\n   confirm.trx.validate();\n   confirm.trx = sign_transaction(confirm.trx, broadcast);\n\n   if( broadcast )\n   {\n      for( const auto& out : confirm.outputs )\n      {\n         try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, \"\" ); } catch ( ... ){}\n      }\n   }\n\n   return confirm;\n} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) }\n\n\n\n/*\n *  Transfers a public balance from @from to one or more blinded balances using a\n *  stealth transfer.\n */\nblind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name,\n                                                  string asset_symbol,\n                                                  /* map from key or label to amount */\n                                                  vector<pair<string, string>> to_amounts,\n                                                  bool broadcast )\n{ try {\n   FC_ASSERT( !is_locked() );\n   idump((to_amounts));\n\n   blind_confirmation confirm;\n   account_object from_account = my->get_account(from_account_id_or_name);\n\n   fc::optional<asset_object> asset_obj = get_asset(asset_symbol);\n   FC_ASSERT(asset_obj, \"Could not find asset matching ${asset}\", (\"asset\", asset_symbol));\n\n   transfer_to_blind_operation bop;\n   bop.from   = from_account.id;\n\n   vector<fc::sha256> blinding_factors;\n\n   asset total_amount = asset_obj->amount(0);\n\n   for( auto item : to_amounts )\n   {\n      auto one_time_key = fc::ecc::private_key::generate();\n      auto to_key       = get_public_key( item.first );\n      auto secret       = one_time_key.get_shared_secret( to_key );\n      auto child        = fc::sha256::hash( secret );\n      auto nonce        = fc::sha256::hash( one_time_key.get_secret() );\n      auto blind_factor = fc::sha256::hash( child );\n\n      blinding_factors.push_back( blind_factor );\n\n      auto amount = asset_obj->amount_from_string(item.second);\n      total_amount += amount;\n\n\n      fc::ecc::public_key to_pub_key = to_key;\n      blind_output out;\n      out.owner       = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );\n      out.commitment  = fc::ecc::blind( blind_factor, amount.amount.value );\n      if( to_amounts.size() > 1 )\n         out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce,\n                                                      0, RANGE_PROOF_MANTISSA, amount.amount.value );\n\n      blind_confirmation::output conf_output;\n      conf_output.label = item.first;\n      conf_output.pub_key = to_key;\n      conf_output.decrypted_memo.amount = amount;\n      conf_output.decrypted_memo.blinding_factor = blind_factor;\n      conf_output.decrypted_memo.commitment = out.commitment;\n      conf_output.decrypted_memo.check   = secret._hash[0].value();\n      conf_output.confirmation.one_time_key = one_time_key.get_public_key();\n      conf_output.confirmation.to = to_key;\n      conf_output.confirmation.encrypted_memo =\n            fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );\n      conf_output.confirmation_receipt = conf_output.confirmation;\n\n      confirm.outputs.push_back( conf_output );\n\n      bop.outputs.push_back(out);\n   }\n   bop.amount          = total_amount;\n   bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );\n\n   /** commitments must be in sorted order */\n   std::sort( bop.outputs.begin(), bop.outputs.end(),\n              [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );\n\n   confirm.trx.operations.push_back( bop );\n   my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.get_current_fees());\n   confirm.trx.validate();\n   confirm.trx = sign_transaction(confirm.trx, broadcast);\n\n   if( broadcast )\n   {\n      for( const auto& out : confirm.outputs )\n      {\n         try {\n            receive_blind_transfer( out.confirmation_receipt, \"@\"+from_account.name, \"from @\"+from_account.name );\n         } catch ( ... ){}\n      }\n   }\n\n   return confirm;\n} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) }\n\nblind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo )\n{\n   FC_ASSERT( !is_locked() );\n   stealth_confirmation conf(confirmation_receipt);\n   FC_ASSERT( conf.to );\n\n   blind_receipt result;\n   result.conf = conf;\n\n   auto to_priv_key_itr = my->_keys.find( *conf.to );\n   FC_ASSERT( to_priv_key_itr != my->_keys.end(), \"No private key for receiver\", (\"conf\",conf) );\n\n\n   auto to_priv_key = wif_to_key( to_priv_key_itr->second );\n   FC_ASSERT( to_priv_key );\n\n   auto secret       = to_priv_key->get_shared_secret( conf.one_time_key );\n   auto child        = fc::sha256::hash( secret );\n\n   auto child_priv_key = to_priv_key->child( child );\n   //auto blind_factor = fc::sha256::hash( child );\n\n   auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo );\n   auto memo = fc::raw::unpack<stealth_confirmation::memo_data>( plain_memo );\n\n   result.to_key   = *conf.to;\n   result.to_label = get_key_label( result.to_key );\n   if( memo.from )\n   {\n      result.from_key = *memo.from;\n      result.from_label = get_key_label( result.from_key );\n      if( result.from_label == string() )\n      {\n         result.from_label = opt_from;\n         set_key_label( result.from_key, result.from_label );\n      }\n   }\n   else\n   {\n      result.from_label = opt_from;\n   }\n   result.amount = memo.amount;\n   result.memo = opt_memo;\n\n   // confirm the amount matches the commitment (verify the blinding factor)\n   auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value );\n   FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) );\n\n   blind_balance bal;\n   bal.amount = memo.amount;\n   bal.to     = *conf.to;\n   if( memo.from ) bal.from   = *memo.from;\n   bal.one_time_key = conf.one_time_key;\n   bal.blinding_factor = memo.blinding_factor;\n   bal.commitment = memo.commitment;\n   bal.used = false;\n\n   auto child_pubkey = child_priv_key.get_public_key();\n   auto owner = authority(1, public_key_type(child_pubkey), 1);\n   result.control_authority = owner;\n   result.data = memo;\n\n   auto child_key_itr = owner.key_auths.find( child_pubkey );\n   if( child_key_itr != owner.key_auths.end() )\n      my->_keys[child_key_itr->first] = key_to_wif( child_priv_key );\n\n   // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal );\n\n   result.date = fc::time_point::now();\n   my->_wallet.blind_receipts.insert( result );\n   my->_keys[child_pubkey] = key_to_wif( child_priv_key );\n\n   save_wallet_file();\n\n   return result;\n}\n\nvector<blind_receipt> wallet_api::blind_history( string key_or_account )\n{\n   vector<blind_receipt> result;\n   auto pub_key = get_public_key( key_or_account );\n\n   if( pub_key == public_key_type() )\n      return vector<blind_receipt>();\n\n   for( auto& r : my->_wallet.blind_receipts )\n   {\n      if( r.from_key == pub_key || r.to_key == pub_key )\n         result.push_back( r );\n   }\n   std::sort( result.begin(), result.end(),\n              [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } );\n   return result;\n}\n\norder_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit )\n{\n   return( my->_remote_db->get_order_book( base, quote, limit ) );\n}\n\n// custom operations\nsigned_transaction wallet_api::account_store_map(string account, string catalog, bool remove,\n      flat_map<string, optional<string>> key_values, bool broadcast)\n{\n   return my->account_store_map(account, catalog, remove, key_values, broadcast);\n}\n\nvector<account_storage_object> wallet_api::get_account_storage(string account, string catalog)\n{ try {\n   return my->_custom_operations->get_storage_info(account, catalog);\n} FC_CAPTURE_AND_RETHROW( (account)(catalog) ) }\n\nsigned_block_with_info::signed_block_with_info( const signed_block& block )\n   : signed_block( block )\n{\n   block_id = id();\n   signing_key = signee();\n   transaction_ids.reserve( transactions.size() );\n   for( const processed_transaction& tx : transactions )\n      transaction_ids.push_back( tx.id() );\n}\n\nvesting_balance_object_with_info::vesting_balance_object_with_info(\n      const vesting_balance_object& vbo,\n      fc::time_point_sec now )\n   : vesting_balance_object( vbo )\n{\n   allowed_withdraw = get_allowed_withdraw( now );\n   allowed_withdraw_time = now;\n}\n\n} } // graphene::wallet\n\nnamespace fc {\n   void to_variant( const account_multi_index_type& accts, variant& vo, uint32_t max_depth )\n   {\n      to_variant( std::vector<account_object>(accts.begin(), accts.end()), vo, max_depth );\n   }\n\n   void from_variant( const variant& var, account_multi_index_type& vo, uint32_t max_depth )\n   {\n      const std::vector<account_object>& v = var.as<std::vector<account_object>>( max_depth );\n      vo = account_multi_index_type(v.begin(), v.end());\n   }\n}\n"
  },
  {
    "path": "libraries/wallet/wallet_account.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <boost/range/algorithm_ext/erase.hpp>\n#include <boost/range/algorithm/unique.hpp>\n#include <boost/range/algorithm/sort.hpp>\n\n#include \"wallet_api_impl.hpp\"\n#include <graphene/wallet/wallet.hpp>\n#include <graphene/protocol/pts_address.hpp>\n\n/****\n * Wallet API methods to handle accounts\n */\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   std::string wallet_api_impl::account_id_to_string(account_id_type id) const\n   {\n      std::string account_id = fc::to_string(id.space_id)\n                               + \".\" + fc::to_string(id.type_id)\n                               + \".\" + fc::to_string(id.instance.value);\n      return account_id;\n   }\n\n   signed_transaction wallet_api_impl::register_account(string name, public_key_type owner,\n         public_key_type active, string  registrar_account, string  referrer_account,\n         uint32_t referrer_percent, bool broadcast )\n   { try {\n      FC_ASSERT( !self.is_locked() );\n      FC_ASSERT( is_valid_name(name) );\n      account_create_operation account_create_op;\n\n      // #449 referrer_percent is on 0-100 scale, if user has larger\n      // number it means their script is using GRAPHENE_100_PERCENT scale\n      // instead of 0-100 scale.\n      FC_ASSERT( referrer_percent <= 100 );\n      // TODO:  process when pay_from_account is ID\n\n      account_object registrar_account_object =\n            this->get_account( registrar_account );\n      FC_ASSERT( registrar_account_object.is_lifetime_member() );\n\n      account_id_type registrar_account_id = registrar_account_object.id;\n\n      account_object referrer_account_object =\n            this->get_account( referrer_account );\n      account_create_op.referrer = referrer_account_object.id;\n      account_create_op.referrer_percent = uint16_t( referrer_percent * GRAPHENE_1_PERCENT );\n\n      account_create_op.registrar = registrar_account_id;\n      account_create_op.name = name;\n      account_create_op.owner = authority(1, owner, 1);\n      account_create_op.active = authority(1, active, 1);\n      account_create_op.options.memo_key = active;\n\n      signed_transaction tx;\n      tx.operations.push_back( account_create_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n      tx.validate();\n\n      return sign_transaction(tx, broadcast);\n   } FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)\n                              (referrer_account)(referrer_percent)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::upgrade_account(string name, bool broadcast)\n   { try {\n      FC_ASSERT( !self.is_locked() );\n      account_object account_obj = get_account(name);\n      FC_ASSERT( !account_obj.is_lifetime_member() );\n\n      signed_transaction tx;\n      account_upgrade_operation op;\n      op.account_to_upgrade = account_obj.get_id();\n      op.upgrade_to_lifetime_member = true;\n      tx.operations = {op};\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (name) ) }\n\n   signed_transaction wallet_api_impl::create_account_with_brain_key(string brain_key,\n                                                      string account_name,\n                                                      string registrar_account,\n                                                      string referrer_account,\n                                                      bool broadcast,\n                                                      bool save_wallet )\n   { try {\n      FC_ASSERT( !self.is_locked() );\n      string normalized_brain_key = normalize_brain_key( brain_key );\n      // TODO:  scan blockchain for accounts that exist with same brain key\n      fc::ecc::private_key owner_privkey = derive_private_key( normalized_brain_key, 0 );\n      return create_account_with_private_key(owner_privkey, account_name, registrar_account,\n                                             referrer_account, broadcast, save_wallet);\n   } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account) ) }\n\n   signed_transaction wallet_api_impl::account_store_map(string account, string catalog, bool remove,\n         flat_map<string, optional<string>> key_values, bool broadcast)\n   {\n      try\n      {\n         FC_ASSERT( !self.is_locked() );\n\n         account_id_type account_id = get_account(account).id;\n\n         custom_operation op;\n         account_storage_map store;\n         store.catalog = catalog;\n         store.remove = remove;\n         store.key_values = key_values;\n\n         custom_plugin_operation custom_plugin_op(store);\n         auto packed = fc::raw::pack(custom_plugin_op);\n\n         op.payer = account_id;\n         op.data = packed;\n\n         signed_transaction tx;\n         tx.operations.push_back(op);\n         set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n         tx.validate();\n\n         return sign_transaction(tx, broadcast);\n\n      } FC_CAPTURE_AND_RETHROW( (account)(remove)(catalog)(key_values)(broadcast) )\n   }\n\n   void wallet_api_impl::claim_registered_account(const graphene::chain::account_object& account)\n   {\n      bool import_keys = false;\n      auto it = _wallet.pending_account_registrations.find( account.name );\n      FC_ASSERT( it != _wallet.pending_account_registrations.end() );\n      for (const std::string& wif_key : it->second) {\n         if( !import_key( account.name, wif_key ) )\n         {\n            // somebody else beat our pending registration, there is\n            //    nothing we can do except log it and move on\n            elog( \"account ${name} registered by someone else first!\",\n                  (\"name\", account.name) );\n            // might as well remove it from pending regs,\n            //    because there is now no way this registration\n            //    can become valid (even in the extremely rare\n            //    possibility of migrating to a fork where the\n            //    name is available, the user can always\n            //    manually re-register)\n         } else {\n            import_keys = true;\n         }\n      }\n      _wallet.pending_account_registrations.erase( it );\n\n      if( import_keys ) {\n         save_wallet_file();\n      }\n   }\n\n   // after a witness registration succeeds, this saves the private key in the wallet permanently\n   //\n   void wallet_api_impl::claim_registered_witness(const std::string& witness_name)\n   {\n      auto iter = _wallet.pending_witness_registrations.find(witness_name);\n      FC_ASSERT(iter != _wallet.pending_witness_registrations.end());\n      std::string wif_key = iter->second;\n\n      // get the list key id this key is registered with in the chain\n      fc::optional<fc::ecc::private_key> witness_private_key = wif_to_key(wif_key);\n      FC_ASSERT(witness_private_key);\n\n      auto pub_key = witness_private_key->get_public_key();\n      _keys[pub_key] = wif_key;\n      _wallet.pending_witness_registrations.erase(iter);\n   }\n\n   account_object wallet_api_impl::get_account(account_id_type id) const\n   {\n      std::string account_id = account_id_to_string(id);\n\n      auto rec = _remote_db->get_accounts({account_id}, {}).front();\n      FC_ASSERT(rec);\n      return *rec;\n   }\n\n   account_object wallet_api_impl::get_account(string account_name_or_id) const\n   {\n      FC_ASSERT( account_name_or_id.size() > 0 );\n\n      if( auto id = maybe_id<account_id_type>(account_name_or_id) )\n      {\n         // It's an ID\n         return get_account(*id);\n      } else {\n         auto rec = _remote_db->lookup_account_names({account_name_or_id}).front();\n         FC_ASSERT( rec && rec->name == account_name_or_id );\n         return *rec;\n      }\n   }\n\n   account_id_type wallet_api_impl::get_account_id(string account_name_or_id) const\n   {\n      return get_account(account_name_or_id).get_id();\n   }\n\n   signed_transaction wallet_api_impl::create_account_with_private_key(fc::ecc::private_key owner_privkey,\n         string account_name, string registrar_account, string referrer_account, bool broadcast,\n         bool save_wallet )\n   { try {\n         int active_key_index = find_first_unused_derived_key_index(owner_privkey);\n         fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index);\n\n         int memo_key_index = find_first_unused_derived_key_index(active_privkey);\n         fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index);\n\n         graphene::chain::public_key_type owner_pubkey = owner_privkey.get_public_key();\n         graphene::chain::public_key_type active_pubkey = active_privkey.get_public_key();\n         graphene::chain::public_key_type memo_pubkey = memo_privkey.get_public_key();\n\n         account_create_operation account_create_op;\n\n         // TODO:  process when pay_from_account is ID\n\n         account_object registrar_account_object = get_account( registrar_account );\n\n         account_id_type registrar_account_id = registrar_account_object.id;\n\n         account_object referrer_account_object = get_account( referrer_account );\n         account_create_op.referrer = referrer_account_object.id;\n         account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage;\n\n         account_create_op.registrar = registrar_account_id;\n         account_create_op.name = account_name;\n         account_create_op.owner = authority(1, owner_pubkey, 1);\n         account_create_op.active = authority(1, active_pubkey, 1);\n         account_create_op.options.memo_key = memo_pubkey;\n\n         // current_fee_schedule()\n         // find_account(pay_from_account)\n\n         // account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule());\n\n         signed_transaction tx;\n         tx.operations.push_back( account_create_op );\n         set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n         tx.validate();\n\n         // we do not insert owner_privkey here because\n         //    it is intended to only be used for key recovery\n         _wallet.pending_account_registrations[account_name].push_back(key_to_wif( active_privkey ));\n         _wallet.pending_account_registrations[account_name].push_back(key_to_wif( memo_privkey ));\n         if( save_wallet )\n            save_wallet_file();\n         return sign_transaction(tx, broadcast);\n   } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::whitelist_account(string authorizing_account, string account_to_list, \n         account_whitelist_operation::account_listing new_listing_status, bool broadcast )\n   { try {\n      account_whitelist_operation whitelist_op;\n      whitelist_op.authorizing_account = get_account_id(authorizing_account);\n      whitelist_op.account_to_list = get_account_id(account_to_list);\n      whitelist_op.new_listing = new_listing_status;\n\n      signed_transaction tx;\n      tx.operations.push_back( whitelist_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) }\n\n   vector< vesting_balance_object_with_info > wallet_api_impl::get_vesting_balances( string account_name )\n   { try {\n      fc::optional<vesting_balance_id_type> vbid = maybe_id<vesting_balance_id_type>( account_name );\n      std::vector<vesting_balance_object_with_info> result;\n      fc::time_point_sec now = _remote_db->get_dynamic_global_properties().time;\n\n      if( vbid )\n      {\n         result.emplace_back( get_object(*vbid), now );\n         return result;\n      }\n\n      vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( account_name );\n      if( vbos.size() == 0 )\n         return result;\n\n      for( const vesting_balance_object& vbo : vbos )\n         result.emplace_back( vbo, now );\n\n      return result;\n   } FC_CAPTURE_AND_RETHROW( (account_name) )\n   }\n\n   vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id, \n         const vector<string>& wif_keys, bool broadcast )\n   { try {\n      FC_ASSERT(!is_locked());\n      const dynamic_global_property_object& dpo = _remote_db->get_dynamic_global_properties();\n      account_object claimer = get_account( name_or_id );\n      uint32_t max_ops_per_tx = 30;\n\n      map< address, private_key_type > keys;  // local index of address -> private key\n      vector< address > addrs;\n      bool has_wildcard = false;\n      addrs.reserve( wif_keys.size() );\n      for( const string& wif_key : wif_keys )\n      {\n         if( wif_key == \"*\" )\n         {\n            if( has_wildcard )\n               continue;\n            for( const public_key_type& pub : _wallet.extra_keys[ claimer.id ] )\n            {\n               addrs.push_back( pub );\n               auto it = _keys.find( pub );\n               if( it != _keys.end() )\n               {\n                  fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );\n                  FC_ASSERT( privkey );\n                  keys[ addrs.back() ] = *privkey;\n               }\n               else\n               {\n                  wlog( \"Somehow _keys has no private key for extra_keys public key ${k}\", (\"k\", pub) );\n               }\n            }\n            has_wildcard = true;\n         }\n         else\n         {\n            optional< private_key_type > key = wif_to_key( wif_key );\n            FC_ASSERT( key.valid(), \"Invalid private key\" );\n            fc::ecc::public_key pk = key->get_public_key();\n            addrs.push_back( pk );\n            keys[addrs.back()] = *key;\n            // see chain/balance_evaluator.cpp\n            addrs.push_back( pts_address( pk, false, 56 ) );\n            keys[addrs.back()] = *key;\n            addrs.push_back( pts_address( pk, true, 56 ) );\n            keys[addrs.back()] = *key;\n            addrs.push_back( pts_address( pk, false, 0 ) );\n            keys[addrs.back()] = *key;\n            addrs.push_back( pts_address( pk, true, 0 ) );\n            keys[addrs.back()] = *key;\n         }\n      }\n\n      vector< balance_object > balances = _remote_db->get_balance_objects( addrs );\n      addrs.clear();\n\n      set<asset_id_type> bal_types;\n      for( auto b : balances ) bal_types.insert( b.balance.asset_id );\n\n      struct claim_tx\n      {\n         vector< balance_claim_operation > ops;\n         set< address > addrs;\n      };\n      vector< claim_tx > claim_txs;\n\n      for( const asset_id_type& a : bal_types )\n      {\n         balance_claim_operation op;\n         op.deposit_to_account = claimer.id;\n         for( const balance_object& b : balances )\n         {\n            if( b.balance.asset_id == a )\n            {\n               op.total_claimed = b.available( dpo.time );\n               if( op.total_claimed.amount == 0 )\n                  continue;\n               op.balance_to_claim = b.id;\n               op.balance_owner_key = keys[b.owner].get_public_key();\n               if( (claim_txs.empty()) || (claim_txs.back().ops.size() >= max_ops_per_tx) )\n                  claim_txs.emplace_back();\n               claim_txs.back().ops.push_back(op);\n               claim_txs.back().addrs.insert(b.owner);\n            }\n         }\n      }\n\n      vector< signed_transaction > result;\n\n      for( const claim_tx& ctx : claim_txs )\n      {\n         signed_transaction tx;\n         tx.operations.reserve( ctx.ops.size() );\n         for( const balance_claim_operation& op : ctx.ops )\n            tx.operations.emplace_back( op );\n         set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n         tx.validate();\n         signed_transaction signed_tx = sign_transaction( tx, false );\n         for( const address& addr : ctx.addrs )\n            signed_tx.sign( keys[addr], _chain_id );\n         // if the key for a balance object was the same as a key for the account we're importing it into,\n         // we may end up with duplicate signatures, so remove those\n         boost::erase(signed_tx.signatures, boost::unique<boost::return_found_end>(boost::sort(signed_tx.signatures)));\n         result.push_back( signed_tx );\n         if( broadcast )\n            _remote_net_broadcast->broadcast_transaction(signed_tx);\n      }\n\n      return result;\n   } FC_CAPTURE_AND_RETHROW( (name_or_id) ) }\n\n   vector<flat_set<account_id_type>> wallet_api_impl::get_key_references(const vector<public_key_type> &keys) const\n   {\n       return _remote_db->get_key_references(keys);\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_api_impl.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <boost/algorithm/string/replace.hpp>\n#include <boost/range/adaptors.hpp>\n\n#include <fc/rpc/api_connection.hpp>\n#include <fc/popcount.hpp>\n#include <fc/git_revision.hpp>\n#include <fc/thread/scoped_lock.hpp>\n#include <fc/io/fstream.hpp>\n\n#include <graphene/wallet/wallet.hpp>\n#include \"wallet_api_impl.hpp\"\n#include <graphene/utilities/git_revision.hpp>\n\n#ifndef WIN32\n# include <sys/types.h>\n# include <sys/stat.h>\n#endif\n\n// explicit instantiation for later use\nnamespace fc {\n\ttemplate class api<graphene::wallet::wallet_api, identity_member_with_optionals>;\n}\n\n/****\n * General methods for wallet impl object (ctor, info, about, wallet file, etc.)\n */\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   wallet_api_impl::wallet_api_impl( wallet_api& s, const wallet_data& initial_data, fc::api<login_api> rapi )\n      : self(s),\n        _chain_id(initial_data.chain_id),\n        _remote_api(rapi),\n        _remote_db(rapi->database()),\n        _remote_net_broadcast(rapi->network_broadcast()),\n        _remote_hist(rapi->history())\n   {\n      try {\n         _custom_operations = rapi->custom_operations();\n      }\n      catch(const fc::exception& e)\n      {\n         wlog(\"Custom operations API is not active on server.\");\n      }\n      chain_id_type remote_chain_id = _remote_db->get_chain_id();\n      if( remote_chain_id != _chain_id )\n      {\n         FC_THROW( \"Remote server gave us an unexpected chain_id\",\n            (\"remote_chain_id\", remote_chain_id)\n            (\"chain_id\", _chain_id) );\n      }\n      init_prototype_ops();\n\n      _remote_db->set_block_applied_callback( [this](const variant& block_id )\n      {\n         on_block_applied( block_id );\n      } );\n\n      _wallet.chain_id = _chain_id;\n      _wallet.ws_server = initial_data.ws_server;\n      _wallet.ws_user = initial_data.ws_user;\n      _wallet.ws_password = initial_data.ws_password;\n   }\n\n   wallet_api_impl::~wallet_api_impl()\n   {\n      try\n      {\n         _remote_db->cancel_all_subscriptions();\n      }\n      catch (const fc::exception& e)\n      {\n         // Right now the wallet_api has no way of knowing if the connection to the\n         // witness has already disconnected (via the witness node exiting first).\n         // If it has exited, cancel_all_subscriptsions() will throw and there's\n         // nothing we can do about it.\n         // dlog(\"Caught exception ${e} while canceling database subscriptions\", (\"e\", e));\n      }\n   }\n\n   fc::variant wallet_api_impl::info() const\n   {\n      auto chain_props = get_chain_properties();\n      auto global_props = get_global_properties();\n      auto dynamic_props = get_dynamic_global_properties();\n      fc::mutable_variant_object result;\n      result[\"head_block_num\"] = dynamic_props.head_block_number;\n      result[\"head_block_id\"] = fc::variant(dynamic_props.head_block_id, 1);\n      result[\"head_block_age\"] = fc::get_approximate_relative_time_string(dynamic_props.time,\n                                                                          time_point_sec(time_point::now()),\n                                                                          \" old\");\n      result[\"next_maintenance_time\"] =\n            fc::get_approximate_relative_time_string(dynamic_props.next_maintenance_time);\n      result[\"chain_id\"] = chain_props.chain_id;\n      stringstream participation;\n      participation << fixed << std::setprecision(2) << (100.0*fc::popcount(dynamic_props.recent_slots_filled)) / 128.0;\n      result[\"participation\"] = participation.str();\n      result[\"active_witnesses\"] = fc::variant(global_props.active_witnesses, GRAPHENE_MAX_NESTED_OBJECTS);\n      result[\"active_committee_members\"] =\n            fc::variant(global_props.active_committee_members, GRAPHENE_MAX_NESTED_OBJECTS);\n      return result;\n   }\n\n   /***\n    * @brief return basic information about this program\n    */\n   fc::variant_object wallet_api_impl::about() const\n   {\n      string client_version( graphene::utilities::git_revision_description );\n      const size_t pos = client_version.find( '/' );\n      if( pos != string::npos && client_version.size() > pos )\n         client_version = client_version.substr( pos + 1 );\n\n      fc::mutable_variant_object result;\n      //result[\"blockchain_name\"]        = BLOCKCHAIN_NAME;\n      //result[\"blockchain_description\"] = BTS_BLOCKCHAIN_DESCRIPTION;\n      result[\"client_version\"]           = client_version;\n      result[\"graphene_revision\"]        = graphene::utilities::git_revision_sha;\n      result[\"graphene_revision_age\"]    = fc::get_approximate_relative_time_string( fc::time_point_sec(\n                                                 graphene::utilities::git_revision_unix_timestamp ) );\n      result[\"fc_revision\"]              = fc::git_revision_sha;\n      result[\"fc_revision_age\"]          = fc::get_approximate_relative_time_string( fc::time_point_sec(\n                                                 fc::git_revision_unix_timestamp ) );\n      result[\"compile_date\"]             = \"compiled on \" __DATE__ \" at \" __TIME__;\n      result[\"boost_version\"]            = boost::replace_all_copy(std::string(BOOST_LIB_VERSION), \"_\", \".\");\n      result[\"openssl_version\"]          = OPENSSL_VERSION_TEXT;\n\n      std::string bitness = boost::lexical_cast<std::string>(8 * sizeof(int*)) + \"-bit\";\n#if defined(__APPLE__)\n      std::string os = \"osx\";\n#elif defined(__linux__)\n      std::string os = \"linux\";\n#elif defined(_MSC_VER)\n      std::string os = \"win32\";\n#else\n      std::string os = \"other\";\n#endif\n      result[\"build\"] = os + \" \" + bitness;\n\n      return result;\n   }\n\n   void wallet_api_impl::quit()\n   {\n      ilog( \"Quitting Cli Wallet ...\" );\n\n      throw fc::canceled_exception();\n   }\n\n   chain_property_object wallet_api_impl::get_chain_properties() const\n   {\n      return _remote_db->get_chain_properties();\n   }\n   global_property_object wallet_api_impl::get_global_properties() const\n   {\n      return _remote_db->get_global_properties();\n   }\n   dynamic_global_property_object wallet_api_impl::get_dynamic_global_properties() const\n   {\n      return _remote_db->get_dynamic_global_properties();\n   }\n\n   void wallet_api_impl::on_block_applied( const variant& block_id )\n   {\n      fc::async([this]{resync();}, \"Resync after block\");\n   }\n\n   void wallet_api_impl::set_operation_fees( signed_transaction& tx, const fee_schedule& s  )\n   {\n      for( auto& op : tx.operations )\n         s.set_fee(op);\n   }\n\n   operation wallet_api_impl::get_prototype_operation( string operation_name )\n   {\n      auto it = _prototype_ops.find( operation_name );\n      if( it == _prototype_ops.end() )\n         FC_THROW(\"Unsupported operation: \\\"${operation_name}\\\"\", (\"operation_name\", operation_name));\n      return it->second;\n   }\n\n   void wallet_api_impl::init_prototype_ops()\n   {\n      operation op;\n      int64_t op_count = op.count();\n      for( int64_t t=0; t<op_count; t++ )\n      {\n         op.set_which( t );\n         op.visit( op_prototype_visitor(t, _prototype_ops) );\n      }\n      return;\n   }\n\n   int wallet_api_impl::find_first_unused_derived_key_index(const fc::ecc::private_key& parent_key)\n   {\n      int first_unused_index = 0;\n      int number_of_consecutive_unused_keys = 0;\n      for (int key_index = 0; ; ++key_index)\n      {\n         fc::ecc::private_key derived_private_key = derive_private_key(key_to_wif(parent_key), key_index);\n         graphene::chain::public_key_type derived_public_key = derived_private_key.get_public_key();\n         if( _keys.find(derived_public_key) == _keys.end() )\n         {\n            if (number_of_consecutive_unused_keys)\n            {\n               ++number_of_consecutive_unused_keys;\n               if (number_of_consecutive_unused_keys > 5)\n                  return first_unused_index;\n            }\n            else\n            {\n               first_unused_index = key_index;\n               number_of_consecutive_unused_keys = 1;\n            }\n         }\n         else\n         {\n            // key_index is used\n            first_unused_index = 0;\n            number_of_consecutive_unused_keys = 0;\n         }\n      }\n   }\n\n   void wallet_api_impl::enable_umask_protection()\n   {\n#ifdef __unix__\n      _old_umask = umask( S_IRWXG | S_IRWXO );\n#endif\n   }\n\n   void wallet_api_impl::disable_umask_protection()\n   {\n#ifdef __unix__\n      umask( _old_umask );\n#endif\n   }\n\n   bool wallet_api_impl::copy_wallet_file( string destination_filename )\n   {\n      fc::path src_path = get_wallet_filename();\n      if( !fc::exists( src_path ) )\n         return false;\n      fc::path dest_path = destination_filename + _wallet_filename_extension;\n      int suffix = 0;\n      while( fc::exists(dest_path) )\n      {\n         ++suffix;\n         dest_path = destination_filename + \"-\" + to_string( suffix ) + _wallet_filename_extension;\n      }\n      wlog( \"backing up wallet ${src} to ${dest}\",\n            (\"src\", src_path)\n            (\"dest\", dest_path) );\n\n      fc::path dest_parent = fc::absolute(dest_path).parent_path();\n      try\n      {\n         enable_umask_protection();\n         if( !fc::exists( dest_parent ) )\n            fc::create_directories( dest_parent );\n         fc::copy( src_path, dest_path );\n         disable_umask_protection();\n      }\n      catch(...)\n      {\n         disable_umask_protection();\n         throw;\n      }\n      return true;\n   }\n\n   /***\n    * @brief returns true if the wallet is unlocked\n    */\n   bool wallet_api_impl::is_locked()const\n   {\n      return _checksum == fc::sha512();\n   }\n\n   void wallet_api_impl::resync()\n   {\n      fc::scoped_lock<fc::mutex> lock(_resync_mutex);\n      // this method is used to update wallet_data annotations\n      //   e.g. wallet has been restarted and was not notified\n      //   of events while it was down\n      //\n      // everything that is done \"incremental style\" when a push\n      //   notification is received, should also be done here\n      //   \"batch style\" by querying the blockchain\n\n      if( !_wallet.pending_account_registrations.empty() )\n      {\n         // make a vector of the account names pending registration\n         std::vector<string> pending_account_names =\n               boost::copy_range<std::vector<string> >(boost::adaptors::keys(_wallet.pending_account_registrations));\n\n         // look those up on the blockchain\n         std::vector<fc::optional<graphene::chain::account_object >>\n               pending_account_objects = _remote_db->lookup_account_names( pending_account_names );\n\n         // if any of them exist, claim them\n         for( const fc::optional<graphene::chain::account_object>& optional_account : pending_account_objects )\n            if( optional_account )\n               claim_registered_account(*optional_account);\n      }\n\n      if (!_wallet.pending_witness_registrations.empty())\n      {\n         // make a vector of the owner accounts for witnesses pending registration\n         std::vector<string> pending_witness_names =\n               boost::copy_range<std::vector<string> >(boost::adaptors::keys(_wallet.pending_witness_registrations));\n\n         // look up the owners on the blockchain\n         std::vector<fc::optional<graphene::chain::account_object>> owner_account_objects =\n               _remote_db->lookup_account_names(pending_witness_names);\n\n         // if any of them have registered witnesses, claim them\n         for( const fc::optional<graphene::chain::account_object>& optional_account : owner_account_objects )\n            if (optional_account)\n            {\n               std::string account_id = account_id_to_string(optional_account->id);\n               fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(account_id);\n               if (witness_obj)\n                  claim_registered_witness(optional_account->name);\n            }\n      }\n   }\n\n   string wallet_api_impl::get_wallet_filename() const\n   {\n      return _wallet_filename;\n   }\n\n   bool wallet_api_impl::load_wallet_file(string wallet_filename)\n   {\n      // TODO:  Merge imported wallet with existing wallet,\n      //        instead of replacing it\n      if( wallet_filename == \"\" )\n         wallet_filename = _wallet_filename;\n\n      if( ! fc::exists( wallet_filename ) )\n         return false;\n\n      _wallet = fc::json::from_file( wallet_filename ).as< wallet_data >( 2 * GRAPHENE_MAX_NESTED_OBJECTS );\n      if( _wallet.chain_id != _chain_id )\n         FC_THROW( \"Wallet chain ID does not match\",\n            (\"wallet.chain_id\", _wallet.chain_id)\n            (\"chain_id\", _chain_id) );\n\n      size_t account_pagination = 100;\n      vector< std::string > account_ids_to_send;\n      size_t n = _wallet.my_accounts.size();\n      account_ids_to_send.reserve( std::min( account_pagination, n ) );\n      auto it = _wallet.my_accounts.begin();\n\n      for( size_t start=0; start<n; start+=account_pagination )\n      {\n         size_t end = std::min( start+account_pagination, n );\n         assert( end > start );\n         account_ids_to_send.clear();\n         std::vector< account_object > old_accounts;\n         for( size_t i=start; i<end; i++ )\n         {\n            assert( it != _wallet.my_accounts.end() );\n            old_accounts.push_back( *it );\n            std::string account_id = account_id_to_string(old_accounts.back().id);\n            account_ids_to_send.push_back( account_id );\n            ++it;\n         }\n         std::vector< optional< account_object > > accounts = _remote_db->get_accounts(account_ids_to_send, {});\n         // server response should be same length as request\n         FC_ASSERT( accounts.size() == account_ids_to_send.size() );\n         size_t i = 0;\n         for( const optional< account_object >& acct : accounts )\n         {\n            account_object& old_acct = old_accounts[i];\n            if( !acct.valid() )\n            {\n               elog( \"Could not find account ${id} : \\\"${name}\\\" does not exist on the chain!\",\n                     (\"id\", old_acct.id)(\"name\", old_acct.name) );\n               i++;\n               continue;\n            }\n            // this check makes sure the server didn't send results\n            // in a different order, or accounts we didn't request\n            FC_ASSERT( acct->id == old_acct.id );\n            if( fc::json::to_string(*acct) != fc::json::to_string(old_acct) )\n            {\n               wlog( \"Account ${id} : \\\"${name}\\\" updated on chain\", (\"id\", acct->id)(\"name\", acct->name) );\n            }\n            _wallet.update_account( *acct );\n            i++;\n         }\n      }\n\n      return true;\n   }\n\n   void wallet_api_impl::save_wallet_file(string wallet_filename)\n   {\n      //\n      // Serialize in memory, then save to disk\n      //\n      // This approach lessens the risk of a partially written wallet\n      // if exceptions are thrown in serialization\n      //\n\n      encrypt_keys();\n\n      if( wallet_filename == \"\" )\n         wallet_filename = _wallet_filename;\n\n      wlog( \"saving wallet to file ${fn}\", (\"fn\", wallet_filename) );\n\n      string data = fc::json::to_pretty_string( _wallet );\n\n      try\n      {\n         enable_umask_protection();\n         //\n         // Parentheses on the following declaration fails to compile,\n         // due to the Most Vexing Parse.  Thanks, C++\n         //\n         // http://en.wikipedia.org/wiki/Most_vexing_parse\n         //\n         std::string tmp_wallet_filename = wallet_filename + \".tmp\";\n         fc::ofstream outfile{ fc::path( tmp_wallet_filename ) };\n         outfile.write( data.c_str(), data.length() );\n         outfile.flush();\n         outfile.close();\n\n         wlog( \"saved successfully wallet to tmp file ${fn}\", (\"fn\", tmp_wallet_filename) );\n\n         std::string wallet_file_content;\n         fc::read_file_contents(tmp_wallet_filename, wallet_file_content);\n\n         if (wallet_file_content == data) {\n            wlog( \"validated successfully tmp wallet file ${fn}\", (\"fn\", tmp_wallet_filename) );\n\n            fc::rename( tmp_wallet_filename, wallet_filename );\n\n            wlog( \"renamed successfully tmp wallet file ${fn}\", (\"fn\", tmp_wallet_filename) );\n         }\n         else\n         {\n            FC_THROW(\"tmp wallet file cannot be validated ${fn}\", (\"fn\", tmp_wallet_filename) );\n         }\n\n         wlog( \"successfully saved wallet to file ${fn}\", (\"fn\", wallet_filename) );\n\n         disable_umask_protection();\n      }\n      catch(...)\n      {\n         string ws_password = _wallet.ws_password;\n         _wallet.ws_password = \"\";\n         wlog(\"wallet file content is next: ${data}\", (\"data\", fc::json::to_pretty_string( _wallet ) ) );\n         _wallet.ws_password = ws_password;\n\n         disable_umask_protection();\n         throw;\n      }\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_api_impl.hpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/thread/mutex.hpp>\n\n#include <graphene/app/api.hpp>\n\n#include <graphene/wallet/api_documentation.hpp>\n#include <graphene/wallet/wallet_structs.hpp>\n#include <graphene/wallet/reflect_util.hpp>\n\nnamespace graphene { namespace wallet { \n\nclass wallet_api;\n\nnamespace detail {\n\nusing namespace graphene::protocol;\nusing namespace graphene::chain;\nusing namespace graphene::app;\n\nstatic const string ENC_HEADER( \"-----BEGIN BITSHARES SIGNED MESSAGE-----\\n\" );\nstatic const string ENC_META(   \"-----BEGIN META-----\\n\" );\nstatic const string ENC_SIG(    \"-----BEGIN SIGNATURE-----\\n\" );\nstatic const string ENC_FOOTER( \"-----END BITSHARES SIGNED MESSAGE-----\" );\n\ntemplate<class T>\nfc::optional<T> maybe_id( const string& name_or_id )\n{\n   if( std::isdigit( name_or_id.front() ) )\n   {\n      try\n      {\n         return fc::variant(name_or_id, 1).as<T>(1);\n      }\n      catch (const fc::exception&)\n      { // not an ID\n      }\n   }\n   return fc::optional<T>();\n}\n\nstring address_to_shorthash( const graphene::protocol::address& addr );\n\nfc::ecc::private_key derive_private_key( const std::string& prefix_string, int sequence_number );\n\nstring normalize_brain_key( string s );\n\nstruct op_prototype_visitor\n{\n   typedef void result_type;\n\n   int t = 0;\n   flat_map< std::string, graphene::protocol::operation >& name2op;\n\n   op_prototype_visitor(\n      int _t,\n      flat_map< std::string, graphene::protocol::operation >& _prototype_ops\n      ):t(_t), name2op(_prototype_ops) {}\n\n   template<typename Type>\n   result_type operator()( const Type& op )const\n   {\n      string name = fc::get_typename<Type>::name();\n      size_t p = name.rfind(':');\n      if( p != string::npos )\n         name = name.substr( p+1 );\n      name2op[ name ] = Type();\n   }\n};\n\nclass wallet_api_impl\n{\npublic:\n   api_documentation method_documentation;\n   wallet_api& self;\n\n   wallet_api_impl( wallet_api& s, const wallet_data& initial_data, fc::api<login_api> rapi );\n\n   virtual ~wallet_api_impl();\n\n   /***\n    * @brief encrypt the keys\n    * This is normally done before saving the wallet file\n    */\n   void encrypt_keys();\n\n   /***\n    * @brief called when a block is applied\n    */\n   void on_block_applied( const variant& block_id );\n\n   /**\n    * @brief make a copy of the wallet file\n    * Note: this will not overwrite. It simply adds a version suffix.\n    * \n    * @param destination_filename the filename to save it to\n    */\n   bool copy_wallet_file( string destination_filename );\n\n   /***\n    * @brief returns true if the wallet is unlocked\n    */\n   bool is_locked()const;\n\n   template<typename ID>\n   graphene::db::object_downcast_t<ID> get_object(ID id)const\n   {\n      auto ob = _remote_db->get_objects({id}, {}).front();\n      return ob.template as<graphene::db::object_downcast_t<ID>>( GRAPHENE_MAX_NESTED_OBJECTS );\n   }\n\n   /***\n    * @brief set fees for each operation in a transaction\n    * @param tx the transaction\n    * @param s the fee schedule\n    */\n   void set_operation_fees( signed_transaction& tx, const fee_schedule& s  );\n\n   /***\n    * @brief return basic info about the chain\n    */\n   variant info() const;\n\n   /***\n    * @brief return basic information about this program\n    */\n   variant_object about() const;\n\n   chain_property_object get_chain_properties() const;\n   global_property_object get_global_properties() const;\n   dynamic_global_property_object get_dynamic_global_properties() const;\n\n   account_object get_account(account_id_type id) const;\n   account_object get_account(string account_name_or_id) const;\n   account_id_type get_account_id(string account_name_or_id) const;\n\n   std::string asset_id_to_string(asset_id_type id) const;\n   \n   optional<extended_asset_object> find_asset(asset_id_type id)const;\n\n   optional<extended_asset_object> find_asset(string asset_symbol_or_id)const;\n\n   extended_asset_object get_asset(asset_id_type id)const;\n\n   extended_asset_object get_asset(string asset_symbol_or_id)const;\n\n   fc::optional<htlc_object> get_htlc(string htlc_id) const;\n\n   asset_id_type get_asset_id(string asset_symbol_or_id) const;\n\n   string get_wallet_filename() const;\n\n   fc::ecc::private_key get_private_key(const public_key_type& id)const;\n\n   fc::ecc::private_key get_private_key_for_account(const account_object& account)const;\n\n   // imports the private key into the wallet, and associate it in some way (?) with the\n   // given account name.\n   // @returns true if the key matches a current active/owner/memo key for the named\n   //          account, false otherwise (but it is stored either way)\n   bool import_key(string account_name_or_id, string wif_key);\n\n   vector< signed_transaction > import_balance( string name_or_id, const vector<string>& wif_keys, bool broadcast );\n\n   bool load_wallet_file(string wallet_filename = \"\");\n\n   /**\n    * Get the required public keys to sign the transaction which had been\n    * owned by us\n    *\n    * NOTE, if `erase_existing_sigs` set to true, the original trasaction's\n    * signatures will be erased\n    *\n    * @param tx           The transaction to be signed\n    * @param erase_existing_sigs\n    *        The transaction could have been partially signed already,\n    *        if set to false, the corresponding public key of existing\n    *        signatures won't be returned.\n    *        If set to true, the existing signatures will be erased and\n    *        all required keys returned.\n   */\n   set<public_key_type> get_owned_required_keys( signed_transaction &tx,\n         bool erase_existing_sigs = true);\n\n   signed_transaction add_transaction_signature( signed_transaction tx,\n         bool broadcast );\n\n   void quit();\n\n   void save_wallet_file(string wallet_filename = \"\");\n\n   transaction_handle_type begin_builder_transaction();\n   void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op);\n   void replace_operation_in_builder_transaction(transaction_handle_type handle,\n         uint32_t operation_index, const operation& new_op);\n   asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL);\n   transaction preview_builder_transaction(transaction_handle_type handle);\n   signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true);\n   signed_transaction sign_builder_transaction2(transaction_handle_type transaction_handle,\n         const vector<public_key_type>& signing_keys = vector<public_key_type>(), bool broadcast = true);\n\n   pair<transaction_id_type,signed_transaction> broadcast_transaction(signed_transaction tx);\n\n   signed_transaction propose_builder_transaction( transaction_handle_type handle,\n         time_point_sec expiration = time_point::now() + fc::minutes(1),\n         uint32_t review_period_seconds = 0, bool broadcast = true);\n\n   signed_transaction propose_builder_transaction2( transaction_handle_type handle,\n         string account_name_or_id, time_point_sec expiration = time_point::now() + fc::minutes(1),\n         uint32_t review_period_seconds = 0, bool broadcast = true);\n\n   void remove_builder_transaction(transaction_handle_type handle);\n\n   signed_transaction register_account(string name, public_key_type owner, public_key_type active,\n         string  registrar_account, string  referrer_account, uint32_t referrer_percent,\n         bool broadcast = false);\n   \n   signed_transaction upgrade_account(string name, bool broadcast);\n\n   signed_transaction create_account_with_private_key(fc::ecc::private_key owner_privkey,\n         string account_name, string registrar_account, string referrer_account,\n         bool broadcast = false, bool save_wallet = true);\n\n   signed_transaction create_account_with_brain_key(string brain_key, string account_name, string registrar_account,\n         string referrer_account, bool broadcast = false, bool save_wallet = true);\n\n   signed_transaction create_asset(string issuer, string symbol, uint8_t precision, asset_options common,\n         fc::optional<bitasset_options> bitasset_opts, bool broadcast = false);\n\n   signed_transaction update_asset(string symbol, optional<string> new_issuer, asset_options new_options,\n         bool broadcast );\n\n   signed_transaction update_asset_issuer(string symbol, string new_issuer, bool broadcast );\n\n   signed_transaction update_bitasset(string symbol, bitasset_options new_options, bool broadcast );\n\n   signed_transaction update_asset_feed_producers(string symbol, flat_set<string> new_feed_producers,\n         bool broadcast );\n\n   signed_transaction publish_asset_feed(string publishing_account, string symbol, price_feed feed,\n         bool broadcast );\n\n   signed_transaction fund_asset_fee_pool(string from, string symbol, string amount, bool broadcast );\n\n   signed_transaction claim_asset_fee_pool(string symbol, string amount, bool broadcast );\n\n   signed_transaction reserve_asset(string from, string amount, string symbol, bool broadcast );\n\n   signed_transaction global_settle_asset(string symbol, price settle_price, bool broadcast );\n\n   signed_transaction settle_asset(string account_to_settle, string amount_to_settle, string symbol,\n         bool broadcast );\n\n   signed_transaction bid_collateral(string bidder_name, string debt_amount, string debt_symbol,\n         string additional_collateral, bool broadcast );\n\n   signed_transaction whitelist_account(string authorizing_account, string account_to_list,\n         account_whitelist_operation::account_listing new_listing_status, bool broadcast );\n\n   signed_transaction create_committee_member(string owner_account, string url, bool broadcast );\n\n   witness_object get_witness( string owner_account );\n\n   committee_member_object get_committee_member( string owner_account );\n\n   signed_transaction create_witness(string owner_account, string url, bool broadcast );\n\n   signed_transaction update_witness(string witness_name, string url, string block_signing_key,\n         bool broadcast );\n\n   signed_transaction create_worker( string owner_account, time_point_sec work_begin_date,\n         time_point_sec work_end_date, share_type daily_pay, string name, string url,\n         variant worker_settings, bool broadcast );\n\n   signed_transaction update_worker_votes( string account, worker_vote_delta delta, bool broadcast );\n\n   signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol,\n         string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size,\n         const uint32_t claim_period_seconds, const std::string& memo, bool broadcast = false );\n\n   signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector<char>& preimage, \n         bool broadcast );\n\n   signed_transaction htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast);\n\n   signed_transaction account_store_map(string account, string catalog, bool remove,\n         flat_map<string, optional<string>> key_values, bool broadcast);\n\n   vector< vesting_balance_object_with_info > get_vesting_balances( string account_name );\n\n   signed_transaction withdraw_vesting( string witness_name, string amount, string asset_symbol,\n         bool broadcast = false );\n\n   signed_transaction vote_for_committee_member(string voting_account, string committee_member,\n         bool approve, bool broadcast );\n\n   signed_transaction vote_for_witness(string voting_account, string witness, bool approve,\n         bool broadcast );\n\n   signed_transaction set_voting_proxy(string account_to_modify, optional<string> voting_account,\n         bool broadcast );\n\n   signed_transaction set_desired_witness_and_committee_member_count(string account_to_modify,\n         uint16_t desired_number_of_witnesses, uint16_t desired_number_of_committee_members,\n         bool broadcast );\n\n   signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false);\n   signed_transaction sign_transaction2(signed_transaction tx,\n                                        const vector<public_key_type>& signing_keys = vector<public_key_type>(),\n                                        bool broadcast = false);\n\n   flat_set<public_key_type> get_transaction_signers(const signed_transaction &tx) const;\n\n   vector<flat_set<account_id_type>> get_key_references(const vector<public_key_type> &keys) const;\n\n   memo_data sign_memo(string from, string to, string memo);\n\n   string read_memo(const memo_data& md);\n\n   signed_message sign_message(string signer, string message);\n\n   bool verify_message( const string& message, const string& account, int block, const string& time,\n         const compact_signature& sig );\n\n   bool verify_signed_message( const signed_message& message );\n\n   bool verify_encapsulated_message( const string& message );\n\n   signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell,\n         string min_to_receive, string symbol_to_receive, uint32_t timeout_sec = 0,\n         bool fill_or_kill = false, bool broadcast = false);\n\n   signed_transaction borrow_asset(string seller_name, string amount_to_borrow, string asset_symbol,\n         string amount_of_collateral, bool broadcast = false);\n\n   signed_transaction borrow_asset_ext( string seller_name, string amount_to_borrow, string asset_symbol,\n         string amount_of_collateral, call_order_update_operation::extensions_type extensions,\n         bool broadcast = false);\n\n   signed_transaction cancel_order(limit_order_id_type order_id, bool broadcast = false);\n\n   signed_transaction transfer(string from, string to, string amount,\n         string asset_symbol, string memo, bool broadcast = false);\n\n   signed_transaction issue_asset(string to_account, string amount, string symbol,\n         string memo, bool broadcast = false);\n\n   std::map<string,std::function<string(fc::variant,const fc::variants&)>> get_result_formatters() const;\n\n   signed_transaction propose_parameter_change( const string& proposing_account, fc::time_point_sec expiration_time,\n         const variant_object& changed_values, bool broadcast = false);\n\n   signed_transaction propose_fee_change( const string& proposing_account, fc::time_point_sec expiration_time,\n         const variant_object& changed_fees, bool broadcast = false);\n\n   signed_transaction approve_proposal( const string& fee_paying_account, const string& proposal_id,\n         const approval_delta& delta, bool broadcast = false);\n\n   void dbg_make_uia(string creator, string symbol);\n\n   void dbg_make_mia(string creator, string symbol);\n\n   void dbg_push_blocks( const std::string& src_filename, uint32_t count );\n\n   void dbg_generate_blocks( const std::string& debug_wif_key, uint32_t count );\n\n   void dbg_stream_json_objects( const std::string& filename );\n\n   void dbg_update_object( const fc::variant_object& update );\n\n   void use_network_node_api();\n\n   void use_debug_api();\n\n   void network_add_nodes( const vector<string>& nodes );\n\n   vector< variant > network_get_connected_peers();\n\n   void flood_network(string prefix, uint32_t number_of_transactions);\n\n   operation get_prototype_operation( string operation_name );\n\n   string                  _wallet_filename;\n   wallet_data             _wallet;\n\n   map<public_key_type,string> _keys;\n   fc::sha512                  _checksum;\n\n   chain_id_type           _chain_id;\n   fc::api<login_api>      _remote_api;\n   fc::api<database_api>   _remote_db;\n   fc::api<network_broadcast_api>   _remote_net_broadcast;\n   fc::api<history_api>    _remote_hist;\n   fc::api<custom_operations_api>    _custom_operations;\n   optional< fc::api<network_node_api> > _remote_net_node;\n   optional< fc::api<graphene::debug_witness::debug_api> > _remote_debug;\n\n   flat_map<string, operation> _prototype_ops;\n\n   static_variant_map _operation_which_map = create_static_variant_map< operation >();\n\nprivate:\n   std::string account_id_to_string(account_id_type id) const;\n\n   static htlc_hash do_hash( const string& algorithm, const std::string& hash );\n\n   void enable_umask_protection();\n\n   void disable_umask_protection();\n\n   // This function generates derived keys starting with index 0 and keeps incrementing\n   // the index until it finds a key that isn't registered in the block chain.  To be\n   // safer, it continues checking for a few more keys to make sure there wasn't a short gap\n   // caused by a failed registration or the like.\n   int find_first_unused_derived_key_index(const fc::ecc::private_key& parent_key);\n\n   void claim_registered_account(const graphene::chain::account_object& account);\n\n   // after a witness registration succeeds, this saves the private key in the wallet permanently\n   //\n   void claim_registered_witness(const std::string& witness_name);\n\n   fc::mutex _resync_mutex;\n   void resync();\n\n   void init_prototype_ops();\n\n   map<transaction_handle_type, signed_transaction> _builder_transactions;\n\n   // if the user executes the same command twice in quick succession,\n   // we might generate the same transaction id, and cause the second\n   // transaction to be rejected.  This can be avoided by altering the\n   // second transaction slightly (bumping up the expiration time by\n   // a second).  Keep track of recent transaction ids we've generated\n   // so we can know if we need to do this\n   struct recently_generated_transaction_record\n   {\n      fc::time_point_sec generation_time;\n      graphene::chain::transaction_id_type transaction_id;\n   };\n   struct timestamp_index{};\n   typedef boost::multi_index_container<\n         recently_generated_transaction_record,\n         boost::multi_index::indexed_by<\n            boost::multi_index::hashed_unique<\n               boost::multi_index::member<\n                  recently_generated_transaction_record,\n                  graphene::chain::transaction_id_type,\n                  &recently_generated_transaction_record::transaction_id\n               >,\n               std::hash<graphene::chain::transaction_id_type>\n            >,\n            boost::multi_index::ordered_non_unique<\n               boost::multi_index::tag<timestamp_index>,\n               boost::multi_index::member<\n                  recently_generated_transaction_record,\n                  fc::time_point_sec,\n                  &recently_generated_transaction_record::generation_time\n            >\n         >\n       >\n     > recently_generated_transaction_set_type;\n   recently_generated_transaction_set_type _recently_generated_transactions;\n\n#ifdef __unix__\n   mode_t                  _old_umask;\n#endif\n   const string _wallet_filename_extension = \".wallet\";\n};\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_asset.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"wallet_api_impl.hpp\"\n#include <graphene/wallet/wallet.hpp>\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   std::string wallet_api_impl::asset_id_to_string(asset_id_type id) const\n   {\n      std::string asset_id = fc::to_string(id.space_id) +\n                             \".\" + fc::to_string(id.type_id) +\n                             \".\" + fc::to_string(id.instance.value);\n      return asset_id;\n   }\n\n   optional<extended_asset_object> wallet_api_impl::find_asset(asset_id_type id)const\n   {\n      auto rec = _remote_db->get_assets({asset_id_to_string(id)}, {}).front();\n      return rec;\n   }\n\n   optional<extended_asset_object> wallet_api_impl::find_asset(string asset_symbol_or_id)const\n   {\n      FC_ASSERT( asset_symbol_or_id.size() > 0 );\n\n      if( auto id = maybe_id<asset_id_type>(asset_symbol_or_id) )\n      {\n         // It's an ID\n         return find_asset(*id);\n      } else {\n         // It's a symbol\n         auto rec = _remote_db->lookup_asset_symbols({asset_symbol_or_id}).front();\n         if( rec )\n         {\n            if( rec->symbol != asset_symbol_or_id )\n               return optional<asset_object>();\n         }\n         return rec;\n      }\n   }\n\n   extended_asset_object wallet_api_impl::get_asset(asset_id_type id)const\n   {\n      auto opt = find_asset(id);\n      FC_ASSERT(opt);\n      return *opt;\n   }\n\n   extended_asset_object wallet_api_impl::get_asset(string asset_symbol_or_id)const\n   {\n      auto opt = find_asset(asset_symbol_or_id);\n      FC_ASSERT(opt);\n      return *opt;\n   }\n\n   asset_id_type wallet_api_impl::get_asset_id(string asset_symbol_or_id) const\n   {\n      FC_ASSERT( asset_symbol_or_id.size() > 0 );\n      vector<optional<extended_asset_object>> opt_asset;\n      if( std::isdigit( asset_symbol_or_id.front() ) )\n         return fc::variant(asset_symbol_or_id, 1).as<asset_id_type>( 1 );\n      opt_asset = _remote_db->lookup_asset_symbols( {asset_symbol_or_id} );\n      FC_ASSERT( (opt_asset.size() > 0) && (opt_asset[0].valid()) );\n      return opt_asset[0]->id;\n   }\n\n   signed_transaction wallet_api_impl::create_asset(string issuer, string symbol,\n         uint8_t precision, asset_options common, fc::optional<bitasset_options> bitasset_opts,\n         bool broadcast )\n   { try {\n      account_object issuer_account = get_account( issuer );\n      FC_ASSERT(!find_asset(symbol).valid(), \"Asset with that symbol already exists!\");\n\n      asset_create_operation create_op;\n      create_op.issuer = issuer_account.id;\n      create_op.symbol = symbol;\n      create_op.precision = precision;\n      create_op.common_options = common;\n      create_op.bitasset_opts = bitasset_opts;\n\n      signed_transaction tx;\n      tx.operations.push_back( create_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::update_asset(string symbol, optional<string> new_issuer,\n         asset_options new_options, bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_update = find_asset(symbol);\n      if (!asset_to_update)\n         FC_THROW(\"No asset with that symbol exists!\");\n      optional<account_id_type> new_issuer_account_id;\n      if (new_issuer)\n      {\n         FC_THROW( \"The use of 'new_issuer' is no longer supported. Please use `update_asset_issuer' instead!\" );\n      }\n\n      asset_update_operation update_op;\n      update_op.issuer = asset_to_update->issuer;\n      update_op.asset_to_update = asset_to_update->id;\n      update_op.new_issuer = new_issuer_account_id;\n      update_op.new_options = new_options;\n\n      signed_transaction tx;\n      tx.operations.push_back( update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (symbol)(new_issuer)(new_options)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::update_asset_issuer(string symbol, string new_issuer,\n         bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_update = find_asset(symbol);\n      if (!asset_to_update)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      account_object new_issuer_account = get_account(new_issuer);\n\n      asset_update_issuer_operation update_issuer;\n      update_issuer.issuer = asset_to_update->issuer;\n      update_issuer.asset_to_update = asset_to_update->id;\n      update_issuer.new_issuer = new_issuer_account.id;\n\n      signed_transaction tx;\n      tx.operations.push_back( update_issuer );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (symbol)(new_issuer)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::update_bitasset(string symbol, bitasset_options new_options,\n         bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_update = find_asset(symbol);\n      if (!asset_to_update)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      asset_update_bitasset_operation update_op;\n      update_op.issuer = asset_to_update->issuer;\n      update_op.asset_to_update = asset_to_update->id;\n      update_op.new_options = new_options;\n\n      signed_transaction tx;\n      tx.operations.push_back( update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (symbol)(new_options)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::update_asset_feed_producers(string symbol, \n         flat_set<string> new_feed_producers, bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_update = find_asset(symbol);\n      if (!asset_to_update)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      asset_update_feed_producers_operation update_op;\n      update_op.issuer = asset_to_update->issuer;\n      update_op.asset_to_update = asset_to_update->id;\n      update_op.new_feed_producers.reserve(new_feed_producers.size());\n      std::transform(new_feed_producers.begin(), new_feed_producers.end(),\n                     std::inserter(update_op.new_feed_producers, update_op.new_feed_producers.end()),\n                     [this](const std::string& account_name_or_id){ return get_account_id(account_name_or_id); });\n\n      signed_transaction tx;\n      tx.operations.push_back( update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (symbol)(new_feed_producers)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::publish_asset_feed(string publishing_account, string symbol,\n         price_feed feed, bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_update = find_asset(symbol);\n      if (!asset_to_update)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      asset_publish_feed_operation publish_op;\n      publish_op.publisher = get_account_id(publishing_account);\n      publish_op.asset_id = asset_to_update->id;\n      publish_op.feed = feed;\n\n      signed_transaction tx;\n      tx.operations.push_back( publish_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::fund_asset_fee_pool(string from, string symbol, string amount,\n         bool broadcast /* = false */)\n   { try {\n      account_object from_account = get_account(from);\n      optional<asset_object> asset_to_fund = find_asset(symbol);\n      if (!asset_to_fund)\n        FC_THROW(\"No asset with that symbol exists!\");\n      asset_object core_asset = get_asset(asset_id_type());\n\n      asset_fund_fee_pool_operation fund_op;\n      fund_op.from_account = from_account.id;\n      fund_op.asset_id = asset_to_fund->id;\n      fund_op.amount = core_asset.amount_from_string(amount).amount;\n\n      signed_transaction tx;\n      tx.operations.push_back( fund_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::claim_asset_fee_pool(string symbol, string amount,\n         bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_pool_to_claim = find_asset(symbol);\n      if (!asset_pool_to_claim)\n        FC_THROW(\"No asset with that symbol exists!\");\n      asset_object core_asset = get_asset(asset_id_type());\n\n      asset_claim_pool_operation claim_op;\n      claim_op.issuer = asset_pool_to_claim->issuer;\n      claim_op.asset_id = asset_pool_to_claim->id;\n      claim_op.amount_to_claim = core_asset.amount_from_string(amount).amount;\n\n      signed_transaction tx;\n      tx.operations.push_back( claim_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (symbol)(amount)(broadcast) ) }\n\n\n   signed_transaction wallet_api_impl::reserve_asset(string from, string amount, string symbol,\n         bool broadcast /* = false */)\n   { try {\n      account_object from_account = get_account(from);\n      optional<asset_object> asset_to_reserve = find_asset(symbol);\n      if (!asset_to_reserve)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      asset_reserve_operation reserve_op;\n      reserve_op.payer = from_account.id;\n      reserve_op.amount_to_reserve = asset_to_reserve->amount_from_string(amount);\n\n      signed_transaction tx;\n      tx.operations.push_back( reserve_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::global_settle_asset(string symbol, price settle_price,\n         bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_settle = find_asset(symbol);\n      if (!asset_to_settle)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      asset_global_settle_operation settle_op;\n      settle_op.issuer = asset_to_settle->issuer;\n      settle_op.asset_to_settle = asset_to_settle->id;\n      settle_op.settle_price = settle_price;\n\n      signed_transaction tx;\n      tx.operations.push_back( settle_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (symbol)(settle_price)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::settle_asset(string account_to_settle, string amount_to_settle,\n         string symbol, bool broadcast /* = false */)\n   { try {\n      optional<asset_object> asset_to_settle = find_asset(symbol);\n      if (!asset_to_settle)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      asset_settle_operation settle_op;\n      settle_op.account = get_account_id(account_to_settle);\n      settle_op.amount = asset_to_settle->amount_from_string(amount_to_settle);\n\n      signed_transaction tx;\n      tx.operations.push_back( settle_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::issue_asset(string to_account, string amount, string symbol,\n         string memo, bool broadcast )\n   {\n      auto asset_obj = get_asset(symbol);\n\n      account_object to = get_account(to_account);\n      account_object issuer = get_account(asset_obj.issuer);\n\n      asset_issue_operation issue_op;\n      issue_op.issuer           = asset_obj.issuer;\n      issue_op.asset_to_issue   = asset_obj.amount_from_string(amount);\n      issue_op.issue_to_account = to.id;\n\n      if( memo.size() )\n      {\n         issue_op.memo = memo_data();\n         issue_op.memo->from = issuer.options.memo_key;\n         issue_op.memo->to = to.options.memo_key;\n         issue_op.memo->set_message(get_private_key(issuer.options.memo_key),\n                                    to.options.memo_key, memo);\n      }\n\n      signed_transaction tx;\n      tx.operations.push_back(issue_op);\n      set_operation_fees(tx,_remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction(tx, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::bid_collateral(string bidder_name, string debt_amount, string debt_symbol,\n         string additional_collateral, bool broadcast )\n   { try {\n      optional<asset_object> debt_asset = find_asset(debt_symbol);\n      if (!debt_asset)\n        FC_THROW(\"No asset with that symbol exists!\");\n\n      FC_ASSERT(debt_asset->bitasset_data_id.valid(), \"Not a bitasset, bidding not possible.\");\n      const asset_object& collateral =\n            get_asset(get_object(*debt_asset->bitasset_data_id).options.short_backing_asset);\n\n      bid_collateral_operation op;\n      op.bidder = get_account_id(bidder_name);\n      op.debt_covered = debt_asset->amount_from_string(debt_amount);\n      op.additional_collateral = collateral.amount_from_string(additional_collateral);\n\n      signed_transaction tx;\n      tx.operations.push_back( op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (bidder_name)(debt_amount)(debt_symbol)(additional_collateral)(broadcast) ) }\n\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_builder.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"wallet_api_impl.hpp\"\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   transaction_handle_type wallet_api_impl::begin_builder_transaction()\n   {\n      int trx_handle = _builder_transactions.empty()? 0\n                                                    : (--_builder_transactions.end())->first + 1;\n      _builder_transactions[trx_handle];\n      return trx_handle;\n   }\n\n   void wallet_api_impl::add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op)\n   {\n      FC_ASSERT(_builder_transactions.count(transaction_handle));\n      _builder_transactions[transaction_handle].operations.emplace_back(op);\n   }\n\n   void wallet_api_impl::replace_operation_in_builder_transaction(transaction_handle_type handle,\n         uint32_t operation_index, const operation& new_op)\n   {\n      FC_ASSERT(_builder_transactions.count(handle));\n      signed_transaction& trx = _builder_transactions[handle];\n      FC_ASSERT( operation_index < trx.operations.size());\n      trx.operations[operation_index] = new_op;\n   }\n\n   asset wallet_api_impl::set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset)\n   {\n      FC_ASSERT(_builder_transactions.count(handle));\n\n      auto fee_asset_obj = get_asset(fee_asset);\n      asset total_fee = fee_asset_obj.amount(0);\n\n      auto gprops = _remote_db->get_global_properties().parameters;\n      if( fee_asset_obj.get_id() != asset_id_type() )\n      {\n         for( auto& op : _builder_transactions[handle].operations )\n            total_fee += gprops.get_current_fees().set_fee( op, fee_asset_obj.options.core_exchange_rate );\n\n         FC_ASSERT((total_fee * fee_asset_obj.options.core_exchange_rate).amount <=\n                   get_object(fee_asset_obj.dynamic_asset_data_id).fee_pool,\n                   \"Cannot pay fees in ${asset}, as this asset's fee pool is insufficiently funded.\",\n                   (\"asset\", fee_asset_obj.symbol));\n      } else {\n         for( auto& op : _builder_transactions[handle].operations )\n            total_fee += gprops.get_current_fees().set_fee( op );\n      }\n\n      return total_fee;\n   }\n\n   transaction wallet_api_impl::preview_builder_transaction(transaction_handle_type handle)\n   {\n      FC_ASSERT(_builder_transactions.count(handle));\n      return _builder_transactions[handle];\n   }\n\n   signed_transaction wallet_api_impl::sign_builder_transaction(transaction_handle_type \n         transaction_handle, bool broadcast )\n   {\n      FC_ASSERT(_builder_transactions.count(transaction_handle));\n\n      return _builder_transactions[transaction_handle] =\n            sign_transaction(_builder_transactions[transaction_handle], broadcast);\n   }\n\n   signed_transaction wallet_api_impl::sign_builder_transaction2(transaction_handle_type\n         transaction_handle, const vector<public_key_type>& signing_keys, bool broadcast)\n   {\n      FC_ASSERT(_builder_transactions.count(transaction_handle));\n\n      return _builder_transactions[transaction_handle] =\n            sign_transaction2(_builder_transactions[transaction_handle], signing_keys, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::propose_builder_transaction( transaction_handle_type handle,\n         time_point_sec expiration, uint32_t review_period_seconds, bool broadcast)\n   {\n      FC_ASSERT(_builder_transactions.count(handle));\n      proposal_create_operation op;\n      op.expiration_time = expiration;\n      signed_transaction& trx = _builder_transactions[handle];\n      std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops),\n                     [](const operation& op) -> op_wrapper { return op; });\n      if( review_period_seconds )\n         op.review_period_seconds = review_period_seconds;\n      trx.operations = {op};\n      _remote_db->get_global_properties().parameters.get_current_fees().set_fee( trx.operations.front() );\n\n      return trx = sign_transaction(trx, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::propose_builder_transaction2( transaction_handle_type handle,\n      string account_name_or_id, time_point_sec expiration, uint32_t review_period_seconds, bool broadcast )\n   {\n      FC_ASSERT(_builder_transactions.count(handle));\n      proposal_create_operation op;\n      op.fee_paying_account = get_account(account_name_or_id).get_id();\n      op.expiration_time = expiration;\n      signed_transaction& trx = _builder_transactions[handle];\n      std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops),\n                     [](const operation& op) -> op_wrapper { return op; });\n      if( review_period_seconds )\n         op.review_period_seconds = review_period_seconds;\n      trx.operations = {op};\n      _remote_db->get_global_properties().parameters.get_current_fees().set_fee( trx.operations.front() );\n\n      return trx = sign_transaction(trx, broadcast);\n   }\n\n   void wallet_api_impl::remove_builder_transaction(transaction_handle_type handle)\n   {\n      _builder_transactions.erase(handle);\n   }\n\n}}} // namespace graphene::wallet::detail1\n"
  },
  {
    "path": "libraries/wallet/wallet_debug.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"wallet_api_impl.hpp\"\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   void wallet_api_impl::dbg_make_uia(string creator, string symbol)\n   {\n      asset_options opts;\n      opts.flags &= ~(white_list | disable_force_settle | global_settle);\n      opts.issuer_permissions = opts.flags;\n      opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1)));\n      create_asset(get_account(creator).name, symbol, 2, opts, {}, true);\n   }\n\n   void wallet_api_impl::dbg_make_mia(string creator, string symbol)\n   {\n      asset_options opts;\n      opts.flags &= ~white_list;\n      opts.issuer_permissions = opts.flags;\n      opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1)));\n      bitasset_options bopts;\n      create_asset(get_account(creator).name, symbol, 2, opts, bopts, true);\n   }\n\n   void wallet_api_impl::dbg_push_blocks( const std::string& src_filename, uint32_t count )\n   {\n      use_debug_api();\n      (*_remote_debug)->debug_push_blocks( src_filename, count );\n      (*_remote_debug)->debug_stream_json_objects_flush();\n   }\n\n   void wallet_api_impl::dbg_generate_blocks( const std::string& debug_wif_key, uint32_t count )\n   {\n      use_debug_api();\n      (*_remote_debug)->debug_generate_blocks( debug_wif_key, count );\n      (*_remote_debug)->debug_stream_json_objects_flush();\n   }\n\n   void wallet_api_impl::dbg_stream_json_objects( const std::string& filename )\n   {\n      use_debug_api();\n      (*_remote_debug)->debug_stream_json_objects( filename );\n      (*_remote_debug)->debug_stream_json_objects_flush();\n   }\n\n   void wallet_api_impl::dbg_update_object( const fc::variant_object& update )\n   {\n      use_debug_api();\n      (*_remote_debug)->debug_update_object( update );\n      (*_remote_debug)->debug_stream_json_objects_flush();\n   }\n\n   void wallet_api_impl::use_debug_api()\n   {\n      if( _remote_debug )\n         return;\n      try\n      {\n        _remote_debug = _remote_api->debug();\n      }\n      catch( const fc::exception& e )\n      {\n         std::cerr << \"\\nCouldn't get debug node API.  You probably are not configured\\n\"\n         \"to access the debug API on the node you are connecting to.\\n\"\n         \"\\n\"\n         \"To fix this problem:\\n\"\n         \"- Please ensure you are running debug_node, not witness_node.\\n\"\n         \"- Please follow the instructions in README.md to set up an apiaccess file.\\n\"\n         \"\\n\";\n      }\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_network.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"wallet_api_impl.hpp\"\n\n/****\n * Methods to handle network / debug / debug_node\n */\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   void wallet_api_impl::network_add_nodes( const vector<string>& nodes )\n   {\n      use_network_node_api();\n      for( const string& node_address : nodes )\n      {\n         (*_remote_net_node)->add_node( fc::ip::endpoint::from_string( node_address ) );\n      }\n   }\n\n   vector< variant > wallet_api_impl::network_get_connected_peers()\n   {\n      use_network_node_api();\n      const auto peers = (*_remote_net_node)->get_connected_peers();\n      vector< variant > result;\n      result.reserve( peers.size() );\n      for( const auto& peer : peers )\n      {\n         variant v;\n         fc::to_variant( peer, v, GRAPHENE_MAX_NESTED_OBJECTS );\n         result.push_back( v );\n      }\n      return result;\n   }\n\n   void wallet_api_impl::flood_network(string prefix, uint32_t number_of_transactions)\n   {\n      try\n      {\n         const account_object& master = *_wallet.my_accounts.get<by_name>().lower_bound(\"import\");\n         int number_of_accounts = number_of_transactions / 3;\n         number_of_transactions -= number_of_accounts;\n         try {\n            dbg_make_uia(master.name, \"SHILL\");\n         } catch(...) {/* Ignore; the asset probably already exists.*/}\n\n         fc::time_point start = fc::time_point::now();\n         for( int i = 0; i < number_of_accounts; ++i )\n         {\n            std::ostringstream brain_key;\n            brain_key << \"brain key for account \" << prefix << i;\n            signed_transaction trx = create_account_with_brain_key(\n                  brain_key.str(), prefix + fc::to_string(i), master.name, master.name,\n                  /* broadcast = */ true, /* save wallet = */ false);\n         }\n         fc::time_point end = fc::time_point::now();\n         ilog(\"Created ${n} accounts in ${time} milliseconds\",\n              (\"n\", number_of_accounts)(\"time\", (end - start).count() / 1000));\n\n         start = fc::time_point::now();\n         for( int i = 0; i < number_of_accounts; ++i )\n         {\n            signed_transaction trx = transfer(master.name, prefix + fc::to_string(i), \"10\", \"CORE\", \"\", true);\n            trx = transfer(master.name, prefix + fc::to_string(i), \"1\", \"CORE\", \"\", true);\n         }\n         end = fc::time_point::now();\n         ilog(\"Transferred to ${n} accounts in ${time} milliseconds\",\n              (\"n\", number_of_accounts*2)(\"time\", (end - start).count() / 1000));\n\n         start = fc::time_point::now();\n         for( int i = 0; i < number_of_accounts; ++i )\n         {\n            signed_transaction trx = issue_asset(prefix + fc::to_string(i), \"1000\", \"SHILL\", \"\", true);\n         }\n         end = fc::time_point::now();\n         ilog(\"Issued to ${n} accounts in ${time} milliseconds\",\n              (\"n\", number_of_accounts)(\"time\", (end - start).count() / 1000));\n      }\n      catch (...)\n      {\n         throw;\n      }\n\n   }\n\n   pair<transaction_id_type,signed_transaction> wallet_api_impl::broadcast_transaction(signed_transaction tx)\n   {\n       try {\n           _remote_net_broadcast->broadcast_transaction(tx);\n       }\n       catch (const fc::exception& e) {\n           elog(\"Caught exception while broadcasting tx ${id}:  ${e}\",\n                (\"id\", tx.id().str())(\"e\", e.to_detail_string()));\n           throw;\n       }\n       return std::make_pair(tx.id(),tx);\n   }   \n\n   void wallet_api_impl::use_network_node_api()\n   {\n      if( _remote_net_node )\n         return;\n      try\n      {\n         _remote_net_node = _remote_api->network_node();\n      }\n      catch( const fc::exception& e )\n      {\n         std::cerr << \"\\nCouldn't get network node API.  You probably are not configured\\n\"\n         \"to access the network API on the witness_node you are\\n\"\n         \"connecting to.  Please follow the instructions in README.md to set up an apiaccess file.\\n\"\n         \"\\n\";\n         throw;\n      }\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_results.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <fc/io/sstream.hpp>\n#include \"wallet_api_impl.hpp\"\n#include \"operation_printer.hpp\"\n\nnamespace graphene { namespace wallet { namespace detail {\n\nstd::map<string,std::function<string(fc::variant,const fc::variants&)>> wallet_api_impl::get_result_formatters() const\n   {\n      std::map<string,std::function<string(fc::variant,const fc::variants&)> > m;\n      m[\"help\"] = [](variant result, const fc::variants& a)\n      {\n         return result.get_string();\n      };\n\n      m[\"gethelp\"] = [](variant result, const fc::variants& a)\n      {\n         return result.get_string();\n      };\n\n      m[\"get_account_history\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<vector<operation_detail>>( GRAPHENE_MAX_NESTED_OBJECTS );\n         std::stringstream ss;\n\n         for( operation_detail& d : r )\n         {\n            operation_history_object& i = d.op;\n            auto b = _remote_db->get_block_header(i.block_num);\n            FC_ASSERT(b);\n            ss << i.block_num << \" \";\n            ss << b->timestamp.to_iso_string() << \" \";\n            i.op.visit(operation_printer(ss, *this, i));\n            ss << \" \\n\";\n         }\n\n         return ss.str();\n      };\n      m[\"get_relative_account_history\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<vector<operation_detail>>( GRAPHENE_MAX_NESTED_OBJECTS );\n         std::stringstream ss;\n\n         for( operation_detail& d : r )\n         {\n            operation_history_object& i = d.op;\n            auto b = _remote_db->get_block_header(i.block_num);\n            FC_ASSERT(b);\n            ss << i.block_num << \" \";\n            ss << b->timestamp.to_iso_string() << \" \";\n            i.op.visit(operation_printer(ss, *this, i));\n            ss << \" \\n\";\n         }\n\n         return ss.str();\n      };\n\n      m[\"get_account_history_by_operations\"] = [this](variant result, const fc::variants& a) {\n          auto r = result.as<account_history_operation_detail>( GRAPHENE_MAX_NESTED_OBJECTS );\n          std::stringstream ss;\n          ss << \"total_count : \";\n          ss << r.total_count;\n          ss << \" \\n\";\n          ss << \"result_count : \";\n          ss << r.result_count;\n          ss << \" \\n\";\n          for (operation_detail_ex& d : r.details) {\n              operation_history_object& i = d.op;\n              auto b = _remote_db->get_block_header(i.block_num);\n              FC_ASSERT(b);\n              ss << i.block_num << \" \";\n              ss << b->timestamp.to_iso_string() << \" \";\n              i.op.visit(operation_printer(ss, *this, i));\n              ss << \" transaction_id : \";\n              ss << d.transaction_id.str();\n              ss << \" \\n\";\n          }\n\n          return ss.str();\n      };\n\n      m[\"list_account_balances\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<vector<asset>>( GRAPHENE_MAX_NESTED_OBJECTS );\n         vector<asset_object> asset_recs;\n         std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) {\n            return get_asset(a.asset_id);\n         });\n\n         std::stringstream ss;\n         for( unsigned i = 0; i < asset_recs.size(); ++i )\n            ss << asset_recs[i].amount_to_pretty_string(r[i]) << \"\\n\";\n\n         return ss.str();\n      };\n\n      m[\"get_blind_balances\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<vector<asset>>( GRAPHENE_MAX_NESTED_OBJECTS );\n         vector<asset_object> asset_recs;\n         std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) {\n            return get_asset(a.asset_id);\n         });\n\n         std::stringstream ss;\n         for( unsigned i = 0; i < asset_recs.size(); ++i )\n            ss << asset_recs[i].amount_to_pretty_string(r[i]) << \"\\n\";\n\n         return ss.str();\n      };\n      m[\"transfer_to_blind\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<blind_confirmation>( GRAPHENE_MAX_NESTED_OBJECTS );\n         std::stringstream ss;\n         r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) );\n         ss << \"\\n\";\n         for( const auto& out : r.outputs )\n         {\n            asset_object a = get_asset( out.decrypted_memo.amount.asset_id );\n            ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << \" to  \" << out.label\n               << \"\\n\\t  receipt: \" << out.confirmation_receipt << \"\\n\\n\";\n         }\n         return ss.str();\n      };\n      m[\"blind_transfer\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<blind_confirmation>( GRAPHENE_MAX_NESTED_OBJECTS );\n         std::stringstream ss;\n         r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) );\n         ss << \"\\n\";\n         for( const auto& out : r.outputs )\n         {\n            asset_object a = get_asset( out.decrypted_memo.amount.asset_id );\n            ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << \" to  \" << out.label\n               << \"\\n\\t  receipt: \" << out.confirmation_receipt << \"\\n\\n\";\n         }\n         return ss.str();\n      };\n      m[\"receive_blind_transfer\"] = [this](variant result, const fc::variants& a)\n      {\n         auto r = result.as<blind_receipt>( GRAPHENE_MAX_NESTED_OBJECTS );\n         std::stringstream ss;\n         asset_object as = get_asset( r.amount.asset_id );\n         ss << as.amount_to_pretty_string( r.amount ) << \"  \" << r.from_label << \"  =>  \"\n            << r.to_label  << \"  \" << r.memo <<\"\\n\";\n         return ss.str();\n      };\n      m[\"blind_history\"] = [this](variant result, const fc::variants& a)\n      {\n         auto records = result.as<vector<blind_receipt>>( GRAPHENE_MAX_NESTED_OBJECTS );\n         std::stringstream ss;\n         ss << \"WHEN         \"\n            << \"  \" << \"AMOUNT\"  << \"  \" << \"FROM\" << \"  =>  \" << \"TO\" << \"  \" << \"MEMO\" <<\"\\n\";\n         ss << \"====================================================================================\\n\";\n         for( auto& r : records )\n         {\n            asset_object as = get_asset( r.amount.asset_id );\n            ss << fc::get_approximate_relative_time_string( r.date )\n               << \"  \" << as.amount_to_pretty_string( r.amount ) << \"  \" << r.from_label << \"  =>  \" << r.to_label\n               << \"  \" << r.memo <<\"\\n\";\n         }\n         return ss.str();\n      };\n      m[\"get_order_book\"] = [](variant result, const fc::variants& a)\n      {\n         auto orders = result.as<order_book>( GRAPHENE_MAX_NESTED_OBJECTS );\n         auto bids = orders.bids;\n         auto asks = orders.asks;\n         std::stringstream ss;\n         std::stringstream sum_stream;\n         sum_stream << \"Sum(\" << orders.base << ')';\n         double bid_sum = 0;\n         double ask_sum = 0;\n         const int spacing = 20;\n\n         auto prettify_num = [&ss]( double n )\n         {\n            if (abs( round( n ) - n ) < 0.00000000001 )\n            {\n               ss << (int) n;\n            }\n            else if (n - floor(n) < 0.000001)\n            {\n               ss << setiosflags( ios::fixed ) << setprecision(10) << n;\n            }\n            else\n            {\n               ss << setiosflags( ios::fixed ) << setprecision(6) << n;\n            }\n         };\n         auto prettify_num_string = [&]( string& num_string )\n         {\n            double n = fc::to_double( num_string );\n            prettify_num( n );\n         };\n\n         ss << setprecision( 8 ) << setiosflags( ios::fixed ) << setiosflags( ios::left );\n\n         ss << ' ' << setw( (spacing * 4) + 6 ) << \"BUY ORDERS\" << \"SELL ORDERS\\n\"\n            << ' ' << setw( spacing + 1 ) << \"Price\" << setw( spacing ) << orders.quote << ' ' << setw( spacing )\n            << orders.base << ' ' << setw( spacing ) << sum_stream.str()\n            << \"   \" << setw( spacing + 1 ) << \"Price\" << setw( spacing ) << orders.quote << ' ' << setw( spacing )\n            << orders.base << ' ' << setw( spacing ) << sum_stream.str()\n            << \"\\n=====================================================================================\"\n            << \"|=====================================================================================\\n\";\n\n         for (unsigned int i = 0; i < bids.size() || i < asks.size() ; i++)\n         {\n            if ( i < bids.size() )\n            {\n                bid_sum += fc::to_double( bids[i].base );\n                ss << ' ' << setw( spacing );\n                prettify_num_string( bids[i].price );\n                ss << ' ' << setw( spacing );\n                prettify_num_string( bids[i].quote );\n                ss << ' ' << setw( spacing );\n                prettify_num_string( bids[i].base );\n                ss << ' ' << setw( spacing );\n                prettify_num( bid_sum );\n                ss << ' ';\n            }\n            else\n            {\n                ss << setw( (spacing * 4) + 5 ) << ' ';\n            }\n\n            ss << '|';\n\n            if ( i < asks.size() )\n            {\n               ask_sum += fc::to_double( asks[i].base );\n               ss << ' ' << setw( spacing );\n               prettify_num_string( asks[i].price );\n               ss << ' ' << setw( spacing );\n               prettify_num_string( asks[i].quote );\n               ss << ' ' << setw( spacing );\n               prettify_num_string( asks[i].base );\n               ss << ' ' << setw( spacing );\n               prettify_num( ask_sum );\n            }\n\n            ss << '\\n';\n         }\n\n         ss << endl\n            << \"Buy Total:  \" << bid_sum << ' ' << orders.base << endl\n            << \"Sell Total: \" << ask_sum << ' ' << orders.base << endl;\n\n         return ss.str();\n      };\n\n      m[\"sign_message\"] = [](variant result, const fc::variants& a)\n      {\n         auto r = result.as<signed_message>( GRAPHENE_MAX_NESTED_OBJECTS );\n\n         fc::stringstream encapsulated;\n         encapsulated << ENC_HEADER;\n         encapsulated << r.message << '\\n';\n         encapsulated << ENC_META;\n         encapsulated << \"account=\" << r.meta.account << '\\n';\n         encapsulated << \"memokey=\" << std::string( r.meta.memo_key ) << '\\n';\n         encapsulated << \"block=\" << r.meta.block << '\\n';\n         encapsulated << \"timestamp=\" << r.meta.time << '\\n';\n         encapsulated << ENC_SIG;\n         encapsulated << fc::to_hex( (const char*)r.signature->data(), r.signature->size() ) << '\\n';\n         encapsulated << ENC_FOOTER;\n\n         return encapsulated.str();\n      };\n\n      return m;\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_sign.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <fc/crypto/aes.hpp>\n\n#include \"wallet_api_impl.hpp\"\n#include <graphene/wallet/wallet.hpp>\n\n/***\n * These methods handle signing and keys\n */\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   string address_to_shorthash( const graphene::protocol::address& addr )\n   {\n      uint32_t x = addr.addr._hash[0].value();\n      static const char hd[] = \"0123456789abcdef\";\n      string result;\n\n      result += hd[(x >> 0x1c) & 0x0f];\n      result += hd[(x >> 0x18) & 0x0f];\n      result += hd[(x >> 0x14) & 0x0f];\n      result += hd[(x >> 0x10) & 0x0f];\n      result += hd[(x >> 0x0c) & 0x0f];\n      result += hd[(x >> 0x08) & 0x0f];\n      result += hd[(x >> 0x04) & 0x0f];\n      result += hd[(x        ) & 0x0f];\n\n      return result;\n   }\n\n   fc::ecc::private_key derive_private_key( const std::string& prefix_string, int sequence_number )\n   {\n      std::string sequence_string = std::to_string(sequence_number);\n      fc::sha512 h = fc::sha512::hash(prefix_string + \" \" + sequence_string);\n      fc::ecc::private_key derived_key = fc::ecc::private_key::regenerate(fc::sha256::hash(h));\n      return derived_key;\n   }\n\n   string normalize_brain_key( string s )\n   {\n      size_t i = 0, n = s.length();\n      std::string result;\n      char c;\n      result.reserve( n );\n\n      bool preceded_by_whitespace = false;\n      bool non_empty = false;\n      while( i < n )\n      {\n         c = s[i++];\n         switch( c )\n         {\n         case ' ':  case '\\t': case '\\r': case '\\n': case '\\v': case '\\f':\n            preceded_by_whitespace = true;\n            continue;\n\n         case 'a': c = 'A'; break;\n         case 'b': c = 'B'; break;\n         case 'c': c = 'C'; break;\n         case 'd': c = 'D'; break;\n         case 'e': c = 'E'; break;\n         case 'f': c = 'F'; break;\n         case 'g': c = 'G'; break;\n         case 'h': c = 'H'; break;\n         case 'i': c = 'I'; break;\n         case 'j': c = 'J'; break;\n         case 'k': c = 'K'; break;\n         case 'l': c = 'L'; break;\n         case 'm': c = 'M'; break;\n         case 'n': c = 'N'; break;\n         case 'o': c = 'O'; break;\n         case 'p': c = 'P'; break;\n         case 'q': c = 'Q'; break;\n         case 'r': c = 'R'; break;\n         case 's': c = 'S'; break;\n         case 't': c = 'T'; break;\n         case 'u': c = 'U'; break;\n         case 'v': c = 'V'; break;\n         case 'w': c = 'W'; break;\n         case 'x': c = 'X'; break;\n         case 'y': c = 'Y'; break;\n         case 'z': c = 'Z'; break;\n\n         default:\n            break;\n         }\n         if( preceded_by_whitespace && non_empty )\n            result.push_back(' ');\n         result.push_back(c);\n         preceded_by_whitespace = false;\n         non_empty = true;\n      }\n      return result;\n   }\n\n   void wallet_api_impl::encrypt_keys()\n   {\n      if( !is_locked() )\n      {\n         plain_keys data;\n         data.keys = _keys;\n         data.checksum = _checksum;\n         auto plain_txt = fc::raw::pack(data);\n         _wallet.cipher_keys = fc::aes_encrypt( data.checksum, plain_txt );\n      }\n   }\n\n   memo_data wallet_api_impl::sign_memo(string from, string to, string memo)\n   {\n      FC_ASSERT( !self.is_locked() );\n\n      memo_data md = memo_data();\n\n      // get account memo key, if that fails, try a pubkey\n      try {\n         account_object from_account = get_account(from);\n         md.from = from_account.options.memo_key;\n      } catch (const fc::exception& e) {\n         md.from =  self.get_public_key( from );\n      }\n      // same as above, for destination key\n      try {\n         account_object to_account = get_account(to);\n         md.to = to_account.options.memo_key;\n      } catch (const fc::exception& e) {\n         md.to = self.get_public_key( to );\n      }\n\n      md.set_message(get_private_key(md.from), md.to, memo);\n      return md;\n   }\n\n   string wallet_api_impl::read_memo(const memo_data& md)\n   {\n      FC_ASSERT(!is_locked());\n      std::string clear_text;\n\n      const memo_data *memo = &md;\n\n      try {\n         FC_ASSERT( _keys.count(memo->to) || _keys.count(memo->from),\n                    \"Memo is encrypted to a key ${to} or ${from} not in this wallet.\",\n                    (\"to\", memo->to)(\"from\",memo->from) );\n         if( _keys.count(memo->to) ) {\n            auto my_key = wif_to_key(_keys.at(memo->to));\n            FC_ASSERT(my_key, \"Unable to recover private key to decrypt memo. Wallet may be corrupted.\");\n            clear_text = memo->get_message(*my_key, memo->from);\n         } else {\n            auto my_key = wif_to_key(_keys.at(memo->from));\n            FC_ASSERT(my_key, \"Unable to recover private key to decrypt memo. Wallet may be corrupted.\");\n            clear_text = memo->get_message(*my_key, memo->to);\n         }\n      } catch (const fc::exception& e) {\n         elog(\"Error when decrypting memo: ${e}\", (\"e\", e.to_detail_string()));\n      }\n\n      return clear_text;\n   }\n\n   signed_message wallet_api_impl::sign_message(string signer, string message)\n   {\n      FC_ASSERT( !self.is_locked() );\n\n      const account_object from_account = get_account(signer);\n      auto dynamic_props = get_dynamic_global_properties();\n\n      signed_message msg;\n      msg.message = message;\n      msg.meta.account = from_account.name;\n      msg.meta.memo_key = from_account.options.memo_key;\n      msg.meta.block = dynamic_props.head_block_number;\n      msg.meta.time = dynamic_props.time.to_iso_string() + \"Z\";\n      msg.signature = get_private_key( from_account.options.memo_key ).sign_compact( msg.digest() );\n      return msg;\n   }\n\n   bool wallet_api_impl::verify_message( const string& message, const string& account, int block, const string& time,\n                        const compact_signature& sig )\n   {\n      const account_object from_account = get_account( account );\n\n      signed_message msg;\n      msg.message = message;\n      msg.meta.account = from_account.name;\n      msg.meta.memo_key = from_account.options.memo_key;\n      msg.meta.block = block;\n      msg.meta.time = time;\n      msg.signature = sig;\n\n      return verify_signed_message( msg );\n   }\n\n   bool wallet_api_impl::verify_signed_message( const signed_message& message )\n   {\n      if( !message.signature.valid() ) return false;\n\n      const account_object from_account = get_account( message.meta.account );\n\n      const public_key signer( *message.signature, message.digest() );\n      if( !( message.meta.memo_key == signer ) ) return false;\n      FC_ASSERT( from_account.options.memo_key == signer,\n                 \"Message was signed by contained key, but it doesn't belong to the contained account!\" );\n\n      return true;\n   }\n\n   /* meta contains lines of the form \"key=value\".\n    * Returns the value for the corresponding key, throws if key is not present. */\n   static string meta_extract( const string& meta, const string& key )\n   {\n      FC_ASSERT( meta.size() > key.size(), \"Key '${k}' not found!\", (\"k\",key) );\n      size_t start;\n      if( meta.substr( 0, key.size() ) == key && meta[key.size()] == '=' )\n         start = 0;\n      else\n      {\n         start = meta.find( \"\\n\" + key + \"=\" );\n         FC_ASSERT( start != string::npos, \"Key '${k}' not found!\", (\"k\",key) );\n         ++start;\n      }\n      start += key.size() + 1;\n      size_t lf = meta.find( \"\\n\", start );\n      if( lf == string::npos ) lf = meta.size();\n      return meta.substr( start, lf - start );\n   }\n\n   bool wallet_api_impl::verify_encapsulated_message( const string& message )\n   {\n      signed_message msg;\n      size_t begin_p = message.find( ENC_HEADER );\n      FC_ASSERT( begin_p != string::npos, \"BEGIN MESSAGE line not found!\" );\n      size_t meta_p = message.find( ENC_META, begin_p );\n      FC_ASSERT( meta_p != string::npos, \"BEGIN META line not found!\" );\n      FC_ASSERT( meta_p >= begin_p + ENC_HEADER.size() + 1, \"Missing message!?\" );\n      size_t sig_p = message.find( ENC_SIG, meta_p );\n      FC_ASSERT( sig_p != string::npos, \"BEGIN SIGNATURE line not found!\" );\n      FC_ASSERT( sig_p >= meta_p + ENC_META.size(), \"Missing metadata?!\" );\n      size_t end_p = message.find( ENC_FOOTER, meta_p );\n      FC_ASSERT( end_p != string::npos, \"END MESSAGE line not found!\" );\n      FC_ASSERT( end_p >= sig_p + ENC_SIG.size() + 1, \"Missing signature?!\" );\n\n      msg.message = message.substr( begin_p + ENC_HEADER.size(), meta_p - begin_p - ENC_HEADER.size() - 1 );\n      const string meta = message.substr( meta_p + ENC_META.size(), sig_p - meta_p - ENC_META.size() );\n      const string sig = message.substr( sig_p + ENC_SIG.size(), end_p - sig_p - ENC_SIG.size() - 1 );\n\n      msg.meta.account = meta_extract( meta, \"account\" );\n      msg.meta.memo_key = public_key_type( meta_extract( meta, \"memokey\" ) );\n      msg.meta.block = boost::lexical_cast<uint32_t>( meta_extract( meta, \"block\" ) );\n      msg.meta.time = meta_extract( meta, \"timestamp\" );\n      msg.signature = variant(sig).as< fc::ecc::compact_signature >( 5 );\n\n      return verify_signed_message( msg );\n   }\n\n   signed_transaction wallet_api_impl::add_transaction_signature( signed_transaction tx, \n         bool broadcast )\n   {\n      set<public_key_type> approving_key_set = get_owned_required_keys(tx, false);\n\n      if ( ( ( tx.ref_block_num == 0 && tx.ref_block_prefix == 0 ) ||\n             tx.expiration == fc::time_point_sec() ) &&\n           tx.signatures.empty() )\n      {\n         auto dyn_props = get_dynamic_global_properties();\n         auto parameters = get_global_properties().parameters;\n         fc::time_point_sec now = dyn_props.time;\n         tx.set_reference_block( dyn_props.head_block_id );\n         tx.set_expiration( now + parameters.maximum_time_until_expiration );\n      }\n      for ( const public_key_type &key : approving_key_set )\n         tx.sign( get_private_key( key ), _chain_id );\n\n      if ( broadcast )\n      {\n         try\n         {\n            _remote_net_broadcast->broadcast_transaction( tx );\n         }\n         catch ( const fc::exception &e )\n         {\n            elog( \"Caught exception while broadcasting tx ${id}:  ${e}\",\n                  ( \"id\", tx.id().str() )( \"e\", e.to_detail_string() ) );\n            FC_THROW( \"Caught exception while broadcasting tx\" );\n         }\n      }\n\n      return tx;\n   }\n\n   signed_transaction wallet_api_impl::sign_transaction( signed_transaction tx, bool broadcast )\n   {\n      return sign_transaction2(tx, {}, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::sign_transaction2( signed_transaction tx,\n                                                         const vector<public_key_type>& signing_keys, bool broadcast)\n   {\n      set<public_key_type> approving_key_set = get_owned_required_keys(tx);\n\n      // Add any explicit keys to the approving_key_set\n      for (const public_key_type& explicit_key : signing_keys) {\n         approving_key_set.insert(explicit_key);\n      }\n\n      auto dyn_props = get_dynamic_global_properties();\n      tx.set_reference_block( dyn_props.head_block_id );\n\n      // first, some bookkeeping, expire old items from _recently_generated_transactions\n      // since transactions include the head block id, we just need the index for keeping transactions unique\n      // when there are multiple transactions in the same block.  choose a time period that should be at\n      // least one block long, even in the worst case.  2 minutes ought to be plenty.\n      fc::time_point_sec oldest_transaction_ids_to_track(dyn_props.time - fc::minutes(2));\n      auto oldest_transaction_record_iter =\n            _recently_generated_transactions.get<timestamp_index>().lower_bound(oldest_transaction_ids_to_track);\n      auto begin_iter = _recently_generated_transactions.get<timestamp_index>().begin();\n      _recently_generated_transactions.get<timestamp_index>().erase(begin_iter, oldest_transaction_record_iter);\n\n      uint32_t expiration_time_offset = 0;\n      for (;;)\n      {\n         tx.set_expiration( dyn_props.time + fc::seconds(30 + expiration_time_offset) );\n         tx.clear_signatures();\n\n         for( const public_key_type& key : approving_key_set )\n            tx.sign( get_private_key(key), _chain_id );\n\n         graphene::chain::transaction_id_type this_transaction_id = tx.id();\n         auto iter = _recently_generated_transactions.find(this_transaction_id);\n         if (iter == _recently_generated_transactions.end())\n         {\n            // we haven't generated this transaction before, the usual case\n            recently_generated_transaction_record this_transaction_record;\n            this_transaction_record.generation_time = dyn_props.time;\n            this_transaction_record.transaction_id = this_transaction_id;\n            _recently_generated_transactions.insert(this_transaction_record);\n            break;\n         }\n\n         // else we've generated a dupe, increment expiration time and re-sign it\n         ++expiration_time_offset;\n      }\n\n      if( broadcast )\n      {\n         try\n         {\n            _remote_net_broadcast->broadcast_transaction( tx );\n         }\n         catch (const fc::exception& e)\n         {\n            elog(\"Caught exception while broadcasting tx ${id}:  ${e}\",\n                 (\"id\", tx.id().str())(\"e\", e.to_detail_string()) );\n            throw;\n         }\n      }\n\n      return tx;\n   }\n\n   fc::ecc::private_key wallet_api_impl::get_private_key(const public_key_type& id)const\n   {\n      auto it = _keys.find(id);\n      FC_ASSERT( it != _keys.end() );\n\n      fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second );\n      FC_ASSERT( privkey );\n      return *privkey;\n   }\n\n   fc::ecc::private_key wallet_api_impl::get_private_key_for_account(const account_object& account)const\n   {\n      vector<public_key_type> active_keys = account.active.get_keys();\n      if (active_keys.size() != 1)\n         FC_THROW(\"Expecting a simple authority with one active key\");\n      return get_private_key(active_keys.front());\n   }\n\n   // imports the private key into the wallet, and associate it in some way (?) with the\n   // given account name.\n   // @returns true if the key matches a current active/owner/memo key for the named\n   //          account, false otherwise (but it is stored either way)\n   bool wallet_api_impl::import_key(string account_name_or_id, string wif_key)\n   {\n      fc::optional<fc::ecc::private_key> optional_private_key = wif_to_key(wif_key);\n      if (!optional_private_key)\n         FC_THROW(\"Invalid private key\");\n      graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key();\n\n      account_object account = get_account( account_name_or_id );\n\n      // make a list of all current public keys for the named account\n      flat_set<public_key_type> all_keys_for_account;\n      std::vector<public_key_type> active_keys = account.active.get_keys();\n      std::vector<public_key_type> owner_keys = account.owner.get_keys();\n      std::copy(active_keys.begin(), active_keys.end(),\n            std::inserter(all_keys_for_account, all_keys_for_account.end()));\n      std::copy(owner_keys.begin(), owner_keys.end(),\n            std::inserter(all_keys_for_account, all_keys_for_account.end()));\n      all_keys_for_account.insert(account.options.memo_key);\n\n      _keys[wif_pub_key] = wif_key;\n\n      _wallet.update_account(account);\n\n      _wallet.extra_keys[account.id].insert(wif_pub_key);\n\n      return all_keys_for_account.find(wif_pub_key) != all_keys_for_account.end();\n   }\n\n   /**\n    * Get the required public keys to sign the transaction which had been\n    * owned by us\n    *\n    * NOTE, if `erase_existing_sigs` set to true, the original trasaction's\n    * signatures will be erased\n    *\n    * @param tx           The transaction to be signed\n    * @param erase_existing_sigs\n    *        The transaction could have been partially signed already,\n    *        if set to false, the corresponding public key of existing\n    *        signatures won't be returned.\n    *        If set to true, the existing signatures will be erased and\n    *        all required keys returned.\n   */\n   set<public_key_type> wallet_api_impl::get_owned_required_keys( signed_transaction &tx,\n         bool erase_existing_sigs )\n   {\n      set<public_key_type> pks = _remote_db->get_potential_signatures( tx );\n      flat_set<public_key_type> owned_keys;\n      owned_keys.reserve( pks.size() );\n      std::copy_if( pks.begin(), pks.end(),\n                    std::inserter( owned_keys, owned_keys.end() ),\n                    [this]( const public_key_type &pk ) {\n                       return _keys.find( pk ) != _keys.end();\n                    } );\n\n      if ( erase_existing_sigs )\n         tx.signatures.clear();\n\n      return _remote_db->get_required_signatures( tx, owned_keys );\n   }\n\n   flat_set<public_key_type> wallet_api_impl::get_transaction_signers(const signed_transaction &tx) const\n   {\n      return tx.get_signature_keys(_chain_id);\n   }\n\n   signed_transaction wallet_api_impl::approve_proposal( const string& fee_paying_account, const string& proposal_id,\n         const approval_delta& delta, bool broadcast )\n   {\n      proposal_update_operation update_op;\n\n      update_op.fee_paying_account = get_account(fee_paying_account).id;\n      update_op.proposal = fc::variant(proposal_id, 1).as<proposal_id_type>( 1 );\n      // make sure the proposal exists\n      get_object( update_op.proposal );\n\n      for( const std::string& name : delta.active_approvals_to_add )\n         update_op.active_approvals_to_add.insert( get_account( name ).id );\n      for( const std::string& name : delta.active_approvals_to_remove )\n         update_op.active_approvals_to_remove.insert( get_account( name ).id );\n      for( const std::string& name : delta.owner_approvals_to_add )\n         update_op.owner_approvals_to_add.insert( get_account( name ).id );\n      for( const std::string& name : delta.owner_approvals_to_remove )\n         update_op.owner_approvals_to_remove.insert( get_account( name ).id );\n      for( const std::string& k : delta.key_approvals_to_add )\n         update_op.key_approvals_to_add.insert( public_key_type( k ) );\n      for( const std::string& k : delta.key_approvals_to_remove )\n         update_op.key_approvals_to_remove.insert( public_key_type( k ) );\n\n      signed_transaction tx;\n      tx.operations.push_back(update_op);\n      set_operation_fees(tx, get_global_properties().parameters.get_current_fees());\n      tx.validate();\n      return sign_transaction(tx, broadcast);\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_transfer.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"wallet_api_impl.hpp\"\n#include <graphene/wallet/wallet.hpp>\n\n/***\n * Methods to handle transfers / exchange orders\n */\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   /***\n    * @brief a helper method to create an htlc_hash from an algo/hash combination\n    */\n   htlc_hash wallet_api_impl::do_hash( const string& algorithm, const std::string& hash )\n   {\n      string name_upper;\n      std::transform( algorithm.begin(), algorithm.end(), std::back_inserter(name_upper), ::toupper);\n      if( name_upper == \"RIPEMD160\" )\n         return fc::ripemd160( hash );\n      if( name_upper == \"SHA256\" )\n         return fc::sha256( hash );\n      if( name_upper == \"SHA1\" )\n         return fc::sha1( hash );\n      if( name_upper == \"HASH160\" )\n         return fc::hash160( hash );\n      FC_THROW_EXCEPTION( fc::invalid_arg_exception, \"Unknown algorithm '${a}'\", (\"a\",algorithm) );\n   }\n\n   signed_transaction wallet_api_impl::transfer(string from, string to, string amount,\n                               string asset_symbol, string memo, bool broadcast )\n   { try {\n      FC_ASSERT( !self.is_locked() );\n      fc::optional<asset_object> asset_obj = get_asset(asset_symbol);\n      FC_ASSERT(asset_obj, \"Could not find asset matching ${asset}\", (\"asset\", asset_symbol));\n\n      account_object from_account = get_account(from);\n      account_object to_account = get_account(to);\n      account_id_type from_id = from_account.id;\n      account_id_type to_id = to_account.id;\n\n      transfer_operation xfer_op;\n\n      xfer_op.from = from_id;\n      xfer_op.to = to_id;\n      xfer_op.amount = asset_obj->amount_from_string(amount);\n\n      if( memo.size() )\n         {\n            xfer_op.memo = memo_data();\n            xfer_op.memo->from = from_account.options.memo_key;\n            xfer_op.memo->to = to_account.options.memo_key;\n            xfer_op.memo->set_message(get_private_key(from_account.options.memo_key),\n                                      to_account.options.memo_key, memo);\n         }\n\n      signed_transaction tx;\n      tx.operations.push_back(xfer_op);\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction(tx, broadcast);\n   } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::htlc_create( string source, string destination, string amount,\n         string asset_symbol, string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size,\n         const uint32_t claim_period_seconds, const std::string& memo, bool broadcast )\n   {\n      try\n      {\n         FC_ASSERT( !self.is_locked() );\n         fc::optional<asset_object> asset_obj = get_asset(asset_symbol);\n         FC_ASSERT(asset_obj, \"Could not find asset matching ${asset}\", (\"asset\", asset_symbol));\n\n         const account_object& from_acct = get_account(source);\n         const account_object& to_acct = get_account(destination);\n         htlc_create_operation create_op;\n         create_op.from = get_account(source).id;\n         create_op.to = get_account(destination).id;\n         create_op.amount = asset_obj->amount_from_string(amount);\n         create_op.claim_period_seconds = claim_period_seconds;\n         create_op.preimage_hash = do_hash( hash_algorithm, preimage_hash );\n         create_op.preimage_size = preimage_size;\n         if (!memo.empty())\n         {\n            memo_data data;\n            data.from = from_acct.options.memo_key;\n            data.to = to_acct.options.memo_key;\n            data.set_message( \n                  get_private_key(from_acct.options.memo_key), to_acct.options.memo_key, memo);\n            create_op.extensions.value.memo = data;\n         }\n\n         signed_transaction tx;\n         tx.operations.push_back(create_op);\n         set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n         tx.validate();\n\n         return sign_transaction(tx, broadcast);\n      } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm)\n            (preimage_hash)(preimage_size)(claim_period_seconds)(broadcast) )\n   }\n\n   signed_transaction wallet_api_impl::htlc_redeem( string htlc_id, string issuer,\n         const std::vector<char>& preimage, bool broadcast )\n   {\n      try\n      {\n         FC_ASSERT( !self.is_locked() );\n         fc::optional<htlc_object> htlc_obj = get_htlc(htlc_id);\n         FC_ASSERT(htlc_obj, \"Could not find HTLC matching ${htlc}\", (\"htlc\", htlc_id));\n\n         account_object issuer_obj = get_account(issuer);\n\n         htlc_redeem_operation update_op;\n         update_op.htlc_id = htlc_obj->id;\n         update_op.redeemer = issuer_obj.id;\n         update_op.preimage = preimage;\n\n         signed_transaction tx;\n         tx.operations.push_back(update_op);\n         set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n         tx.validate();\n\n         return sign_transaction(tx, broadcast);\n      } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) )\n   }\n\n   signed_transaction wallet_api_impl::htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add,\n         bool broadcast)\n   {\n      try\n      {\n         FC_ASSERT( !self.is_locked() );\n         fc::optional<htlc_object> htlc_obj = get_htlc(htlc_id);\n         FC_ASSERT(htlc_obj, \"Could not find HTLC matching ${htlc}\", (\"htlc\", htlc_id));\n\n         account_object issuer_obj = get_account(issuer);\n\n         htlc_extend_operation update_op;\n         update_op.htlc_id = htlc_obj->id;\n         update_op.update_issuer = issuer_obj.id;\n         update_op.seconds_to_add = seconds_to_add;\n\n         signed_transaction tx;\n         tx.operations.push_back(update_op);\n         set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n         tx.validate();\n\n         return sign_transaction(tx, broadcast);\n      } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(seconds_to_add)(broadcast) )\n   }\n\n   fc::optional<htlc_object> wallet_api_impl::get_htlc(string htlc_id) const\n   {\n      htlc_id_type id;\n      fc::from_variant(htlc_id, id);\n      auto obj = _remote_db->get_objects( { id }, {}).front();\n      if ( !obj.is_null() )\n      {\n         return fc::optional<htlc_object>(obj.template as<htlc_object>(GRAPHENE_MAX_NESTED_OBJECTS));\n      }\n      return fc::optional<htlc_object>();\n   }\n\n   signed_transaction wallet_api_impl::sell_asset(string seller_account, string amount_to_sell,\n         string symbol_to_sell, string min_to_receive, string symbol_to_receive,\n         uint32_t timeout_sec, bool fill_or_kill, bool broadcast )\n   {\n      account_object seller   = get_account( seller_account );\n\n      limit_order_create_operation op;\n      op.seller = seller.id;\n      op.amount_to_sell = get_asset(symbol_to_sell).amount_from_string(amount_to_sell);\n      op.min_to_receive = get_asset(symbol_to_receive).amount_from_string(min_to_receive);\n      if( timeout_sec )\n         op.expiration = fc::time_point::now() + fc::seconds(timeout_sec);\n      op.fill_or_kill = fill_or_kill;\n\n      signed_transaction tx;\n      tx.operations.push_back(op);\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   }\n\n   signed_transaction wallet_api_impl::borrow_asset(string seller_name, string amount_to_borrow, \n         string asset_symbol, string amount_of_collateral, bool broadcast )\n   {\n      account_object seller = get_account(seller_name);\n      asset_object mia = get_asset(asset_symbol);\n      FC_ASSERT(mia.is_market_issued());\n      asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset);\n\n      call_order_update_operation op;\n      op.funding_account = seller.id;\n      op.delta_debt   = mia.amount_from_string(amount_to_borrow);\n      op.delta_collateral = collateral.amount_from_string(amount_of_collateral);\n\n      signed_transaction trx;\n      trx.operations = {op};\n      set_operation_fees( trx, _remote_db->get_global_properties().parameters.get_current_fees());\n      trx.validate();\n\n      return sign_transaction(trx, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::borrow_asset_ext( string seller_name, string amount_to_borrow, \n         string asset_symbol, string amount_of_collateral,\n         call_order_update_operation::extensions_type extensions, bool broadcast )\n   {\n      account_object seller = get_account(seller_name);\n      asset_object mia = get_asset(asset_symbol);\n      FC_ASSERT(mia.is_market_issued());\n      asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset);\n\n      call_order_update_operation op;\n      op.funding_account = seller.id;\n      op.delta_debt   = mia.amount_from_string(amount_to_borrow);\n      op.delta_collateral = collateral.amount_from_string(amount_of_collateral);\n      op.extensions = extensions;\n\n      signed_transaction trx;\n      trx.operations = {op};\n      set_operation_fees( trx, _remote_db->get_global_properties().parameters.get_current_fees());\n      trx.validate();\n\n      return sign_transaction(trx, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::cancel_order(limit_order_id_type order_id, bool broadcast )\n   { try {\n         FC_ASSERT(!is_locked());\n         signed_transaction trx;\n\n         limit_order_cancel_operation op;\n         op.fee_paying_account = get_object(order_id).seller;\n         op.order = order_id;\n         trx.operations = {op};\n         set_operation_fees( trx, _remote_db->get_global_properties().parameters.get_current_fees());\n\n         trx.validate();\n         return sign_transaction(trx, broadcast);\n   } FC_CAPTURE_AND_RETHROW((order_id)) }\n\n   signed_transaction wallet_api_impl::withdraw_vesting( string witness_name, string amount, string asset_symbol,\n         bool broadcast )\n   { try {\n      asset_object asset_obj = get_asset( asset_symbol );\n      fc::optional<vesting_balance_id_type> vbid = maybe_id<vesting_balance_id_type>(witness_name);\n      if( !vbid )\n      {\n         witness_object wit = get_witness( witness_name );\n         FC_ASSERT( wit.pay_vb );\n         vbid = wit.pay_vb;\n      }\n\n      vesting_balance_object vbo = get_object( *vbid );\n      vesting_balance_withdraw_operation vesting_balance_withdraw_op;\n\n      vesting_balance_withdraw_op.vesting_balance = *vbid;\n      vesting_balance_withdraw_op.owner = vbo.owner;\n      vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount);\n\n      signed_transaction tx;\n      tx.operations.push_back( vesting_balance_withdraw_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) )\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "libraries/wallet/wallet_voting.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include \"wallet_api_impl.hpp\"\n#include <graphene/utilities/key_conversion.hpp>\n\n/****\n * Methods to handle voting / workers / committee\n */\n\nnamespace graphene { namespace wallet { namespace detail {\n\n   template<typename WorkerInit>\n   static WorkerInit _create_worker_initializer( const variant& worker_settings )\n   {\n      WorkerInit result;\n      from_variant( worker_settings, result, GRAPHENE_MAX_NESTED_OBJECTS );\n      return result;\n   }\n   \n   signed_transaction wallet_api_impl::update_worker_votes(\n      string account,\n      worker_vote_delta delta,\n      bool broadcast\n      )\n   {\n      account_object acct = get_account( account );\n\n      // you could probably use a faster algorithm for this, but flat_set is fast enough :)\n      flat_set< worker_id_type > merged;\n      merged.reserve( delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() );\n      for( const worker_id_type& wid : delta.vote_for )\n      {\n         bool inserted = merged.insert( wid ).second;\n         FC_ASSERT( inserted, \"worker ${wid} specified multiple times\", (\"wid\", wid) );\n      }\n      for( const worker_id_type& wid : delta.vote_against )\n      {\n         bool inserted = merged.insert( wid ).second;\n         FC_ASSERT( inserted, \"worker ${wid} specified multiple times\", (\"wid\", wid) );\n      }\n      for( const worker_id_type& wid : delta.vote_abstain )\n      {\n         bool inserted = merged.insert( wid ).second;\n         FC_ASSERT( inserted, \"worker ${wid} specified multiple times\", (\"wid\", wid) );\n      }\n\n      // should be enforced by FC_ASSERT's above\n      assert( merged.size() == delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() );\n\n      vector< object_id_type > query_ids;\n      for( const worker_id_type& wid : merged )\n         query_ids.push_back( wid );\n\n      flat_set<vote_id_type> new_votes( acct.options.votes );\n\n      fc::variants objects = _remote_db->get_objects( query_ids, {} );\n      for( const variant& obj : objects )\n      {\n         worker_object wo;\n         from_variant( obj, wo, GRAPHENE_MAX_NESTED_OBJECTS );\n         new_votes.erase( wo.vote_for );\n         new_votes.erase( wo.vote_against );\n         if( delta.vote_for.find( wo.id ) != delta.vote_for.end() )\n            new_votes.insert( wo.vote_for );\n         else if( delta.vote_against.find( wo.id ) != delta.vote_against.end() )\n            new_votes.insert( wo.vote_against );\n         else\n            assert( delta.vote_abstain.find( wo.id ) != delta.vote_abstain.end() );\n      }\n\n      account_update_operation update_op;\n      update_op.account = acct.id;\n      update_op.new_options = acct.options;\n      update_op.new_options->votes = new_votes;\n\n      signed_transaction tx;\n      tx.operations.push_back( update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   }\n\n   signed_transaction wallet_api_impl::create_committee_member(string owner_account, string url, \n         bool broadcast )\n   { try {\n\n      committee_member_create_operation committee_member_create_op;\n      committee_member_create_op.committee_member_account = get_account_id(owner_account);\n      committee_member_create_op.url = url;\n      if (_remote_db->get_committee_member_by_account(owner_account))\n         FC_THROW(\"Account ${owner_account} is already a committee_member\", (\"owner_account\", owner_account));\n\n      signed_transaction tx;\n      tx.operations.push_back( committee_member_create_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }\n\n   witness_object wallet_api_impl::get_witness(string owner_account)\n   {\n      try\n      {\n         fc::optional<witness_id_type> witness_id = maybe_id<witness_id_type>(owner_account);\n         if (witness_id)\n         {\n            std::vector<witness_id_type> ids_to_get;\n            ids_to_get.push_back(*witness_id);\n            std::vector<fc::optional<witness_object>> witness_objects = _remote_db->get_witnesses(ids_to_get);\n            if (witness_objects.front())\n               return *witness_objects.front();\n            FC_THROW(\"No witness is registered for id ${id}\", (\"id\", owner_account));\n         }\n         else\n         {\n            // then maybe it's the owner account\n            try\n            {\n               std::string owner_account_id = account_id_to_string(get_account_id(owner_account));\n               fc::optional<witness_object> witness = _remote_db->get_witness_by_account(owner_account_id);\n               if (witness)\n                  return *witness;\n               else\n                  FC_THROW(\"No witness is registered for account ${account}\", (\"account\", owner_account));\n            }\n            catch (const fc::exception&)\n            {\n               FC_THROW(\"No account or witness named ${account}\", (\"account\", owner_account));\n            }\n         }\n      }\n      FC_CAPTURE_AND_RETHROW( (owner_account) )\n   }\n\n   committee_member_object wallet_api_impl::get_committee_member(string owner_account)\n   {\n      try\n      {\n         fc::optional<committee_member_id_type> committee_member_id =\n               maybe_id<committee_member_id_type>(owner_account);\n         if (committee_member_id)\n         {\n            std::vector<committee_member_id_type> ids_to_get;\n            ids_to_get.push_back(*committee_member_id);\n            std::vector<fc::optional<committee_member_object>> committee_member_objects =\n                  _remote_db->get_committee_members(ids_to_get);\n            if (committee_member_objects.front())\n               return *committee_member_objects.front();\n            FC_THROW(\"No committee_member is registered for id ${id}\", (\"id\", owner_account));\n         }\n         else\n         {\n            // then maybe it's the owner account\n            try\n            {\n               fc::optional<committee_member_object> committee_member =\n                     _remote_db->get_committee_member_by_account(owner_account);\n               if (committee_member)\n                  return *committee_member;\n               else\n                  FC_THROW(\"No committee_member is registered for account ${account}\", (\"account\", owner_account));\n            }\n            catch (const fc::exception&)\n            {\n               FC_THROW(\"No account or committee_member named ${account}\", (\"account\", owner_account));\n            }\n         }\n      }\n      FC_CAPTURE_AND_RETHROW( (owner_account) )\n   }\n\n   signed_transaction wallet_api_impl::create_witness(string owner_account,\n         string url, bool broadcast /* = false */)\n   { try {\n      account_object witness_account = get_account(owner_account);\n      fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account);\n      int witness_key_index = find_first_unused_derived_key_index(active_private_key);\n      fc::ecc::private_key witness_private_key =\n            derive_private_key(key_to_wif(active_private_key), witness_key_index);\n      graphene::chain::public_key_type witness_public_key = witness_private_key.get_public_key();\n\n      witness_create_operation witness_create_op;\n      witness_create_op.witness_account = witness_account.id;\n      witness_create_op.block_signing_key = witness_public_key;\n      witness_create_op.url = url;\n\n      if (_remote_db->get_witness_by_account(account_id_to_string(witness_create_op.witness_account)))\n         FC_THROW(\"Account ${owner_account} is already a witness\", (\"owner_account\", owner_account));\n\n      signed_transaction tx;\n      tx.operations.push_back( witness_create_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      _wallet.pending_witness_registrations[owner_account] = key_to_wif(witness_private_key);\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::update_witness(string witness_name, string url,\n         string block_signing_key, bool broadcast )\n   { try {\n      witness_object witness = get_witness(witness_name);\n      account_object witness_account = get_account( witness.witness_account );\n\n      witness_update_operation witness_update_op;\n      witness_update_op.witness = witness.id;\n      witness_update_op.witness_account = witness_account.id;\n      if( url != \"\" )\n         witness_update_op.new_url = url;\n      if( block_signing_key != \"\" )\n         witness_update_op.new_signing_key = public_key_type( block_signing_key );\n\n      signed_transaction tx;\n      tx.operations.push_back( witness_update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (witness_name)(url)(block_signing_key)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::create_worker( string owner_account, time_point_sec work_begin_date,\n         time_point_sec work_end_date, share_type daily_pay, string name, string url,\n         variant worker_settings, bool broadcast)\n   {\n      worker_initializer init;\n      std::string wtype = worker_settings[\"type\"].get_string();\n\n      // TODO:  Use introspection to do this dispatch\n      if( wtype == \"burn\" )\n         init = _create_worker_initializer< burn_worker_initializer >( worker_settings );\n      else if( wtype == \"refund\" )\n         init = _create_worker_initializer< refund_worker_initializer >( worker_settings );\n      else if( wtype == \"vesting\" )\n         init = _create_worker_initializer< vesting_balance_worker_initializer >( worker_settings );\n      else\n      {\n         FC_ASSERT( false, \"unknown worker[\\\"type\\\"] value\" );\n      }\n\n      worker_create_operation op;\n      op.owner = get_account( owner_account ).id;\n      op.work_begin_date = work_begin_date;\n      op.work_end_date = work_end_date;\n      op.daily_pay = daily_pay;\n      op.name = name;\n      op.url = url;\n      op.initializer = init;\n\n      signed_transaction tx;\n      tx.operations.push_back( op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() );\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   }\n\n   signed_transaction wallet_api_impl::vote_for_committee_member(string voting_account,\n         string committee_member, bool approve, bool broadcast )\n   { try {\n      account_object voting_account_object = get_account(voting_account);\n      fc::optional<committee_member_object> committee_member_obj =\n            _remote_db->get_committee_member_by_account(committee_member);\n      if (!committee_member_obj)\n         FC_THROW(\"Account ${committee_member} is not registered as a committee_member\",\n                  (\"committee_member\", committee_member));\n      if (approve)\n      {\n         auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id);\n         if (!insert_result.second)\n            FC_THROW(\"Account ${account} was already voting for committee_member ${committee_member}\",\n                     (\"account\", voting_account)(\"committee_member\", committee_member));\n      }\n      else\n      {\n         unsigned votes_removed = voting_account_object.options.votes.erase(committee_member_obj->vote_id);\n         if (!votes_removed)\n            FC_THROW(\"Account ${account} is already not voting for committee_member ${committee_member}\",\n                     (\"account\", voting_account)(\"committee_member\", committee_member));\n      }\n      account_update_operation account_update_op;\n      account_update_op.account = voting_account_object.id;\n      account_update_op.new_options = voting_account_object.options;\n\n      signed_transaction tx;\n      tx.operations.push_back( account_update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::vote_for_witness(string voting_account, string witness,\n         bool approve, bool broadcast )\n   { try {\n      account_object voting_account_object = get_account(voting_account);\n\n      fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness);\n      if (!witness_obj)\n         FC_THROW(\"Account ${witness} is not registered as a witness\", (\"witness\", witness));\n      if (approve)\n      {\n         auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);\n         if (!insert_result.second)\n            FC_THROW(\"Account ${account} was already voting for witness ${witness}\",\n                     (\"account\", voting_account)(\"witness\", witness));\n      }\n      else\n      {\n         unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id);\n         if (!votes_removed)\n            FC_THROW(\"Account ${account} is already not voting for witness ${witness}\",\n                     (\"account\", voting_account)(\"witness\", witness));\n      }\n      account_update_operation account_update_op;\n      account_update_op.account = voting_account_object.id;\n      account_update_op.new_options = voting_account_object.options;\n\n      signed_transaction tx;\n      tx.operations.push_back( account_update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::set_voting_proxy(string account_to_modify, \n         optional<string> voting_account, bool broadcast )\n   { try {\n      account_object account_object_to_modify = get_account(account_to_modify);\n      if (voting_account)\n      {\n         account_id_type new_voting_account_id = get_account_id(*voting_account);\n         if (account_object_to_modify.options.voting_account == new_voting_account_id)\n            FC_THROW(\"Voting proxy for ${account} is already set to ${voter}\",\n                     (\"account\", account_to_modify)(\"voter\", *voting_account));\n         account_object_to_modify.options.voting_account = new_voting_account_id;\n      }\n      else\n      {\n         if (account_object_to_modify.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT)\n            FC_THROW(\"Account ${account} is already voting for itself\", (\"account\", account_to_modify));\n         account_object_to_modify.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n      }\n\n      account_update_operation account_update_op;\n      account_update_op.account = account_object_to_modify.id;\n      account_update_op.new_options = account_object_to_modify.options;\n\n      signed_transaction tx;\n      tx.operations.push_back( account_update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (account_to_modify)(voting_account)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::set_desired_witness_and_committee_member_count(\n         string account_to_modify, uint16_t desired_number_of_witnesses, uint16_t desired_number_of_committee_members,\n         bool broadcast )\n   { try {\n      account_object account_object_to_modify = get_account(account_to_modify);\n\n      if (account_object_to_modify.options.num_witness == desired_number_of_witnesses &&\n          account_object_to_modify.options.num_committee == desired_number_of_committee_members)\n         FC_THROW(\"Account ${account} is already voting for ${witnesses} witnesses\"\n                  \" and ${committee_members} committee_members\",\n                  (\"account\", account_to_modify)(\"witnesses\", desired_number_of_witnesses)\n                  (\"committee_members\",desired_number_of_witnesses));\n      account_object_to_modify.options.num_witness = desired_number_of_witnesses;\n      account_object_to_modify.options.num_committee = desired_number_of_committee_members;\n\n      account_update_operation account_update_op;\n      account_update_op.account = account_object_to_modify.id;\n      account_update_op.new_options = account_object_to_modify.options;\n\n      signed_transaction tx;\n      tx.operations.push_back( account_update_op );\n      set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees());\n      tx.validate();\n\n      return sign_transaction( tx, broadcast );\n   } FC_CAPTURE_AND_RETHROW( (account_to_modify)(desired_number_of_witnesses)\n                             (desired_number_of_committee_members)(broadcast) ) }\n\n   signed_transaction wallet_api_impl::propose_parameter_change( const string& proposing_account,\n         fc::time_point_sec expiration_time, const variant_object& changed_values, bool broadcast )\n   {\n      FC_ASSERT( !changed_values.contains(\"current_fees\") );\n\n      const chain_parameters& current_params = get_global_properties().parameters;\n      chain_parameters new_params = current_params;\n      fc::reflector<chain_parameters>::visit(\n         fc::from_variant_visitor<chain_parameters>( changed_values, new_params, GRAPHENE_MAX_NESTED_OBJECTS )\n         );\n\n      committee_member_update_global_parameters_operation update_op;\n      update_op.new_parameters = new_params;\n\n      proposal_create_operation prop_op;\n\n      prop_op.expiration_time = expiration_time;\n      prop_op.review_period_seconds = current_params.committee_proposal_review_period;\n      prop_op.fee_paying_account = get_account(proposing_account).id;\n\n      prop_op.proposed_ops.emplace_back( update_op );\n      current_params.get_current_fees().set_fee( prop_op.proposed_ops.back().op );\n\n      signed_transaction tx;\n      tx.operations.push_back(prop_op);\n      set_operation_fees(tx, current_params.get_current_fees());\n      tx.validate();\n\n      return sign_transaction(tx, broadcast);\n   }\n\n   signed_transaction wallet_api_impl::propose_fee_change( const string& proposing_account,\n         fc::time_point_sec expiration_time, const variant_object& changed_fees, bool broadcast )\n   {\n      const chain_parameters& current_params = get_global_properties().parameters;\n      const fee_schedule_type& current_fees = current_params.get_current_fees();\n\n      flat_map< int, fee_parameters > fee_map;\n      fee_map.reserve( current_fees.parameters.size() );\n      for( const fee_parameters& op_fee : current_fees.parameters )\n         fee_map[ op_fee.which() ] = op_fee;\n      uint32_t scale = current_fees.scale;\n\n      for( const auto& item : changed_fees )\n      {\n         const string& key = item.key();\n         if( key == \"scale\" )\n         {\n            int64_t _scale = item.value().as_int64();\n            FC_ASSERT( _scale >= 0 );\n            FC_ASSERT( _scale <= std::numeric_limits<uint32_t>::max() );\n            scale = uint32_t( _scale );\n            continue;\n         }\n         // is key a number?\n         auto is_numeric = [&key]() -> bool\n         {\n            size_t n = key.size();\n            for( size_t i=0; i<n; i++ )\n            {\n               if( !isdigit( key[i] ) )\n                  return false;\n            }\n            return true;\n         };\n\n         int which;\n         if( is_numeric() )\n            which = std::stoi( key );\n         else\n         {\n            const auto& n2w = _operation_which_map.name_to_which;\n            auto it = n2w.find( key );\n            FC_ASSERT( it != n2w.end(), \"unknown operation\" );\n            which = it->second;\n         }\n\n         fee_parameters fp = from_which_variant< fee_parameters >( which, item.value(), GRAPHENE_MAX_NESTED_OBJECTS );\n         fee_map[ which ] = fp;\n      }\n\n      fee_schedule_type new_fees;\n\n      for( const std::pair< int, fee_parameters >& item : fee_map )\n         new_fees.parameters.insert( item.second );\n      new_fees.scale = scale;\n\n      chain_parameters new_params = current_params;\n      new_params.get_mutable_fees() = new_fees;\n\n      committee_member_update_global_parameters_operation update_op;\n      update_op.new_parameters = new_params;\n\n      proposal_create_operation prop_op;\n\n      prop_op.expiration_time = expiration_time;\n      prop_op.review_period_seconds = current_params.committee_proposal_review_period;\n      prop_op.fee_paying_account = get_account(proposing_account).id;\n\n      prop_op.proposed_ops.emplace_back( update_op );\n      current_params.get_current_fees().set_fee( prop_op.proposed_ops.back().op );\n\n      signed_transaction tx;\n      tx.operations.push_back(prop_op);\n      set_operation_fees(tx, current_params.get_current_fees());\n      tx.validate();\n\n      return sign_transaction(tx, broadcast);\n   }\n\n}}} // namespace graphene::wallet::detail\n"
  },
  {
    "path": "programs/CMakeLists.txt",
    "content": "add_subdirectory( build_helpers )\nadd_subdirectory( cli_wallet )\nadd_subdirectory( genesis_util )\nadd_subdirectory( witness_node )\nadd_subdirectory( js_operation_serializer )\nadd_subdirectory( size_checker )\nadd_subdirectory( network_mapper )\n"
  },
  {
    "path": "programs/README.md",
    "content": "# BitShares Programs\n\nThe bitshares programs are a collection of binaries to run the blockchain, interact with it or utilities.\n\nThe main program is the `witness_node`, used to run a bitshares block producer, API or plugin node. The second in importance is the `cli_wallet`, used to interact with the blockchain. This 2 programs are the most used by the community and updated by the developers, rest of the programs are utilities.\n\nPrograms in here are part of the **bitshares-core** project and are maintained by the bitshares core team and contributors.\n\n\n# Available Programs\n\nFolder | Name  | Description | Category | Status | Help \n---|---|---|---|---|---\n[witness_node](witness_node) | Witness Node | Main software used to sign blocks or provide services. | Node | Active | `./witness_node --help` - [Delayed node configuration](https://github.com/bitshares/bitshares-core/wiki/Delayed-Node)\n[cli_wallet](cli_wallet) | CLI Wallet | Software to interact with the blockchain by command line.  | Wallet | Active | `./cli_wallet --help` \n[js_operation_serializer](js_operation_serializer) | Operation Serializer | Dump all blockchain operations and types. Used by the UI. | Tool | Old | `./js_operation_serializer`\n[size_checker](size_checker) | Size Checker | Return wire size average in bytes of all the operations.  | Tool | Old | `./size_checker`\n[cat-parts](build_helpers/cat-parts.cpp) | Cat parts | Used to create `hardfork.hpp` from individual files. | Tool | Active | `./cat-parts`\n[check_reflect](build_helpers/check_reflect.py) | Check reflect | Check reflected fields automatically(https://github.com/cryptonomex/graphene/issues/562) | Tool | Old | `doxygen;cp -rf doxygen programs/build_helpers; ./check_reflect.py`\n[member_enumerator](build_helpers/member_enumerator.cpp) | Member enumerator | | Tool | Deprecated | `./member_enumerator`\n[get_dev_key](genesis_util/get_dev_key.cpp) | Get Dev Key | Create public, private and address keys. Useful in private testnets, `genesis.json` files, new blockchain creation and others. | Tool | Active | `/programs/genesis_util/get_dev_key -h`\n[genesis_util](genesis_util) | Genesis Utils | Other utilities for genesis creation. | Tool | Old |\n[network_mapper](network_mapper) | Network Mapper | Generates .DOT file that can be rendered by graphviz to make images of node connectivity. | Tool | Experimental | `./programs/network_mapper/network_mapper`\n"
  },
  {
    "path": "programs/build_helpers/CMakeLists.txt",
    "content": "get_target_property(GRAPHENE_CHAIN_SOURCE graphene_chain SOURCE_DIR)\nget_target_property(GRAPHENE_CHAIN_BIN graphene_chain BINARY_DIR)\n\n# This is here only to support CMake < 3.4\nif (NOT GRAPHENE_CHAIN_SOURCE)\n  set(GRAPHENE_CHAIN_SOURCE ${GRAPHENE_CHAIN_SOURCE_LEGACY})\nendif()\n# This is here only to support CMake < 3.4\nif (NOT GRAPHENE_CHAIN_BIN)\n  set(GRAPHENE_CHAIN_BIN ${GRAPHENE_CHAIN_BIN_LEGACY})\nendif()\n\nadd_custom_target( build_hardfork_hpp\n  COMMAND\n    ${CMAKE_COMMAND}\n    -DINIT_BINARY_DIR=${GRAPHENE_CHAIN_BIN}\n    -DINIT_SOURCE_DIR=${GRAPHENE_CHAIN_SOURCE}\n    -P ${CMAKE_CURRENT_SOURCE_DIR}/cat-parts.cmake\n)\n\nadd_executable( member_enumerator member_enumerator.cpp )\nif( UNIX AND NOT APPLE )\n  set( rt_library rt )\nendif()\n\n# we only actually need Boost, but link against FC for now so we don't duplicate it.\ntarget_link_libraries( member_enumerator PRIVATE fc graphene_app graphene_net graphene_chain graphene_egenesis_brief\n                                                 graphene_utilities graphene_wallet ${CMAKE_DL_LIBS}\n                                                 ${PLATFORM_SPECIFIC_LIBS} )\n"
  },
  {
    "path": "programs/build_helpers/build_and_test",
    "content": "#!/bin/bash\nset -e\n\nprograms/build_helpers/buildstep -s 3500\nccache -s\nprograms/build_helpers/buildstep Prepare 1 \"sed -i '/tests/d' libraries/fc/CMakeLists.txt\"\nprograms/build_helpers/buildstep cmake 5 \"cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON .\"\nprograms/build_helpers/buildstep make.some.targets 2400 \"make -j 2 witness_node cli_wallet chain_test cli_test js_operation_serializer get_dev_key network_mapper\"\nprograms/build_helpers/buildstep make.others 600 \"make\"\nset -o pipefail\nprograms/build_helpers/buildstep run.chain_test 600 \"libraries/fc/tests/run-parallel-tests.sh tests/chain_test\"\nprograms/build_helpers/buildstep run.cli_test 180 \"libraries/fc/tests/run-parallel-tests.sh tests/cli_test\"\nprograms/build_helpers/buildstep end 0\nccache -s\n\n"
  },
  {
    "path": "programs/build_helpers/build_for_cache",
    "content": "#!/bin/bash\nset -e\n\nprograms/build_helpers/buildstep -s 3500\nccache -s\nprograms/build_helpers/buildstep Prepare 1 \"sed -i '/tests/d' libraries/fc/CMakeLists.txt\"\nprograms/build_helpers/buildstep cmake 5 \"cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON .\"\nprograms/build_helpers/buildstep make.fc 230 \"make -j 2 fc\"\nprograms/build_helpers/buildstep make.custom_auths 700 \"make -j 1 graphene_protocol_custom_auths\"\nprograms/build_helpers/buildstep make.protocol 250 \"make -j 2 graphene_protocol\"\nprograms/build_helpers/buildstep make.chain 450 \"make -j 2 graphene_chain\"\nprograms/build_helpers/buildstep make.node 600 \"make -j 2 witness_node\"\nprograms/build_helpers/buildstep make.cli 500 \"make -j 2 cli_wallet\"\nprograms/build_helpers/buildstep end 0\nccache -s\n"
  },
  {
    "path": "programs/build_helpers/buildstep",
    "content": "#!/bin/sh\n\nusage () {\n    echo Usage:\n    echo \"  ${0##*/} [-h | --help] \t\tDisplay this help message\"\n    echo \"  ${0##*/} -s | --start <max-time>\tInitialize timing\"\n    echo \"  ${0##*/} <name> <est> <cmd>\"\n    echo \"The last form executes build step <name> consisting of shell <cmd>\"\n    echo \"if <est>imated time is still available, otherwise it fails fast.\"\n    echo \"<max-time> and <est> must be specified in seconds.\"\n    exit $1\n}\n\nif [ \"$#\" = 0 -o \"$1\" = \"--help\" -o \"$1\" = \"-h\" ]; then\n    usage `test \"$#\" = 1; echo $?`\nfi\n\nNOW=\"$(date +%s)\"\n\nif [ \"$1\" = \"--start\" -o \"$1\" = \"-s\" ]; then\n    if [ \"$#\" != 2 ]; then\n\tusage 1\n    fi\n    echo \"$2 $NOW\" >_start_time\n    echo \"Starting at $(date --date=@$NOW)\"\n    exit 0\nfi\n\nNAME=\"$1\"\nEST=\"$2\"\nCMD=\"$3\"\n\nif [ ! -r _start_time ]; then\n    echo \"Need to initialize with '$0 -s <max-time>' first!\" 1>&2\n    exit 1\nfi\n\nread max begin prev_name prev_begin <_start_time\n\nif [ \"$prev_name\" != \"\" ]; then\n    echo \"'$prev_name' took $(($NOW - $prev_begin))s\"\nfi\n\nif [ \"$CMD\" != \"\" ]; then\n    if [ $(($NOW - $begin + $EST)) -lt $max ]; then\n\techo \"Running '$NAME' at $NOW...\"\n\techo \"sh -c '$CMD'\"\n\techo \"$max $begin $NAME $NOW\" >_start_time\n\texec bash -c \"$CMD\"\n    fi\n    echo \"$(($begin + $max - $NOW))s left - insufficient to run '$NAME', exiting!\" 1>&2\n    exit 1\nfi\n\nexit 0\n"
  },
  {
    "path": "programs/build_helpers/check_reflect.py",
    "content": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport re\nimport xml.etree.ElementTree as etree\n\ndef process_node(path, node):\n    \"\"\"\n    if node.tag == \"TestCase\":\n        if node.attrib.get(\"result\", \"UNKNOWN\") != \"passed\":\n            failset.add(node)\n        return\n    if node.tag in [\"TestResult\", \"TestSuite\"]:\n        for child in node:\n            cpath = path+\"/\"+child.attrib[\"name\"]\n            process_node(cpath, child)\n        return\n    \"\"\"\n    #print(\"unknown node\", node.tag)\n    print(node.tag)\n    return\n\nname2members_doxygen = {}\n\ndef process_class_node(node):\n    result = {\"name\":\"\", \"vmembers\":[]}\n    for child in node:\n        if child.tag == \"name\":\n            result[\"name\"] = child.text\n        elif child.tag == \"member\":\n            kind = child.attrib.get(\"kind\")\n            if kind == \"variable\":\n                result[\"vmembers\"].append(child[0].text)\n    name2members_doxygen[result[\"name\"]] = result[\"vmembers\"]\n    return\n\ntree = etree.parse(\"doxygen/xml/index.xml\")\nroot = tree.getroot()\nfor child in root:\n    if (child.tag == \"compound\") and (child.attrib.get(\"kind\") in [\"struct\", \"class\"]):\n        process_class_node(child)\n\ns_static_names = set([\"space_id\", \"type_id\"])\n\nfor k, v in name2members_doxygen.items():\n    name2members_doxygen[k] = [x for x in v if x not in s_static_names]\n\n#with open(\"stuff/member_enumerator.out\", \"r\") as f:\n#    name2members_fc = json.load(f)\n\n# scan for FC_REFLECT( graphene::... in all cpp,hpp files under libraries/ programs/ tests/\n\nre_reflect = re.compile(r\"\"\"\nFC_REFLECT\\s*[(]\n\\s*(graphene::[a-zA-Z0-9_:]+)\n\\s*,\n((?:\\s*[(]\\s*[a-zA-Z0-9_]+\\s*[)])*)\n\"\"\", re.VERBOSE)\n\nre_reflect_derived = re.compile(r\"\"\"\nFC_REFLECT_DERIVED\\s*[(]\n\\s*(graphene::[a-zA-Z0-9_:]+)\n\\s*,\n\\s*[(]\\s*(graphene::[a-zA-Z0-9_:]+)\\s*[)]\n\\s*,\n((?:\\s*[(]\\s*[a-zA-Z0-9_]+\\s*[)])*)\n\"\"\", re.VERBOSE)\n\nre_bubble_item = re.compile(r\"\\s*[(]\\s*([a-zA-Z0-9_]+)\\s*\")\n\ndef bubble_list(x):\n    return [re_bubble_item.match(e).group(1) for e in x.split(\")\")[:-1]]\n\nname2members_re = {}\n\nfor root, dirs, files in os.walk(\".\"):\n    if root == \".\":\n        dirs[:] = [\"libraries\", \"programs\", \"tests\"]\n    for filename in files:\n        if not (filename.endswith(\".cpp\") or filename.endswith(\".hpp\")):\n            continue\n        try:\n            with open( os.path.join(root, filename), \"r\" ) as f:\n                content = f.read()\n                for m in re_reflect.finditer(content):\n                    cname = m.group(1)\n                    members = bubble_list(m.group(2))\n                    name2members_re[cname] = members\n                    if cname.endswith(\"_object\"):\n                       print(\"FC_REFLECT on {} should be FC_REFLECT_DERIVED\".format(cname))\n                for m in re_reflect_derived.finditer(content):\n                    cname = m.group(1)\n                    members = bubble_list(m.group(3))\n                    name2members_re[cname] = members\n        except OSError as e:\n            pass\n\ndef validate_members(name2members_ref, name2members_test):\n    ok_items = []\n    ne_items = []\n    error_items = []\n\n    for name in sorted(name2members_ref.keys()):\n        if name not in name2members_test:\n            ne_items.append(name)\n        elif sorted(name2members_ref[name]) != sorted(name2members_test[name]):\n            error_items.append(name)\n            print(\"\")\n            print(\"error in\", name)\n            print(\"doxygen:\", name2members_ref[name])\n            print(\"fc     :\", name2members_test[name])\n        else:\n            ok_items.append(name)\n    return\n\n\"\"\"\nprint(\"\")\nprint(\"ok:\")\nfor item in ok_items:\n    print(item)\n\nprint(\"\")\nprint(\"not evaluated:\")\nfor item in ne_items:\n    print(item)\n\nprint(\"\")\nprint(\"error:\")\nfor item in error_items:\n    print(item)\n\"\"\"\n\nvalidate_members(name2members_doxygen, name2members_re)\n"
  },
  {
    "path": "programs/build_helpers/make_with_sonar",
    "content": "#!/bin/sh\n\nOUT_DIR=\"$1\"\nshift\nif which build-wrapper-linux-x86-64 >/dev/null; then\n    exec build-wrapper-linux-x86-64 --out-dir \"$OUT_DIR\" make \"$@\"\nfi\nexec make \"$@\"\n"
  },
  {
    "path": "programs/build_helpers/member_enumerator.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n *\n * 1. Any modified source or binaries are used only with the BitShares network.\n *\n * 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n *\n * 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n */\n#include <graphene/protocol/block.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/market_evaluator.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <iostream>\n\nusing namespace graphene::chain;\n\nnamespace graphene { namespace member_enumerator {\n\nstruct class_processor\n{\n   explicit class_processor( std::map< std::string, std::vector< std::string > >& r ) : result(r) {}\n\n   template< typename T >\n   void process_class( const T* dummy );\n\n   template< typename... T >\n   void process_class( const static_variant< T... >* dummy );\n\n   template< typename T >\n   void process_class( const std::vector< T >* dummy );\n\n   template< typename K, typename V >\n   void process_class( const std::map< K, V >* dummy );\n\n   template< typename T >\n   void process_class( const fc::flat_set< T >* dummy );\n\n   template< typename K, typename V >\n   void process_class( const fc::flat_map< K, V >* dummy );\n\n   template< typename T >\n   void process_class( const fc::optional< T >* dummy );\n\n   template< typename T >\n   static void process_class( std::map< std::string, std::vector< std::string > >& result );\n\n   std::map< std::string, std::vector< std::string > >& result;\n};\n\ntemplate< typename T >\nstruct member_visitor\n{\n   member_visitor( class_processor* p ) : proc(p) {}\n\n   template<typename Member, class Class, Member (Class::*member)>\n   void operator()( const char* name )const\n   {\n      members.emplace_back( name );\n      proc->process_class( (const Member*) nullptr );\n   }\n\n   class_processor* proc;\n   mutable std::vector< std::string > members;\n};\n\nstruct static_variant_visitor\n{\n   explicit static_variant_visitor( class_processor* p ) : proc(p) {}\n\n   typedef void result_type;\n\n   template<typename T>\n   void operator()( const T& element )const\n   {\n      proc->process_class( (const T*) nullptr );\n   }\n\n   class_processor* proc;\n};\n\ntemplate< typename... T >\nvoid class_processor::process_class( const static_variant< T... >* dummy )\n{\n   static_variant<T...> dummy2;\n   static_variant_visitor vtor( this );\n\n   for( size_t w=0; w<dummy2.count(); w++ )\n   {\n      dummy2.set_which(w);\n      dummy2.visit( vtor );\n   }\n}\n\ntemplate<typename IsReflected=std::false_type>\nstruct if_reflected\n{\n   template< typename T >\n   static void process_class( class_processor* proc, const T* dummy )\n   {\n      std::string tname = fc::get_typename<T>::name();\n      std::cerr << \"skipping non-reflected class \" << tname << std::endl;\n   }\n};\n\ntemplate<>\nstruct if_reflected<std::true_type>\n{\n   template< typename T >\n   static void process_class( class_processor* proc, const T* dummy )\n   {\n      std::string tname = fc::get_typename<T>::name();\n      if( proc->result.find( tname ) != proc->result.end() )\n         return;\n      ilog( \"processing class ${c}\", (\"c\", tname) );\n      // need this to keep from recursing on same class\n      proc->result.emplace( tname, std::vector< std::string >() );\n\n      member_visitor<T> vtor( proc );\n      fc::reflector<T>::visit( vtor );\n      ilog( \"members of class ${c} are ${m}\", (\"c\", tname)(\"m\", vtor.members) );\n      proc->result[tname] = vtor.members;\n   }\n};\n\ntemplate< typename T >\nvoid class_processor::process_class( const T* dummy )\n{\n   if_reflected< typename fc::reflector<T>::is_defined >::process_class( this, dummy );\n}\n\ntemplate< typename T >\nvoid class_processor::process_class( const std::vector< T >* dummy )\n{\n   process_class( (T*) nullptr );\n}\n\ntemplate< typename K, typename V >\nvoid class_processor::process_class( const std::map< K, V >* dummy )\n{\n   process_class( (K*) nullptr );\n   process_class( (V*) nullptr );\n}\n\ntemplate< typename T >\nvoid class_processor::process_class( const fc::flat_set< T >* dummy )\n{\n   process_class( (T*) nullptr );\n}\n\ntemplate< typename K, typename V >\nvoid class_processor::process_class( const fc::flat_map< K, V >* dummy )\n{\n   process_class( (K*) nullptr );\n   process_class( (V*) nullptr );\n}\n\ntemplate< typename T >\nvoid class_processor::process_class( const fc::optional< T >* dummy )\n{\n   process_class( (T*) nullptr );\n}\n\ntemplate< typename T >\nvoid class_processor::process_class( std::map< std::string, std::vector< std::string > >& result )\n{\n   class_processor proc(result);\n   proc.process_class( (T*) nullptr );\n}\n\n} }\n\nint main( int argc, char** argv )\n{\n   try\n   {\n      std::map< std::string, std::vector< std::string > > result;\n      graphene::member_enumerator::class_processor::process_class<signed_block>(result);\n\n      fc::mutable_variant_object mvo;\n      for( const std::pair< std::string, std::vector< std::string > >& e : result )\n      {\n         variant v;\n         to_variant( e.second, v , 1);\n         mvo.set( e.first, v );\n      }\n\n      std::cout << fc::json::to_string( mvo ) << std::endl;\n   }\n   catch ( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n   }\n   return 0;\n}\n"
  },
  {
    "path": "programs/build_helpers/scan_with_sonar_step_1",
    "content": "#!/bin/bash\nset -e\n\nprograms/build_helpers/buildstep -s 3500\nccache -s\nprograms/build_helpers/buildstep Prepare 1 \"sed -i '/tests/d' libraries/fc/CMakeLists.txt\"\nprograms/build_helpers/buildstep cmake 5 \"cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON .\"\nprograms/build_helpers/buildstep make.programs 1800 \"programs/build_helpers/make_with_sonar bw-output -j 2 witness_node cli_wallet js_operation_serializer get_dev_key network_mapper app_test chain_test cli_test\"\nset -o pipefail\nprograms/build_helpers/buildstep prepare.sonar 20 \"find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d -print | while read d; do gcov -o \\\"\\$d\\\" \\\"\\${d/CMakeFiles*.dir//}\\\"/*.cpp; done >/dev/null; programs/build_helpers/set_sonar_branch sonar-project.properties\"\ndu -hs sonar_cache\n# The first pass, skip some files. This will remove the skipped files from the cache, but is an acceptable trade-off\nprograms/build_helpers/buildstep prepare.sonar.part1 1 \"cp sonar-project.properties sonar-project.properties.bak; sed -i '/sonar\\.exclusions=/d;s/#sonar\\.exclusions.part1/sonar.exclusions/' sonar-project.properties\"\nprograms/build_helpers/buildstep run.sonar.part1 1500 \"which sonar-scanner && sonar-scanner\"\nprograms/build_helpers/buildstep prepare.sonar.full 1 \"cp sonar-project.properties.bak sonar-project.properties\"\ndu -hs sonar_cache\nprograms/build_helpers/buildstep end 0\nccache -s\n\n"
  },
  {
    "path": "programs/build_helpers/scan_with_sonar_step_2",
    "content": "#!/bin/bash\nset -e\n\nprograms/build_helpers/buildstep -s 3500\nccache -s\nprograms/build_helpers/buildstep Prepare 1 \"sed -i '/tests/d' libraries/fc/CMakeLists.txt\"\nprograms/build_helpers/buildstep cmake 5 \"cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON .\"\nprograms/build_helpers/buildstep make.programs 1800 \"programs/build_helpers/make_with_sonar bw-output -j 2 witness_node cli_wallet js_operation_serializer get_dev_key network_mapper app_test chain_test cli_test\"\nset -o pipefail\nprograms/build_helpers/buildstep prepare.sonar 20 \"find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d -print | while read d; do gcov -o \\\"\\$d\\\" \\\"\\${d/CMakeFiles*.dir//}\\\"/*.cpp; done >/dev/null; programs/build_helpers/set_sonar_branch sonar-project.properties\"\ndu -hs sonar_cache\n# The first pass, skip some files. This will remove the skipped files from the cache, but is an acceptable trade-off\nprograms/build_helpers/buildstep prepare.sonar.part1 1 \"cp sonar-project.properties sonar-project.properties.bak; sed -i '/sonar\\.exclusions=/d;s/#sonar\\.exclusions.part1/sonar.exclusions/' sonar-project.properties\"\nprograms/build_helpers/buildstep run.sonar.part1 1500 \"which sonar-scanner && sonar-scanner\"\nprograms/build_helpers/buildstep prepare.sonar.full 1 \"cp sonar-project.properties.bak sonar-project.properties\"\ndu -hs sonar_cache\n# The second pass, skip fewer files\nprograms/build_helpers/buildstep prepare.sonar.part2 1 \"cp sonar-project.properties sonar-project.properties.bak; sed -i '/sonar\\.exclusions=/d;s/#sonar\\.exclusions.part2/sonar.exclusions/' sonar-project.properties\"\nprograms/build_helpers/buildstep run.sonar.part2 1500 \"which sonar-scanner && sonar-scanner\"\nprograms/build_helpers/buildstep prepare.sonar.full2 1 \"cp sonar-project.properties.bak sonar-project.properties\"\ndu -hs sonar_cache\nprograms/build_helpers/buildstep end 0\nccache -s\n\n"
  },
  {
    "path": "programs/build_helpers/scan_with_sonar_step_3",
    "content": "#!/bin/bash\nset -e\n\nprograms/build_helpers/buildstep -s 3500\nccache -s\nprograms/build_helpers/buildstep Prepare 1 \"sed -i '/tests/d' libraries/fc/CMakeLists.txt\"\nprograms/build_helpers/buildstep cmake 5 \"cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON .\"\nprograms/build_helpers/buildstep make.programs 1800 \"programs/build_helpers/make_with_sonar bw-output -j 2 witness_node cli_wallet js_operation_serializer get_dev_key network_mapper app_test chain_test cli_test\"\nset -o pipefail\nprograms/build_helpers/buildstep run.chain_test 600 \"libraries/fc/tests/run-parallel-tests.sh tests/chain_test\"\nprograms/build_helpers/buildstep run.cli_test 180 \"libraries/fc/tests/run-parallel-tests.sh tests/cli_test\"\nprograms/build_helpers/buildstep run.app_test 180 \"tests/app_test\"\nprograms/build_helpers/buildstep prepare.sonar 20 \"find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d -print | while read d; do gcov -o \\\"\\$d\\\" \\\"\\${d/CMakeFiles*.dir//}\\\"/*.cpp; done >/dev/null; programs/build_helpers/set_sonar_branch sonar-project.properties\"\ndu -hs sonar_cache\n# The first pass is skipped here\n# The second pass, skip fewer files than the first pass. This will remove the skipped files from the cache, but is an acceptable trade-off\nprograms/build_helpers/buildstep prepare.sonar.part2 1 \"cp sonar-project.properties sonar-project.properties.bak; sed -i '/sonar\\.exclusions=/d;s/#sonar\\.exclusions.part2/sonar.exclusions/' sonar-project.properties\"\nprograms/build_helpers/buildstep run.sonar.part2 1500 \"which sonar-scanner && sonar-scanner\"\nprograms/build_helpers/buildstep prepare.sonar.full 1 \"cp sonar-project.properties.bak sonar-project.properties\"\ndu -hs sonar_cache\n# The third pass, scan all files\nprograms/build_helpers/buildstep run.sonar.full 1500 \"which sonar-scanner && sonar-scanner\"\ndu -hs sonar_cache\nprograms/build_helpers/buildstep end 0\nccache -s\n\n"
  },
  {
    "path": "programs/build_helpers/set_sonar_branch",
    "content": "#!/bin/sh\n\n# Relevant variables set by travis:\n# TRAVIS_BRANCH:\n# * for push builds, or builds not triggered by a pull request, this is the\n#   name of the branch.\n# * for builds triggered by a pull request this is the name of the branch\n#   targeted by the pull request.\n# * for builds triggered by a tag, this is the same as the name of the tag\n#   (see TRAVIS_TAG).\n# TRAVIS_PULL_REQUEST: The pull request number if the current job is a pull\n#   request, “false” if it’s not a pull request.\n# TRAVIS_TAG: If the current build is for a git tag, this variable is set to\n#   the tag’s name.\n\nif [ \"$#\" != 1 ]; then\n    echo \"Usage: $0 <sonar-properties-file>\" 1>&2\n    exit 1\nfi\n\nclear_branch () {\n    sed -i '/sonar\\.branch/d' \"$1\"\n}\n\nORIGINAL_TARGET=\"$( grep 'sonar\\.branch\\.target' \"$1\" | sed 's=^.*[:=] *==' )\"\n\nBRANCH=\nTARGET=\"$ORIGINAL_TARGET\"\n\nif [ -n \"$TRAVIS_PULL_REQUEST\" -a \"$TRAVIS_PULL_REQUEST\" != \"false\" ]; then\n    # PRs work per default, remove sonar.branch.* since they only work with sonar.pullrequest.*\n    echo \"Detected PR '$TRAVIS_PULL_REQUEST'\"\n    TARGET=\nelif [ -n \"$TRAVIS_TAG\" ]; then\n    # Tag build is either master or testnet\n    echo \"Detected tag '$TRAVIS_TAG'\"\n    BRANCH=\"$TRAVIS_BRANCH\"\n    case \"$TRAVIS_TAG\" in\n\t*test*) TARGET=testnet; ;;\n\t*)\tTARGET=master; ;;\n    esac\nelse\n    BRANCH=\"$TRAVIS_BRANCH\"\n    case \"$TRAVIS_BRANCH\" in\n\tmaster|develop|testnet|hardfork)\n\t    # Long-lived branches stand for themselves, no target\n\t    echo \"Detected long-lived branch '$TRAVIS_BRANCH'\"\n\t    TARGET=\n\t    ;;\n\t*test*release*)\n\t    # Testnet release branch will be merged into testnet\n\t    echo \"Detected testnet release branch '$TRAVIS_BRANCH'\"\n\t    TARGET=testnet\n\t    ;;\n\t*release*)\n\t    # Release branch will be merged into default (master)\n\t    echo \"Detected release branch '$TRAVIS_BRANCH'\"\n\t    TARGET=master\n\t    ;;\n\t*)\n\t    # All other branches should have sonar.branch.target in their\n\t    # sonar.properties, leave it unchanged\n\t    echo \"Detected normal branch '$TRAVIS_BRANCH'\"\n    esac\nfi\n\necho \"Branch '$BRANCH', target '$TARGET'\"\n\ngit fetch --no-tags --unshallow\ngit fetch --no-tags origin +refs/heads/$TRAVIS_BRANCH:refs/remotes/origin/$TRAVIS_BRANCH\nif [ -n \"$TARGET\" ]; then\n    git fetch --no-tags origin +refs/heads/$TARGET:refs/remotes/origin/$TARGET\nfi\n\nif [ \"x$TARGET\" != \"x$ORIGINAL_TARGET\" ]; then\n    clear_branch \"$1\"\n    if [ -n \"$TARGET\" ]; then\n        echo \"sonar.branch.target=$TARGET\" >>\"$1\"\n    fi\nfi\nif [ -n \"$BRANCH\" ]; then\n    echo \"sonar.branch.name=$BRANCH\" >>\"$1\"\nfi\n\nexit 0\n"
  },
  {
    "path": "programs/cli_wallet/CMakeLists.txt",
    "content": "add_executable( cli_wallet main.cpp )\nif( UNIX AND NOT APPLE )\n  set(rt_library rt )\nendif()\n\nfind_package( Gperftools QUIET )\nif( GPERFTOOLS_FOUND )\n    message( STATUS \"Found gperftools; compiling cli_wallet with TCMalloc\")\n    list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )\nendif()\n\ntarget_link_libraries( cli_wallet\n      PRIVATE graphene_app graphene_net graphene_chain graphene_egenesis_brief graphene_utilities graphene_wallet fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\nif(MSVC)\n  set_source_files_properties( main.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   cli_wallet\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "programs/cli_wallet/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <algorithm>\n#include <iomanip>\n#include <iostream>\n#include <iterator>\n\n#include <fc/io/json.hpp>\n#include <fc/io/stdio.hpp>\n#include <fc/network/http/websocket.hpp>\n#include <fc/rpc/cli.hpp>\n#include <fc/rpc/websocket_api.hpp>\n#include <fc/stacktrace.hpp>\n\n#include <graphene/app/api.hpp>\n#include <graphene/chain/config.hpp>\n#include <graphene/egenesis/egenesis.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n#include <graphene/wallet/wallet.hpp>\n\n#include <fc/interprocess/signals.hpp>\n#include <boost/program_options.hpp>\n\n#include <fc/log/console_appender.hpp>\n#include <fc/log/file_appender.hpp>\n#include <fc/log/logger.hpp>\n#include <fc/log/logger_config.hpp>\n\n#include <graphene/utilities/git_revision.hpp>\n#include <boost/version.hpp>\n#include <boost/algorithm/string/replace.hpp>\n#include <websocketpp/version.hpp>\n\n#ifdef WIN32\n# include <signal.h>\n#else\n# include <csignal>\n#endif\n\nusing namespace graphene::app;\nusing namespace graphene::chain;\nusing namespace graphene::utilities;\nusing namespace graphene::wallet;\nusing namespace std;\nnamespace bpo = boost::program_options;\n\nfc::log_level string_to_level(string level)\n{\n   fc::log_level result;\n   if(level == \"info\")\n      result = fc::log_level::info;\n   else if(level == \"debug\")\n      result = fc::log_level::debug;\n   else if(level == \"warn\")\n      result = fc::log_level::warn;\n   else if(level == \"error\")\n      result = fc::log_level::error;\n   else if(level == \"all\")\n      result = fc::log_level::all;\n   else\n      FC_THROW(\"Log level not allowed. Allowed levels are info, debug, warn, error and all.\");\n\n   return result;\n}\n\nvoid setup_logging(string console_level, bool file_logger, string file_level, string file_name)\n{\n   fc::logging_config cfg;\n\n   // console logger\n   fc::console_appender::config console_appender_config;\n   console_appender_config.level_colors.emplace_back(\n         fc::console_appender::level_color(fc::log_level::debug,\n         fc::console_appender::color::green));\n   console_appender_config.level_colors.emplace_back(\n         fc::console_appender::level_color(fc::log_level::warn,\n         fc::console_appender::color::brown));\n   console_appender_config.level_colors.emplace_back(\n         fc::console_appender::level_color(fc::log_level::error,\n         fc::console_appender::color::red));\n   cfg.appenders.push_back(fc::appender_config( \"default\", \"console\", fc::variant(console_appender_config, 20)));\n   cfg.loggers = { fc::logger_config(\"default\"), fc::logger_config( \"rpc\") };\n   cfg.loggers.front().level = string_to_level(console_level);\n   cfg.loggers.front().appenders = {\"default\"};\n\n   // file logger\n   if(file_logger) {\n      fc::path data_dir;\n      fc::path log_dir = data_dir / \"cli_wallet_logs\";\n      fc::file_appender::config ac;\n      ac.filename             = log_dir / file_name;\n      ac.flush                = true;\n      ac.rotate               = true;\n      ac.rotation_interval    = fc::hours( 1 );\n      ac.rotation_limit       = fc::days( 1 );\n      cfg.appenders.push_back(fc::appender_config( \"rpc\", \"file\", fc::variant(ac, 5)));\n      cfg.loggers.back().level = string_to_level(file_level);\n      cfg.loggers.back().appenders = {\"rpc\"};\n      fc::configure_logging( cfg );\n      ilog (\"Logging RPC to file: \" + (ac.filename).preferred_string());\n   }\n}\n\nint main( int argc, char** argv )\n{\n   fc::print_stacktrace_on_segfault();\n   try {\n\n      boost::program_options::options_description opts;\n         opts.add_options()\n         (\"help,h\", \"Print this help message and exit.\")\n         (\"server-rpc-endpoint,s\", bpo::value<string>()->implicit_value(\"ws://127.0.0.1:8090\"), \"Server websocket RPC endpoint\")\n         (\"server-rpc-user,u\", bpo::value<string>(), \"Server Username\")\n         (\"server-rpc-password,p\", bpo::value<string>(), \"Server Password\")\n         (\"rpc-endpoint,r\", bpo::value<string>()->implicit_value(\"127.0.0.1:8091\"),\n               \"Endpoint for wallet websocket RPC to listen on (DEPRECATED, use rpc-http-endpoint instead)\")\n         (\"rpc-tls-endpoint,t\", bpo::value<string>()->implicit_value(\"127.0.0.1:8092\"),\n               \"Endpoint for wallet websocket TLS RPC to listen on\")\n         (\"rpc-tls-certificate,c\", bpo::value<string>()->implicit_value(\"server.pem\"),\n               \"PEM certificate for wallet websocket TLS RPC\")\n         (\"rpc-http-endpoint,H\", bpo::value<string>()->implicit_value(\"127.0.0.1:8093\"),\n               \"Endpoint for wallet HTTP and websocket RPC to listen on\")\n         (\"daemon,d\", \"Run the wallet in daemon mode\" )\n         (\"wallet-file,w\", bpo::value<string>()->implicit_value(\"wallet.json\"), \"wallet to load\")\n         (\"chain-id\", bpo::value<string>(), \"chain ID to connect to\")\n         (\"suggest-brain-key\", \"Suggest a safe brain key to use for creating your account\")\n         (\"logs-rpc-console-level\", bpo::value<string>()->default_value(\"info\"),\n               \"Level of console logging. Allowed levels: info, debug, warn, error, all\")\n         (\"logs-rpc-file\", bpo::value<bool>()->default_value(false), \"Turn on/off file logging\")\n         (\"logs-rpc-file-level\", bpo::value<string>()->default_value(\"debug\"),\n               \"Level of file logging. Allowed levels: info, debug, warn, error, all\")\n         (\"logs-rpc-file-name\", bpo::value<string>()->default_value(\"rpc.log\"), \"File name for file rpc logs\")\n         (\"version,v\", \"Display version information\");\n\n      bpo::variables_map options;\n\n      bpo::store( bpo::parse_command_line(argc, argv, opts), options );\n\n      if( options.count(\"help\") )\n      {\n         std::cout << opts << \"\\n\";\n         return 0;\n      }\n      if( options.count(\"version\") )\n      {\n         std::cout << \"Version: \" << graphene::utilities::git_revision_description << \"\\n\";\n         std::cout << \"SHA: \" << graphene::utilities::git_revision_sha << \"\\n\";\n         std::cout << \"Timestamp: \" << fc::get_approximate_relative_time_string(fc::time_point_sec(graphene::utilities::git_revision_unix_timestamp)) << \"\\n\";\n         std::cout << \"SSL: \" << OPENSSL_VERSION_TEXT << \"\\n\";\n         std::cout << \"Boost: \" << boost::replace_all_copy(std::string(BOOST_LIB_VERSION), \"_\", \".\") << \"\\n\";\n         std::cout << \"Websocket++: \" << websocketpp::major_version << \".\" << websocketpp::minor_version << \".\" << websocketpp::patch_version << \"\\n\";\n         return 0;\n      }\n      if( options.count(\"suggest-brain-key\") )\n      {\n         auto keyinfo = graphene::wallet::utility::suggest_brain_key();\n         string data = fc::json::to_pretty_string( keyinfo );\n         std::cout << data.c_str() << std::endl;\n         return 0;\n      }\n\n      setup_logging(options.at(\"logs-rpc-console-level\").as<string>(),options.at(\"logs-rpc-file\").as<bool>(),\n            options.at(\"logs-rpc-file-level\").as<string>(), options.at(\"logs-rpc-file-name\").as<string>());\n\n      // TODO:  We read wallet_data twice, once in main() to grab the\n      //    socket info, again in wallet_api when we do\n      //    load_wallet_file().  Seems like this could be better\n      //    designed.\n      //\n      wallet_data wdata;\n\n      fc::path wallet_file( options.count(\"wallet-file\") ? options.at(\"wallet-file\").as<string>() : \"wallet.json\");\n      if( fc::exists( wallet_file ) )\n      {\n         wdata = fc::json::from_file( wallet_file ).as<wallet_data>( GRAPHENE_MAX_NESTED_OBJECTS );\n         if( options.count(\"chain-id\") )\n         {\n            // the --chain-id on the CLI must match the chain ID embedded in the wallet file\n            if( chain_id_type(options.at(\"chain-id\").as<std::string>()) != wdata.chain_id )\n            {\n               std::cout << \"Chain ID in wallet file does not match specified chain ID\\n\";\n               return 1;\n            }\n         }\n      }\n      else\n      {\n         if( options.count(\"chain-id\") )\n         {\n            wdata.chain_id = chain_id_type(options.at(\"chain-id\").as<std::string>());\n            std::cout << \"Starting a new wallet with chain ID \" << wdata.chain_id.str() << \" (from CLI)\\n\";\n         }\n         else\n         {\n            wdata.chain_id = graphene::egenesis::get_egenesis_chain_id();\n            std::cout << \"Starting a new wallet with chain ID \" << wdata.chain_id.str() << \" (from egenesis)\\n\";\n         }\n      }\n\n      // but allow CLI to override\n      if( options.count(\"server-rpc-endpoint\") )\n         wdata.ws_server = options.at(\"server-rpc-endpoint\").as<std::string>();\n      if( options.count(\"server-rpc-user\") )\n         wdata.ws_user = options.at(\"server-rpc-user\").as<std::string>();\n      if( options.count(\"server-rpc-password\") )\n         wdata.ws_password = options.at(\"server-rpc-password\").as<std::string>();\n\n      fc::http::websocket_client client;\n      idump((wdata.ws_server));\n      auto con  = client.connect( wdata.ws_server );\n      auto apic = std::make_shared<fc::rpc::websocket_api_connection>(con, GRAPHENE_MAX_NESTED_OBJECTS);\n\n      auto remote_api = apic->get_remote_api< login_api >(1);\n      edump((wdata.ws_user)(wdata.ws_password) );\n      FC_ASSERT( remote_api->login( wdata.ws_user, wdata.ws_password ), \"Failed to log in to API server\" );\n\n      auto wapiptr = std::make_shared<wallet_api>( wdata, remote_api );\n      wapiptr->set_wallet_filename( wallet_file.generic_string() );\n      wapiptr->load_wallet_file();\n\n      fc::api<wallet_api> wapi(wapiptr);\n\n      std::shared_ptr<fc::http::websocket_server> _websocket_server;\n      if( options.count(\"rpc-endpoint\") )\n      {\n         _websocket_server = std::make_shared<fc::http::websocket_server>();\n         _websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){\n            auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_MAX_NESTED_OBJECTS);\n            wsc->register_api(wapi);\n            c->set_session_data( wsc );\n         });\n         ilog( \"Listening for incoming HTTP and WS RPC requests on ${p}\", (\"p\", options.at(\"rpc-endpoint\").as<string>() ));\n         _websocket_server->listen( fc::ip::endpoint::from_string(options.at(\"rpc-endpoint\").as<string>()) );\n         _websocket_server->start_accept();\n      }\n\n      string cert_pem = \"server.pem\";\n      if( options.count( \"rpc-tls-certificate\" ) )\n         cert_pem = options.at(\"rpc-tls-certificate\").as<string>();\n\n      std::shared_ptr<fc::http::websocket_tls_server> _websocket_tls_server;\n      if( options.count(\"rpc-tls-endpoint\") )\n      {\n         _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(cert_pem);\n         _websocket_tls_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){\n            auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_MAX_NESTED_OBJECTS);\n            wsc->register_api(wapi);\n            c->set_session_data( wsc );\n         });\n         ilog( \"Listening for incoming HTTPS and WSS RPC requests on ${p}\",\n               (\"p\", options.at(\"rpc-tls-endpoint\").as<string>()) );\n         _websocket_tls_server->listen( fc::ip::endpoint::from_string(options.at(\"rpc-tls-endpoint\").as<string>()) );\n         _websocket_tls_server->start_accept();\n      }\n\n      std::shared_ptr<fc::http::websocket_server> _http_ws_server;\n      if( options.count(\"rpc-http-endpoint\" ) )\n      {\n         _http_ws_server = std::make_shared<fc::http::websocket_server>();\n         ilog( \"Listening for incoming HTTP and WS RPC requests on ${p}\",\n               (\"p\", options.at(\"rpc-http-endpoint\").as<string>()) );\n         _http_ws_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){\n            auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_MAX_NESTED_OBJECTS);\n            wsc->register_api(wapi);\n            c->set_session_data( wsc );\n         });\n         _http_ws_server->listen( fc::ip::endpoint::from_string(options.at(\"rpc-http-endpoint\").as<string>()) );\n         _http_ws_server->start_accept();\n      }\n\n      if( !options.count( \"daemon\" ) )\n      {\n         auto wallet_cli = std::make_shared<fc::rpc::cli>( GRAPHENE_MAX_NESTED_OBJECTS );\n         \n         wallet_cli->set_regex_secret(\"\\\\s*(unlock|set_password)\\\\s*\");\n\n         for( auto& name_formatter : wapiptr->get_result_formatters() )\n            wallet_cli->format_result( name_formatter.first, name_formatter.second );\n\n         std::cout << \"\\nType \\\"help\\\" for a list of available commands.\\n\";\n         std::cout << \"Type \\\"gethelp <command>\\\" for info about individual commands.\\n\\n\";\n         if( wapiptr->is_new() )\n         {\n            std::cout << \"Please use the \\\"set_password\\\" method to initialize a new wallet before continuing\\n\";\n            wallet_cli->set_prompt( \"new >>> \" );\n         }\n         else\n            wallet_cli->set_prompt( \"locked >>> \" );\n\n         boost::signals2::scoped_connection locked_connection( wapiptr->lock_changed.connect(\n            [wallet_cli](bool locked) {\n               wallet_cli->set_prompt( locked ? \"locked >>> \" : \"unlocked >>> \" );\n            }));\n\n         auto sig_set = fc::set_signal_handler( [wallet_cli](int signal) {\n            ilog( \"Captured SIGINT not in daemon mode, exiting\" );\n            fc::set_signal_handler( [](int sig) {}, SIGINT ); // reinstall an empty SIGINT handler\n            wallet_cli->cancel();\n         }, SIGINT );\n\n         fc::set_signal_handler( [wallet_cli,sig_set](int signal) {\n            ilog( \"Captured SIGTERM not in daemon mode, exiting\" );\n            sig_set->cancel();\n            fc::set_signal_handler( [](int sig) {}, SIGINT ); // reinstall an empty SIGINT handler\n            wallet_cli->cancel();\n         }, SIGTERM );\n#ifdef SIGQUIT\n         fc::set_signal_handler( [wallet_cli,sig_set](int signal) {\n            ilog( \"Captured SIGQUIT not in daemon mode, exiting\" );\n            sig_set->cancel();\n            fc::set_signal_handler( [](int sig) {}, SIGINT ); // reinstall an empty SIGINT handler\n            wallet_cli->cancel();\n         }, SIGQUIT );\n#endif\n         boost::signals2::scoped_connection closed_connection( con->closed.connect( [wallet_cli,sig_set] {\n            elog( \"Server has disconnected us.\" );\n            sig_set->cancel();\n            fc::set_signal_handler( [](int sig) {}, SIGINT ); // reinstall an empty SIGINT handler\n            wallet_cli->cancel();\n         }));\n\n         wallet_cli->register_api( wapi );\n         wallet_cli->start();\n         wallet_cli->wait();\n\n         locked_connection.disconnect();\n         closed_connection.disconnect();\n      }\n      else\n      {\n         fc::promise<int>::ptr exit_promise = fc::promise<int>::create(\"UNIX Signal Handler\");\n\n         fc::set_signal_handler( [&exit_promise](int signal) {\n            ilog( \"Captured SIGINT in daemon mode, exiting\" );\n            exit_promise->set_value(signal);\n         }, SIGINT );\n\n         fc::set_signal_handler( [&exit_promise](int signal) {\n            ilog( \"Captured SIGTERM in daemon mode, exiting\" );\n            exit_promise->set_value(signal);\n         }, SIGTERM );\n#ifdef SIGQUIT\n         fc::set_signal_handler( [&exit_promise](int signal) {\n            ilog( \"Captured SIGQUIT in daemon mode, exiting\" );\n            exit_promise->set_value(signal);\n         }, SIGQUIT );\n#endif\n         boost::signals2::scoped_connection closed_connection( con->closed.connect( [&exit_promise] {\n            elog( \"Server has disconnected us.\" );\n            exit_promise->set_value(0);\n         }));\n\n         ilog( \"Entering Daemon Mode, ^C to exit\" );\n         exit_promise->wait();\n\n         closed_connection.disconnect();\n      }\n\n      wapi->save_wallet_file(wallet_file.generic_string());\n   }\n   catch ( const fc::exception& e )\n   {\n      std::cerr << e.to_detail_string() << \"\\n\";\n      return -1;\n   }\n   return 0;\n}\n"
  },
  {
    "path": "programs/genesis_util/CMakeLists.txt",
    "content": "\nadd_executable( genesis_update genesis_update.cpp )\nif( UNIX AND NOT APPLE )\n  set(rt_library rt )\nendif()\n\ntarget_link_libraries( genesis_update\n                       PRIVATE graphene_app graphene_chain graphene_egenesis_none fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\ninstall( TARGETS\n   genesis_update\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n\nadd_executable( get_dev_key get_dev_key.cpp )\n\ntarget_link_libraries( get_dev_key\n                       PRIVATE graphene_utilities graphene_protocol fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\ninstall( TARGETS\n   get_dev_key\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n\nadd_executable( convert_address convert_address.cpp )\n\ntarget_link_libraries( convert_address\n                       PRIVATE graphene_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n"
  },
  {
    "path": "programs/genesis_util/apply_patch.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Apply a patch file to a JSON object\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-d\", \"--delta\", metavar=\"DELTA\", nargs=\"+\", help=\"list of delta file(s) to apply\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)        \n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    if opts.delta is None: \n        opts.delta = []\n    for filename in opts.delta:\n        with open(filename, \"r\") as f:\n            patch = json.load(f)\n        for k, v in patch.get(\"append\", {}).items():\n            if k not in genesis:\n                genesis[k] = []\n                sys.stderr.write(\"[WARN]  item {k} was created\\n\".format(k=k))\n            genesis[k].extend(v)\n            sys.stderr.write(\"appended {n} items to {k}\\n\".format(n=len(v), k=k))\n        for k, v in patch.get(\"replace\", {}).items():\n            genesis[k] = v\n            sys.stderr.write(\"replaced item {k}\\n\".format(k=k))\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/canonical_format.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\nif len(sys.argv) < 3:\n    print(\"syntax: \"+sys.argv[0]+\" INFILE OUTFILE\")\n    sys.exit(0)\n\nwith open(sys.argv[1], \"r\") as infile:\n    genesis = json.load(infile)\nwith open(sys.argv[2], \"w\") as outfile:\n    json.dump(genesis, outfile, separators=(',', ':'), sort_keys=True)\n"
  },
  {
    "path": "programs/genesis_util/change_asset_symbol.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Change an asset's symbol with referential integrity\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-f\", \"--from\", metavar=\"PREFIX\", default=\"\", help=\"initial prefix\")\n    parser.add_argument(\"-t\", \"--to\", metavar=\"PREFIX\", default=\"\", help=\"new prefix\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    frum = opts.__dict__[\"from\"]    # from is a language keyword and cannot be an attribute name\n\n    for asset in genesis[\"initial_assets\"]:\n        if asset[\"symbol\"] == frum:\n            asset[\"symbol\"] = opts.to\n\n    for balance in genesis[\"initial_balances\"]:\n        if balance[\"asset_symbol\"] == frum:\n            balance[\"asset_symbol\"] = opts.to\n\n    for vb in genesis[\"initial_vesting_balances\"]:\n        if balance[\"asset_symbol\"] == frum:\n            balance[\"asset_symbol\"] = opts.to\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/change_bitasset_owners.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Change initial_assets owned by the witness-account to the committee-account\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    for asset in genesis[\"initial_assets\"]:\n        if asset[\"issuer_name\"] == \"witness-account\":\n            asset[\"issuer_name\"] = \"committee-account\"\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/change_key_prefix.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Set is_prefixed=false for all asset owners\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-f\", \"--from\", metavar=\"PREFIX\", default=\"\", help=\"initial prefix\")\n    parser.add_argument(\"-t\", \"--to\", metavar=\"PREFIX\", default=\"\", help=\"new prefix\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    frum = opts.__dict__[\"from\"]    # from is a language keyword and cannot be an attribute name\n\n    def convert(k):\n        if not k.startswith(frum):\n            print(\"key {k} does not start with prefix {p}\".format(k=repr(k), p=repr(frum)))\n            raise RuntimeError()\n        return opts.to + k[len(frum):]\n\n    for account in genesis[\"initial_accounts\"]:\n        account[\"owner_key\"] = convert(account[\"owner_key\"])\n        account[\"active_key\"] = convert(account[\"active_key\"])\n\n    for asset in genesis[\"initial_assets\"]:\n        for cr in asset.get(\"collateral_records\", []):\n            cr[\"owner\"] = convert(cr[\"owner\"])\n\n    for balance in genesis[\"initial_balances\"]:\n        balance[\"owner\"] = convert(balance[\"owner\"])\n\n    for vb in genesis[\"initial_vesting_balances\"]:\n        vb[\"owner\"] = convert(vb[\"owner\"])\n\n    for witness in genesis[\"initial_witness_candidates\"]:\n        witness[\"block_signing_key\"] = convert(witness[\"block_signing_key\"])\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/convert_address.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n/**\n * Convert BTC / PTS addresses to a Graphene address.\n */\n\n#include <graphene/protocol/pts_address.hpp>\n#include <graphene/protocol/address.hpp>\n\n#include <iostream>\n#include <string>\n\nusing namespace graphene::protocol;\n\nint main(int argc, char** argv)\n{\n   // grab 0 or more whitespace-delimited PTS addresses from stdin\n   std::string s;\n   while( std::cin >> s )\n   {\n      std::cout << std::string( address( pts_address( s ) ) ) << std::endl;\n   }\n   return 0;\n}\n"
  },
  {
    "path": "programs/genesis_util/create_bloom_filter.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport hashlib\nimport json\nimport sys\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Set is_prefixed=false for all asset owners\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-n\", \"--num\", metavar=\"N\", default=3, type=int, help=\"number of hashes per key (default: 3)\")\n    parser.add_argument(\"-s\", \"--size\", metavar=\"BITS\", default=8*1048576, type=int, help=\"number of bits in filter\")\n    parser.add_argument(\"-a\", \"--algorithm\", metavar=\"NAME\", default=\"sha256\", type=str, help=\"hash algorithm (must exist in hashlib)\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    keys = set()\n\n    for account in genesis[\"initial_accounts\"]:\n        keys.add(account[\"owner_key\"])\n        keys.add(account[\"active_key\"])\n\n    for asset in genesis[\"initial_assets\"]:\n        for cr in asset.get(\"collateral_records\", []):\n            keys.add(cr[\"owner\"])\n\n    for balance in genesis[\"initial_balances\"]:\n        keys.add(balance[\"owner\"])\n\n    for vb in genesis[\"initial_vesting_balances\"]:\n        keys.add(vb[\"owner\"])\n\n    for witness in genesis[\"initial_witness_candidates\"]:\n        keys.add(witness[\"block_signing_key\"])\n\n    sys.stderr.write(\"got {n} distinct keys\\n\".format(n=len(keys)))\n\n    keys = [(str(i) + \":\" + k).encode(\"UTF-8\") for k in sorted(keys) for i in range(opts.num)]\n\n    data = bytearray((opts.size + 7) >> 3)\n\n    h = getattr(hashlib, opts.algorithm)\n    for k in keys:\n        address = int(h(k).hexdigest(), 16) % opts.size\n        data[address >> 3] |= (1 << (address & 7))\n\n    popcount = [bin(i).count(\"1\") for i in range(256)]\n    w = sum(popcount[x] for x in data)\n    sys.stderr.write(\"\"\"w={w}   o={o:.3%}   p={p:.3%}\nw: Hamming weight    o: Occupancy    p: False positive probability\n\"\"\".format(\nw=w,\no=float(w) / float(opts.size),\np=(float(w) / float(opts.size))**opts.num,\n))\n\n    if opts.output == \"-\":\n        sys.stdout.write(data)\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"wb\") as f:\n            f.write(data)\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/generate_account_patch.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport subprocess\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Generate a patch file that adds init accounts\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-a\", \"--accounts\", metavar=\"ACCOUNTS\", default=\"-\", help=\"file containing name, balances to create\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    parser.add_argument(\"-s\", \"--secret\", metavar=\"SECRET\", default=None, help=\"private key generation secret\")\n    opts = parser.parse_args()\n\n    if opts.secret is None:\n        sys.stderr.write(\"missing required parameter --secret\\n\")\n        sys.stderr.flush()\n        sys.exit(1)\n\n    with open(opts.accounts, \"r\") as f:\n        accounts = json.load(f)\n\n    initial_accounts = []\n    initial_balances = []\n    for e in accounts:\n        name = e[\"name\"]\n        owner_str = subprocess.check_output([\"programs/genesis_util/get_dev_key\", opts.secret, \"owner-\"+name]).decode(\"utf-8\")\n        active_str = subprocess.check_output([\"programs/genesis_util/get_dev_key\", opts.secret, \"active-\"+name]).decode(\"utf-8\")\n        owner = json.loads(owner_str)\n        active = json.loads(active_str)\n        initial_accounts.append({\n            \"name\" : name,\n            \"owner_key\" : owner[0][\"public_key\"],\n            \"active_key\" : active[0][\"public_key\"],\n            \"is_lifetime_member\" : True,\n            })\n        for bal in e.get(\"balances\", []):\n            bal = dict(bal)\n            bal[\"owner\"] = active[0][\"address\"]\n            initial_balances.append(bal)\n    result = {\n       \"append\" : {\n       \"initial_accounts\" : initial_accounts },\n    }\n    if len(initial_balances) > 0:\n        result[\"append\"][\"initial_balances\"] = initial_balances\n\n    if opts.output == \"-\":\n        dump_json( result, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( result, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/generate_init_config.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport subprocess\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Generate a patch file that adds init accounts\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-n\", \"--num\", metavar=\"N\", default=11, type=int, help=\"number of init witnesses\")\n    parser.add_argument(\"-w\", \"--witness\", metavar=\"N\", default=1, type=int, help=\"starting witness ID\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    parser.add_argument(\"-m\", \"--mname\", metavar=\"HOSTNAME\", default=\"\", help=\"machine name of target machine\")\n    parser.add_argument(\"-s\", \"--secret\", metavar=\"SECRET\", default=None, help=\"private key generation secret\")\n    opts = parser.parse_args()\n\n    if opts.secret is None:\n        sys.stderr.write(\"missing required parameter --secret\\n\")\n        sys.stderr.flush()\n        sys.exit(1)\n\n    out_wits = []\n    out_keys = []\n\n    for i in range(opts.num):\n        if opts.mname != \"\":\n           istr = \"wit-block-signing-\"+opts.mname+\"-\"+str(i)\n        else:\n           istr = \"wit-block-signing-\"+str(i)\n        prod_str = subprocess.check_output([\"programs/genesis_util/get_dev_key\", opts.secret, istr]).decode(\"utf-8\")\n        prod = json.loads(prod_str)\n        out_wits.append('witness-id = \"1.6.'+str(opts.witness+i)+'\"\\n')\n        out_keys.append(\"private-key = \"+json.dumps([prod[0][\"public_key\"], prod[0][\"private_key\"]])+\"\\n\")\n\n    out_data = \"\".join(out_wits + [\"\\n\"] + out_keys)\n\n    if opts.output == \"-\":\n        sys.stdout.write(out_data)\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            f.write(out_data)\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/generate_init_patch.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport subprocess\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Generate a patch file that adds init accounts\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-n\", \"--num\", metavar=\"N\", default=11, type=int, help=\"number of init witnesses\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    parser.add_argument(\"-s\", \"--secret\", metavar=\"SECRET\", default=None, help=\"private key generation secret\")\n    opts = parser.parse_args()\n\n    if opts.secret is None:\n        sys.stderr.write(\"missing required parameter --secret\\n\")\n        sys.stderr.flush()\n        sys.exit(1)\n\n    wit_accounts = []\n    wit_wits = []\n    committee = []\n\n    for i in range(opts.num):\n        owner_str = subprocess.check_output([\"programs/genesis_util/get_dev_key\", opts.secret, \"wit-owner-\"+str(i)]).decode(\"utf-8\")\n        active_str = subprocess.check_output([\"programs/genesis_util/get_dev_key\", opts.secret, \"wit-active-\"+str(i)]).decode(\"utf-8\")\n        prod_str = subprocess.check_output([\"programs/genesis_util/get_dev_key\", opts.secret, \"wit-block-signing-\"+str(i)]).decode(\"utf-8\")\n        owner = json.loads(owner_str)\n        active = json.loads(active_str)\n        prod = json.loads(prod_str)\n        wit_accounts.append({\n            \"name\" : \"init\"+str(i),\n            \"owner_key\" : owner[0][\"public_key\"],\n            \"active_key\" : active[0][\"public_key\"],\n            \"is_lifetime_member\" : True,\n            })\n        wit_wits.append({\n            \"owner_name\" : \"init\"+str(i),\n            \"block_signing_key\" : prod[0][\"public_key\"],\n            })\n        committee.append({\"owner_name\" : \"init\"+str(i)})\n    result = {\n       \"append\" : {\n       \"initial_accounts\" : wit_accounts },\n       \"replace\" : {\n       \"initial_active_witnesses\" : opts.num,\n       \"initial_worker_candidates\" : [],\n       \"initial_witness_candidates\" : wit_wits,\n       \"initial_committee_candidates\" : committee,\n        }\n    }\n\n    if opts.output == \"-\":\n        dump_json( result, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( result, f, opts.pretty )\n  \n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/genesis_update.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <algorithm>\n#include <iomanip>\n#include <iostream>\n#include <iterator>\n\n#include <fc/io/fstream.hpp>\n#include <fc/io/json.hpp>\n#include <fc/io/stdio.hpp>\n\n#include <graphene/app/api.hpp>\n#include <graphene/protocol/address.hpp>\n#include <graphene/egenesis/egenesis.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n\n#include <boost/filesystem.hpp>\n\n#ifndef WIN32\n#include <csignal>\n#endif\n\nusing namespace graphene::app;\nusing namespace graphene::chain;\nusing namespace graphene::utilities;\nusing namespace std;\nnamespace bpo = boost::program_options;\n\n// hack:  import create_example_genesis() even though it's a way, way\n// specific internal detail\nnamespace graphene { namespace app { namespace detail {\ngenesis_state_type create_example_genesis();\n} } } // graphene::app::detail\n\nint main( int argc, char** argv )\n{\n   try\n   {\n      bpo::options_description cli_options(\"BitShares empty blocks\");\n      cli_options.add_options()\n            (\"help,h\", \"Print this help message and exit.\")\n            (\"genesis-json,g\", bpo::value<boost::filesystem::path>(), \"File to read genesis state from\")\n            (\"out,o\", bpo::value<boost::filesystem::path>(), \"File to output new genesis to\")\n            (\"dev-account-prefix\", bpo::value<std::string>()->default_value(\"devacct\"), \"Prefix for dev accounts\")\n            (\"dev-key-prefix\", bpo::value<std::string>()->default_value(\"devkey-\"), \"Prefix for dev key\")\n            (\"dev-account-count\", bpo::value<uint32_t>()->default_value(0), \"Prefix for dev accounts\")\n            (\"dev-balance-count\", bpo::value<uint32_t>()->default_value(0), \"Prefix for dev balances\")\n            (\"dev-balance-amount\", bpo::value<uint64_t>()->default_value(uint64_t(1000)*uint64_t(1000)*uint64_t(100000)), \"Amount in each dev balance\")\n            ;\n\n      bpo::variables_map options;\n      try\n      {\n         boost::program_options::store( boost::program_options::parse_command_line(argc, argv, cli_options), options );\n      }\n      catch (const boost::program_options::error& e)\n      {\n         std::cerr << \"empty_blocks:  error parsing command line: \" << e.what() << \"\\n\";\n         return 1;\n      }\n\n      if( options.count(\"help\") )\n      {\n         std::cout << cli_options << \"\\n\";\n         return 1;\n      }\n\n      if( !options.count( \"genesis-json\" ) )\n      {\n         std::cerr << \"--genesis-json option is required\\n\";\n         return 1;\n      }\n\n      if( !options.count( \"out\" ) )\n      {\n         std::cerr << \"--out option is required\\n\";\n         return 1;\n      }\n\n      genesis_state_type genesis;\n      if( options.count(\"genesis-json\") )\n      {\n         fc::path genesis_json_filename = options[\"genesis-json\"].as<boost::filesystem::path>();\n         std::cerr << \"update_genesis:  Reading genesis from file \" << genesis_json_filename.preferred_string() << \"\\n\";\n         std::string genesis_json;\n         read_file_contents( genesis_json_filename, genesis_json );\n         genesis = fc::json::from_string( genesis_json ).as< genesis_state_type >(20);\n      }\n      else\n      {\n         std::cerr << \"update_genesis:  Using example genesis\\n\";\n         genesis = graphene::app::detail::create_example_genesis();\n      }\n\n      const std::string dev_key_prefix = options[\"dev-key-prefix\"].as<std::string>();\n\n      auto get_dev_key = [&dev_key_prefix]( std::string prefix, uint32_t i )\n      {\n         return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key();\n      };\n\n      uint32_t dev_account_count = options[\"dev-account-count\"].as<uint32_t>();\n      std::string dev_account_prefix = options[\"dev-account-prefix\"].as<std::string>();\n      for(uint32_t i=0;i<dev_account_count;i++)\n      {\n         genesis_state_type::initial_account_type acct(\n            dev_account_prefix+std::to_string(i),\n            get_dev_key( \"owner-\", i ),\n            get_dev_key( \"active-\", i ),\n            false );\n\n         genesis.initial_accounts.push_back( acct );\n      }\n\n      uint32_t dev_balance_count = options[\"dev-balance-count\"].as<uint32_t>();\n      uint64_t dev_balance_amount = options[\"dev-balance-amount\"].as<uint64_t>();\n      for(uint32_t i=0;i<dev_balance_count;i++)\n      {\n         genesis_state_type::initial_balance_type bal;\n         bal.owner = address( get_dev_key( \"balance-\", i ) );\n         bal.asset_symbol = \"CORE\";\n         bal.amount = dev_balance_amount;\n         genesis.initial_balances.push_back( bal );\n      }\n\n      std::map< std::string, size_t > name2index;\n      size_t num_accounts = genesis.initial_accounts.size();\n      for( size_t i=0; i<num_accounts; i++ )\n         name2index[ genesis.initial_accounts[i].name ] = i;\n\n      for( uint32_t i=0; i<genesis.initial_active_witnesses; i++ )\n      {\n         genesis_state_type::initial_witness_type& wit = genesis.initial_witness_candidates[ i ];\n         genesis_state_type::initial_account_type& wit_acct = genesis.initial_accounts[ name2index[ wit.owner_name ] ];\n         if( wit.owner_name.substr(0, 4) != \"init\" )\n         {\n            std::cerr << \"need \" << genesis.initial_active_witnesses << \" init accounts as first entries in initial_active_witnesses\\n\";\n            return 1;\n         }\n         wit.block_signing_key = get_dev_key( \"wit-block-signing-\", i );\n         wit_acct.owner_key = get_dev_key( \"wit-owner-\", i );\n         wit_acct.active_key = get_dev_key( \"wit-active-\", i );\n      }\n\n      fc::path output_filename = options[\"out\"].as<boost::filesystem::path>();\n      fc::json::save_to_file( genesis, output_filename );\n   }\n   catch ( const fc::exception& e )\n   {\n      std::cout << e.to_detail_string() << \"\\n\";\n      return 1;\n   }\n   return 0;\n}\n"
  },
  {
    "path": "programs/genesis_util/get_dev_key.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <iostream>\n#include <string>\n\n#include <fc/crypto/elliptic.hpp>\n#include <fc/io/json.hpp>\n\n#include <graphene/protocol/address.hpp>\n#include <graphene/protocol/types.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n\n#ifndef WIN32\n#include <csignal>\n#endif\n\nusing namespace std;\n\nint main( int argc, char** argv )\n{\n   try\n   {\n      std::string dev_key_prefix;\n      bool need_help = false;\n      if( argc < 3 ) // requires at least a prefix and a suffix\n         need_help = true;\n      else\n      {\n         dev_key_prefix = argv[1];\n         if(  (dev_key_prefix == \"-h\")\n           || (dev_key_prefix == \"--help\")\n           )\n           need_help = true;\n      }\n\n      if( need_help )\n      {\n         std::cerr << \"\\nThis program generates keys with specified prefix and suffix(es) as seed(s).\\n\\n\"\n             \"Syntax:\\n\\n\"\n             \"  get_dev_key <prefix> <suffix> ...\\n\\n\"\n             \"Examples:\\n\\n\"\n             \"  get_dev_key nath an\\n\"\n             \"  get_dev_key wxyz- owner-5 active-7 balance-9 wit-block-signing-3 wit-owner-5 wit-active-33\\n\"\n             \"  get_dev_key wxyz- wit-block-signing-0:101\\n\"\n             \"\\n\";\n         return 1;\n      }\n\n      bool comma = false;\n\n      auto show_key = [&comma]( const fc::ecc::private_key& priv_key )\n      {\n         fc::limited_mutable_variant_object mvo(5);\n         graphene::protocol::public_key_type pub_key = priv_key.get_public_key();\n         mvo( \"private_key\", graphene::utilities::key_to_wif( priv_key ) )\n            ( \"public_key\", std::string( pub_key ) )\n            ( \"address\", graphene::protocol::address( pub_key ) )\n            ;\n         if( comma )\n            std::cout << \",\\n\";\n         std::cout << fc::json::to_string( fc::mutable_variant_object(mvo) );\n         comma = true;\n      };\n\n      std::cout << \"[\";\n\n      for( int i=2; i<argc; i++ )\n      {\n         std::string arg = argv[i];\n         std::string prefix;\n         int lep = -1, rep = -1;\n         auto dash_pos = arg.rfind('-');\n         if( dash_pos != string::npos )\n         {\n            std::string lhs = arg.substr( 0, dash_pos+1 );\n            std::string rhs = arg.substr( dash_pos+1 );\n            auto colon_pos = rhs.find(':');\n            if( colon_pos != string::npos )\n            {\n               prefix = lhs;\n               lep = std::stoi( rhs.substr( 0, colon_pos ) );\n               rep = std::stoi( rhs.substr( colon_pos+1 ) );\n            }\n         }\n         if( lep >= 0 )\n         {\n            for( int k=lep; k<rep; k++ )\n            {\n               std::string s = dev_key_prefix + prefix + std::to_string(k);\n               show_key( fc::ecc::private_key::regenerate( fc::sha256::hash( s ) ) );\n            }\n         }\n         else\n         {\n            show_key( fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + arg ) ) );\n         }\n      }\n      std::cout << \"]\\n\";\n   }\n   catch ( const fc::exception& e )\n   {\n      std::cerr << e.to_detail_string() << \"\\n\";\n      return 1;\n   }\n   return 0;\n}\n"
  },
  {
    "path": "programs/genesis_util/prefix_accounts.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Add a prefix to selected account names\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-b\", \"--begin\", metavar=\"PREFIX\", default=\"\", help=\"prefix to add\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    taken_names = set()\n    unsettled_names = []\n    name_map = {}\n    prefix = opts.begin\n\n    for account in genesis[\"initial_accounts\"]:\n        is_prefixed = account[\"is_prefixed\"]\n        name = account[\"name\"]\n        if is_prefixed:\n            unsettled_names.append(name)\n            name_map[name] = name\n        else:\n            taken_names.add(name)\n\n    pass_num = 0\n    while len(unsettled_names) > 0:\n        num_resolved = 0\n        pass_num += 1\n        sys.stderr.write(\"attempting to resolve {n} names\\n\".format(n=len(unsettled_names)))\n        if pass_num > 1:\n            sys.stderr.write(\"names: {}\\n\".format(\"\\n\".join(unsettled_names)))\n        new_unsettled_names = []\n        for name in unsettled_names:\n            new_name = prefix+name_map[name]\n            name_map[name] = new_name\n            if new_name in taken_names:\n                new_unsettled_names.append(name)\n            else:\n                taken_names.add(name)\n                num_resolved += 1\n        sys.stderr.write(\"resolved {n} names\\n\".format(n=num_resolved))\n        unsettled_names = new_unsettled_names\n\n    for account in genesis[\"initial_accounts\"]:\n        name = account[\"name\"]\n        account[\"name\"] = name_map.get(name, name)\n        del account[\"is_prefixed\"]\n    for asset in genesis[\"initial_assets\"]:\n        issuer_name = asset[\"issuer_name\"]\n        asset[\"issuer_name\"] = name_map.get(issuer_name, issuer_name)\n    for witness in genesis[\"initial_witness_candidates\"]:\n        owner_name = witness[\"owner_name\"]\n        witness[\"owner_name\"] = name_map.get(owner_name, owner_name)\n    for committee in genesis[\"initial_committee_candidates\"]:\n        owner_name = member[\"owner_name\"]\n        member[\"owner_name\"] = name_map.get(owner_name, owner_name)\n    for worker in genesis[\"initial_worker_candidates\"]:\n        owner_name = worker[\"owner_name\"]\n        worker[\"owner_name\"] = name_map.get(owner_name, owner_name)\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/python_format.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\nif len(sys.argv) < 3:\n    print(\"syntax: \"+sys.argv[0]+\" INFILE OUTFILE\")\n    sys.exit(0)\n\nwith open(sys.argv[1], \"r\") as infile:\n    genesis = json.load(infile)\nwith open(sys.argv[2], \"w\") as outfile:\n    json.dump(genesis, outfile, indent=2, sort_keys=True)\n"
  },
  {
    "path": "programs/genesis_util/remove.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Remove entities from snapshot\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-a\", \"--asset\", metavar=\"ASSETS\", nargs=\"+\", help=\"list of asset(s) to delete\")    \n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)        \n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    if opts.asset is None: \n        opts.asset = []\n    rm_asset_set = set(opts.asset)\n\n    removed_asset_entries = {aname : 0 for aname in opts.asset}\n    new_initial_assets = []\n    for asset in genesis[\"initial_assets\"]:\n        symbol = asset[\"symbol\"]\n        if symbol not in rm_asset_set:\n            new_initial_assets.append(asset)\n        else:\n            removed_asset_entries[symbol] += 1\n    genesis[\"initial_assets\"] = new_initial_assets\n\n    removed_balance_entries = {aname : [] for aname in opts.asset}\n    new_initial_balances = []\n    for balance in genesis[\"initial_balances\"]:\n        symbol = balance[\"asset_symbol\"]\n        if symbol not in rm_asset_set:\n            new_initial_balances.append(balance)\n        else:\n            removed_balance_entries[symbol].append(balance)\n    genesis[\"initial_balances\"] = new_initial_balances\n    # TODO:  Remove from initial_vesting_balances\n\n    for aname in opts.asset:\n        sys.stderr.write(\n           \"Asset {sym} removed {acount} initial_assets, {bcount} initial_balances totaling {btotal}\\n\".format(\n              sym=aname,\n              acount=removed_asset_entries[aname],\n              bcount=len(removed_balance_entries[aname]),\n              btotal=sum(int(e[\"amount\"]) for e in removed_balance_entries[aname]),\n              ))\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/sort_objects.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Sort initial_accounts and initial_assets by \\\"id\\\" member, then remove \\\"id\\\" member\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    genesis[\"initial_assets\"].sort( key=lambda e : e[\"id\"] )\n    genesis[\"initial_accounts\"].sort( key=lambda e : e[\"id\"] )\n\n    for e in genesis[\"initial_assets\"]:\n        del e[\"id\"]\n    for e in genesis[\"initial_accounts\"]:\n        del e[\"id\"]\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/unprefix_asset_owners.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Set is_prefixed=false for all asset owners\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    asset_owners = set()\n    for asset in genesis[\"initial_assets\"]:\n        asset_owners.add(asset[\"issuer_name\"])\n    for account in genesis[\"initial_accounts\"]:\n        if account[\"name\"] in asset_owners:\n            account[\"is_prefixed\"] = False\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/unprefix_names.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\ndef load_names(infile):\n    names = set()\n    for line in infile:\n        if '#' in line:\n            line = line[:line.index('#')]\n        line = line.strip()\n        if line == \"\":\n            continue\n        names.add(line)\n    return names\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Set is_prefixed=False for a list of names\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-n\", \"--names\", metavar=\"NAMES\", help=\"list of names to unprefix\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    if opts.names == \"-\":\n        names = load_names(sys.stdin)\n    else:\n        with open(opts.names, \"r\") as f:\n            names = load_names(f)\n\n    for account in genesis[\"initial_accounts\"]:\n        if account[\"name\"] in names:\n            account[\"is_prefixed\"] = False\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/genesis_util/upgrade_members.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport json\nimport re\nimport sys\n\ndef dump_json(obj, out, pretty):\n    if pretty:\n        json.dump(obj, out, indent=2, sort_keys=True)\n    else:\n        json.dump(obj, out, separators=(\",\", \":\"), sort_keys=True)\n    return\n\nre_init = re.compile(r\"^init[0-9]+$\")\n\ndef load_names(infile):\n    names = set()\n    for line in infile:\n        if '#' in line:\n            line = line[:line.index('#')]\n        line = line.strip()\n        if line == \"\":\n            continue\n        names.add(line)\n    return names\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Upgrade a list of members\")\n    parser.add_argument(\"-o\", \"--output\", metavar=\"OUT\", default=\"-\", help=\"output filename (default: stdout)\")\n    parser.add_argument(\"-i\", \"--input\", metavar=\"IN\", default=\"-\", help=\"input filename (default: stdin)\")\n    parser.add_argument(\"-n\", \"--names\", metavar=\"NAMES\", default=\"\", help=\"file containing names to upgrade\")\n    parser.add_argument(\"-p\", \"--pretty\", action=\"store_true\", default=False, help=\"pretty print output\")\n    opts = parser.parse_args()\n\n    if opts.input == \"-\":\n        genesis = json.load(sys.stdin)\n    else:\n        with open(opts.input, \"r\") as f:\n            genesis = json.load(f)\n\n    if opts.names == \"-\":\n        names = load_names(sys.stdin)\n    else:\n        with open(opts.names, \"r\") as f:\n            names = load_names(f)\n\n    for account in genesis[\"initial_accounts\"]:\n        if account[\"name\"] in names:\n            account[\"is_lifetime_member\"] = True\n\n    if opts.output == \"-\":\n        dump_json( genesis, sys.stdout, opts.pretty )\n        sys.stdout.flush()\n    else:\n        with open(opts.output, \"w\") as f:\n            dump_json( genesis, f, opts.pretty )\n    return\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "programs/js_operation_serializer/CMakeLists.txt",
    "content": "add_executable( js_operation_serializer main.cpp )\nif( UNIX AND NOT APPLE )\n  set(rt_library rt )\nendif()\n\ntarget_link_libraries( js_operation_serializer\n                       PRIVATE graphene_app graphene_net graphene_chain graphene_egenesis_none graphene_utilities graphene_wallet fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\ninstall( TARGETS\n   js_operation_serializer\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "programs/js_operation_serializer/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/protocol/block.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/confidential_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n\n#include <iostream>\n\nusing namespace graphene::chain;\nusing namespace graphene::custom_operations;\n\nnamespace detail_ns {\n\nstring remove_tail_if( const string& str, char c, const string& match )\n{\n   auto last = str.find_last_of( c );\n   if( last != std::string::npos )\n      if( str.substr( last + 1 ) == match )\n         return str.substr( 0, last );\n   return str;\n}\nstring remove_namespace_if( const string& str, const string& match )\n{\n   auto last = str.find( match );\n   if( last != std::string::npos )\n      return str.substr( match.size()+2 );\n   return str;\n}\n\n\nstring remove_namespace( string str )\n{\n   str = remove_tail_if( str, '_', \"operation\" );\n   str = remove_tail_if( str, '_', \"t\" );\n   str = remove_tail_if( str, '_', \"object\" );\n   str = remove_tail_if( str, '_', \"type\" );\n   str = remove_namespace_if( str, \"graphene::chain\" );\n   str = remove_namespace_if( str, \"graphene::db\" );\n   str = remove_namespace_if( str, \"std\" );\n   str = remove_namespace_if( str, \"fc\" );\n   auto pos = str.find( \":\" );\n   if( pos != str.npos )\n      str.replace( pos, 2, \"_\" );\n   return str;\n}\n\n\n\n\ntemplate<typename T>\nvoid generate_serializer();\ntemplate<typename T>\nvoid register_serializer();\n\n\nmap<string, size_t >                st;\nvector<std::function<void()>>       serializers;\n\nbool register_serializer( const string& name, std::function<void()> sr )\n{\n   if( st.find(name) == st.end() )\n   {\n      serializers.push_back( sr );\n      st[name] = serializers.size() - 1;\n      return true;\n   }\n   return false;\n}\n\ntemplate<typename T> struct js_name { static std::string name(){ return  remove_namespace(fc::get_typename<T>::name()); }; };\n\ntemplate<typename T, size_t N>\nstruct js_name<std::array<T,N>>\n{\n   static std::string name(){ return  \"fixed_array \"+ fc::to_string(N) + \", \" \n                                      + remove_namespace(fc::get_typename<T>::name()); };\n};\ntemplate<size_t N>   struct js_name<std::array<char,N>>   { static std::string name(){ return  \"bytes(\"+ fc::to_string(N) + \")\"; }; };\ntemplate<size_t N>   struct js_name<std::array<uint8_t,N>>{ static std::string name(){ return  \"bytes(\"+ fc::to_string(N) + \")\"; }; };\ntemplate<typename T> struct js_name< fc::optional<T> >    { static std::string name(){ return \"optional(\" + js_name<T>::name() + \")\"; } };\ntemplate<>           struct js_name< object_id_type >     { static std::string name(){ return \"object_id_type\"; } };\ntemplate<typename T> struct js_name< fc::flat_set<T> >    { static std::string name(){ return \"set(\" + js_name<T>::name() + \")\"; } };\ntemplate<typename T> struct js_name< std::vector<T> >     { static std::string name(){ return \"array(\" + js_name<T>::name() + \")\"; } };\ntemplate<typename T> struct js_name< fc::safe<T> > { static std::string name(){ return js_name<T>::name(); } };\n\n\ntemplate<> struct js_name< std::vector<char> > { static std::string name(){ return \"bytes()\";     } };\ntemplate<> struct js_name<fc::uint160>         { static std::string name(){ return \"bytes(20)\";   } };\ntemplate<> struct js_name<fc::sha1>            { static std::string name(){ return \"bytes(20)\";   } };\ntemplate<> struct js_name<fc::sha224>          { static std::string name(){ return \"bytes(28)\";   } };\ntemplate<> struct js_name<fc::sha256>          { static std::string name(){ return \"bytes(32)\";   } };\ntemplate<> struct js_name<fc::unsigned_int>    { static std::string name(){ return \"varuint64\";  } };\ntemplate<> struct js_name< vote_id_type >      { static std::string name(){ return \"vote_id\";    } };\ntemplate<> struct js_name< time_point_sec >    { static std::string name(){ return \"time_point_sec\"; } };\n\ntemplate<uint8_t S, uint8_t T>\nstruct js_name<graphene::protocol::object_id<S,T> >\n{\n   static std::string name(){\n      return \"protocol_id_type(\\\"\" +\n             remove_namespace(fc::get_typename<object_downcast_t<object_id<S,T>>>::name()) + \"\\\")\";\n   };\n};\n\n\ntemplate<typename T> struct js_name< std::set<T> > { static std::string name(){ return \"set \" + js_name<T>::name(); } };\n\ntemplate<typename K, typename V>\nstruct js_name< std::map<K,V> > { static std::string name(){ return \"map(\" + js_name<K>::name() + \", \" + js_name<V>::name() +\")\"; } };\n\ntemplate<typename K, typename V>\nstruct js_name< fc::flat_map<K,V> > { static std::string name(){ return \"map(\" + js_name<K>::name() + \", \" + js_name<V>::name() +\")\"; } };\n\n\ntemplate<typename... T> struct js_sv_name;\n\ntemplate<typename A> struct js_sv_name<A>\n{ static std::string name(){ return  \"\\n    \" + js_name<A>::name(); } };\n\ntemplate<typename A, typename... T>\nstruct js_sv_name<A,T...> { static std::string name(){ return  \"\\n    \" + js_name<A>::name() +\",\" + js_sv_name<T...>::name(); } };\n\ntemplate<typename... T>\nstruct js_name< fc::static_variant<T...> >\n{\n   static std::string name( std::string n = \"\"){\n      static const std::string name = n;\n      if( name == \"\" )\n         return \"static_variant([\" + js_sv_name<T...>::name() + \"\\n]);\";\n      else return name;\n   }\n};\ntemplate<>\nstruct js_name< fc::static_variant<> >\n{\n   static std::string name( std::string n = \"\"){\n      static const std::string name = n;\n      if( name == \"\" )\n         return \"static_variant([]);\";\n      else return name;\n   }\n};\n\n\n\ntemplate<typename T, bool reflected = fc::reflector<T>::is_defined::value>\nstruct serializer;\n\n\nstruct register_type_visitor\n{\n   typedef void result_type;\n\n   template<typename Type>\n   result_type operator()( const Type& op )const { serializer<Type>::init(); }\n};\n\nclass register_member_visitor;\n\nstruct serialize_type_visitor\n{\n   typedef void result_type;\n\n   int t = 0;\n   serialize_type_visitor(int _t ):t(_t){}\n\n   template<typename Type>\n   result_type operator()( const Type& op )const\n   {\n      std::cout << \"    \" <<remove_namespace( fc::get_typename<Type>::name() )  <<\": \"<<t<<\"\\n\";\n   }\n};\n\n\nclass serialize_member_visitor\n{\n   public:\n      template<typename Member, class Class, Member (Class::*member)>\n      void operator()( const char* name )const\n      {\n         std::cout << \"    \" << name <<\": \" << js_name<Member>::name() <<\",\\n\";\n      }\n};\n\ntemplate<typename T>\nstruct serializer<T,false>\n{\n   static_assert( fc::reflector<T>::is_defined::value == false, \"invalid template arguments\" );\n   static void init()\n   {}\n\n   static void generate()\n   {}\n};\n\ntemplate<typename T, size_t N>\nstruct serializer<std::array<T,N>,false>\n{\n   static void init() { serializer<T>::init(); }\n   static void generate() {}\n};\ntemplate<typename T>\nstruct serializer<std::vector<T>,false>\n{\n   static void init() { serializer<T>::init(); }\n   static void generate() {}\n};\n\ntemplate<>\nstruct serializer<std::vector<operation>,false>\n{\n   static void init() { }\n   static void generate() {}\n};\n\ntemplate<>\nstruct serializer<object_id_type,true>\n{\n   static void init() {}\n\n   static void generate() {}\n};\ntemplate<>\nstruct serializer<uint64_t,false>\n{\n   static void init() {}\n   static void generate() {}\n};\ntemplate<> struct serializer<vote_id_type,false> { static void init() {} static void generate() {} };\n#ifdef __APPLE__\n// on mac, size_t is a distinct type from uint64_t or uint32_t and needs a separate specialization\ntemplate<> struct serializer<size_t,false> { static void init() {} static void generate() {} };\n#endif\ntemplate<> struct serializer<int64_t,false> { static void init() {} static void generate() {} };\ntemplate<> struct serializer<int64_t,true> { static void init() {} static void generate() {} };\n\ntemplate<typename T>\nstruct serializer<fc::optional<T>,false>\n{\n   static void init() { serializer<T>::init(); }\n   static void generate(){}\n};\n\ntemplate<uint8_t SpaceID, uint8_t TypeID>\nstruct serializer< graphene::db::object_id<SpaceID,TypeID> ,true>\n{\n   static void init() {}\n   static void generate() {}\n};\n\ntemplate<typename... T>\nstruct serializer< fc::static_variant<T...>, false >\n{\n   static void init()\n   {\n      static bool init = false;\n      if( !init )\n      {\n         init = true;\n         fc::static_variant<T...> var;\n         for( size_t i = 0; i < var.count(); ++i )\n         {\n            var.set_which(i);\n            var.visit( register_type_visitor() );\n         }\n         register_serializer( js_name<fc::static_variant<T...>>::name(), [=](){ generate(); } );\n      }\n   }\n\n   static void generate()\n   {\n      std::cout << \"var \" <<  js_name<fc::static_variant<T...>>::name() << \" = static_variant([\" + js_sv_name<T...>::name() + \"\\n]);\\n\\n\";\n   }\n};\ntemplate<>\nstruct serializer< fc::static_variant<>, false >\n{\n   static void init()\n   {\n      static bool init = false;\n      if( !init )\n      {\n         init = true;\n         fc::static_variant<> var;\n         register_serializer( js_name<fc::static_variant<>>::name(), [=](){ generate(); } );\n      }\n   }\n\n   static void generate()\n   {\n      std::cout <<  js_name<fc::static_variant<>>::name() << \" = static_variant([]);\\n\\n\";\n   }\n};\n\n\nclass register_member_visitor\n{\n   public:\n      template<typename Member, class Class, Member (Class::*member)>\n      void operator()( const char* name )const\n      {\n         serializer<Member>::init();\n      }\n};\n\ntemplate<typename T, bool reflected>\nstruct serializer\n{\n   static_assert( fc::reflector<T>::is_defined::value == reflected, \"invalid template arguments\" );\n   static void init()\n   {\n      auto name = js_name<T>::name();\n      if( st.find(name) == st.end() )\n      {\n         fc::reflector<T>::visit( register_member_visitor() );\n         register_serializer( name, [=](){ generate(); } );\n      }\n   }\n\n   static void generate()\n   {\n      auto name = remove_namespace( js_name<T>::name() );\n      if( name == \"int64\" ) return;\n      std::cout << \"export const \" << name\n                << \" = new Serializer(\"\n                << \"\\\"\" + name + \"\\\", {\\n\";\n      fc::reflector<T>::visit( serialize_member_visitor() );\n      std::cout <<\"});\\n\\n\";\n   }\n};\n\n} // namespace detail_ns\n\nint main( int argc, char** argv )\n{\n   try {\n    operation op;\n\n    std::cout << \"ChainTypes.operations=\\n\";\n    for( size_t i = 0; i < op.count(); ++i )\n    {\n       op.set_which(i);\n       op.visit( detail_ns::serialize_type_visitor(i) );\n    }\n    std::cout << \"\\n\";\n\n    detail_ns::js_name<fee_parameters>::name(\"fee_parameters\");\n    detail_ns::js_name<operation>::name(\"operation\");\n    detail_ns::js_name<operation_result>::name(\"operation_result\");\n    detail_ns::js_name<future_extensions>::name(\"future_extensions\");\n    detail_ns::js_name<worker_initializer>::name(\"worker_initializer\");\n    detail_ns::js_name<predicate>::name(\"predicate\");\n    detail_ns::js_name<vesting_policy_initializer>::name(\"vesting_policy_initializer\");\n    detail_ns::serializer<fee_parameters>::init();\n    detail_ns::serializer<fee_schedule>::init();\n    detail_ns::serializer<signed_block>::init();\n    detail_ns::serializer<block_header>::init();\n    detail_ns::serializer<signed_block_header>::init();\n    detail_ns::serializer<operation>::init();\n    detail_ns::serializer<transaction>::init();\n    detail_ns::serializer<signed_transaction>::init();\n    detail_ns::serializer<account_storage_map>::init();\n\n    for( const auto& gen : detail_ns::serializers )\n       gen();\n\n  } catch ( const fc::exception& e ){ edump((e.to_detail_string())); }\n   return 0;\n}\n"
  },
  {
    "path": "programs/network_mapper/CMakeLists.txt",
    "content": "add_executable( network_mapper network_mapper.cpp )\ntarget_link_libraries( network_mapper fc graphene_chain graphene_net )\n\n"
  },
  {
    "path": "programs/network_mapper/network_mapper.cpp",
    "content": "#include <fc/crypto/elliptic.hpp>\n#include <fc/io/json.hpp>\n#include <fc/exception/exception.hpp>\n#include <fc/io/raw_variant.hpp>\n#include <fc/network/ip.hpp>\n#include <fc/network/resolve.hpp>\n#include <fc/thread/future.hpp>\n#include <fstream>\n#include <iostream>\n#include <queue>\n#include <future>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/net/peer_connection.hpp>\n\nclass peer_probe : public graphene::net::peer_connection_delegate\n{\npublic:\n  bool _peer_closed_connection;\n  bool _we_closed_connection;\n  graphene::net::peer_connection_ptr _connection;\n  std::vector<graphene::net::address_info> _peers;\n  fc::ecc::public_key _node_id;\n  fc::ip::endpoint _remote;\n  bool _connection_was_rejected;\n  bool _done;\n  fc::promise<void>::ptr _probe_complete_promise;\n\npublic:\n  peer_probe() :\n    _peer_closed_connection(false),\n    _we_closed_connection(false),\n    _connection(graphene::net::peer_connection::make_shared(this)),\n    _connection_was_rejected(false),\n    _done(false),\n    _probe_complete_promise(fc::promise<void>::create(\"probe_complete\"))\n  {}\n\n  void start(const fc::ip::endpoint& endpoint_to_probe,\n             const fc::ecc::private_key& my_node_id,\n             const graphene::chain::chain_id_type& chain_id)\n  {\n    _remote = endpoint_to_probe;\n    fc::future<void> connect_task = fc::async([this](){ _connection->connect_to(_remote); }, \"connect_task\");\n    try\n    {\n      connect_task.wait(fc::seconds(10));\n    }\n    catch (const fc::timeout_exception&)\n    {\n      ilog(\"timeout connecting to node ${endpoint}\", (\"endpoint\", endpoint_to_probe));\n      connect_task.cancel(__FUNCTION__);\n      throw;\n    }\n\n    fc::sha256::encoder shared_secret_encoder;\n    fc::sha512 shared_secret = _connection->get_shared_secret();\n    shared_secret_encoder.write(shared_secret.data(), sizeof(shared_secret));\n    fc::ecc::compact_signature signature = my_node_id.sign_compact(shared_secret_encoder.result());\n\n    graphene::net::hello_message hello(\"network_mapper\",\n                                  GRAPHENE_NET_PROTOCOL_VERSION,\n                                  fc::ip::address(), 0, 0,\n                                  my_node_id.get_public_key(),\n                                  signature,\n\t\t\t\t  chain_id,\n                                  fc::variant_object());\n\n    _connection->send_message(hello);\n  }\n\n  void on_message(graphene::net::peer_connection* originating_peer,\n                  const graphene::net::message& received_message) override\n  {\n    graphene::net::message_hash_type message_hash = received_message.id();\n    dlog( \"handling message ${type} ${hash} size ${size} from peer ${endpoint}\",\n          ( \"type\", graphene::net::core_message_type_enum(received_message.msg_type.value() ) )(\"hash\", message_hash )\n          (\"size\", received_message.size )(\"endpoint\", originating_peer->get_remote_endpoint() ) );\n    switch ( received_message.msg_type.value() )\n    {\n    case graphene::net::core_message_type_enum::hello_message_type:\n      on_hello_message( originating_peer, received_message.as<graphene::net::hello_message>() );\n      break;\n    case graphene::net::core_message_type_enum::connection_accepted_message_type:\n      on_connection_accepted_message( originating_peer, received_message.as<graphene::net::connection_accepted_message>() );\n      break;\n    case graphene::net::core_message_type_enum::connection_rejected_message_type:\n      on_connection_rejected_message( originating_peer, received_message.as<graphene::net::connection_rejected_message>() );\n      break;\n    case graphene::net::core_message_type_enum::address_request_message_type:\n      on_address_request_message( originating_peer, received_message.as<graphene::net::address_request_message>() );\n      break;\n    case graphene::net::core_message_type_enum::address_message_type:\n      on_address_message( originating_peer, received_message.as<graphene::net::address_message>() );\n      break;\n    case graphene::net::core_message_type_enum::closing_connection_message_type:\n      on_closing_connection_message( originating_peer, received_message.as<graphene::net::closing_connection_message>() );\n      break;\n    default:\n      break;\n    }\n  }\n\n  void on_hello_message(graphene::net::peer_connection* originating_peer,\n                        const graphene::net::hello_message& hello_message_received)\n  {\n    _node_id = hello_message_received.node_public_key;\n    if (hello_message_received.user_data.contains(\"node_id\"))\n      originating_peer->node_id = hello_message_received.user_data[\"node_id\"].as<graphene::net::node_id_t>( 1 );\n    originating_peer->send_message(graphene::net::connection_rejected_message());\n  }\n\n  void on_connection_accepted_message(graphene::net::peer_connection* originating_peer,\n                                      const graphene::net::connection_accepted_message& connection_accepted_message_received)\n  {\n    _connection_was_rejected = false;\n    originating_peer->send_message(graphene::net::address_request_message());\n  }\n\n  void on_connection_rejected_message( graphene::net::peer_connection* originating_peer,\n                                       const graphene::net::connection_rejected_message& connection_rejected_message_received )\n  {\n    _connection_was_rejected = true;\n    originating_peer->send_message(graphene::net::address_request_message());\n  }\n\n  void on_address_request_message(graphene::net::peer_connection* originating_peer,\n                                  const graphene::net::address_request_message& address_request_message_received)\n  {\n    originating_peer->send_message(graphene::net::address_message());\n  }\n\n\n  void on_address_message(graphene::net::peer_connection* originating_peer,\n                          const graphene::net::address_message& address_message_received)\n  {\n    _peers = address_message_received.addresses;\n    originating_peer->send_message(graphene::net::closing_connection_message(\"Thanks for the info\"));\n    _we_closed_connection = true;\n  }\n\n  void on_closing_connection_message(graphene::net::peer_connection* originating_peer,\n                                     const graphene::net::closing_connection_message& closing_connection_message_received)\n  {\n    if (_we_closed_connection)\n      _connection->close_connection();\n    else\n      _peer_closed_connection = true;\n  }\n\n  void on_connection_closed(graphene::net::peer_connection* originating_peer) override\n  {\n    _done = true;\n    _probe_complete_promise->set_value();\n  }\n\n  graphene::net::message get_message_for_item(const graphene::net::item_id& item) override\n  {\n    return graphene::net::item_not_available_message(item);\n  }\n\n  void wait( const fc::microseconds& timeout_us )\n  {\n    _probe_complete_promise->wait( timeout_us );\n  }\n};\n\nint main(int argc, char** argv)\n{\n  std::queue<fc::ip::endpoint> nodes_to_visit;\n  std::set<fc::ip::endpoint> nodes_to_visit_set;\n  std::set<fc::ip::endpoint> nodes_already_visited;\n\n  if ( argc < 3 ) {\n     std::cerr << \"Usage: \" << argv[0] << \" <chain-id> <seed-addr> [<seed-addr> ...]\\n\";\n     exit(1);\n  }\n\n  const graphene::chain::chain_id_type chain_id( argv[1] );\n  for ( int i = 2; i < argc; i++ )\n  {\n     std::string ep(argv[i]);\n     uint16_t port;\n     auto pos = ep.find(':');\n     if (pos > 0)\n        port = boost::lexical_cast<uint16_t>( ep.substr( pos+1, ep.size() ) );\n     else\n        port = 1776;\n     for (const auto& addr : fc::resolve( ep.substr( 0, pos > 0 ? pos : ep.size() ), port ))\n        nodes_to_visit.push( addr );\n  }\n\n  fc::path data_dir = fc::temp_directory_path() / (\"network_map_\" + (fc::string) chain_id);\n  fc::create_directories(data_dir);\n\n  fc::ip::endpoint seed_node1 = nodes_to_visit.front();\n\n  fc::ecc::private_key my_node_id = fc::ecc::private_key::generate();\n  std::map<graphene::net::node_id_t, graphene::net::address_info> address_info_by_node_id;\n  std::map<graphene::net::node_id_t, std::vector<graphene::net::address_info> > connections_by_node_id;\n  std::vector<std::shared_ptr<peer_probe>> probes;\n\n  while (!nodes_to_visit.empty() || !probes.empty())\n  {\n    while (!nodes_to_visit.empty())\n    {\n       fc::ip::endpoint remote = nodes_to_visit.front();\n       nodes_to_visit.pop();\n       nodes_to_visit_set.erase( remote );\n       nodes_already_visited.insert( remote );\n\n       try\n       {\n          std::shared_ptr<peer_probe> probe(new peer_probe());\n          probe->start(remote, my_node_id, chain_id);\n          probes.emplace_back( std::move( probe ) );\n       }\n       catch (const fc::exception&)\n       {\n          std::cerr << \"Failed to connect \" << fc::string(remote) << \" - skipping!\" << std::endl;\n       }\n    }\n\n    if (!probes.empty())\n    {\n       fc::yield();\n       std::vector<std::shared_ptr<peer_probe>> running;\n       for ( auto& probe : probes ) {\n          if (probe->_probe_complete_promise->error())\n          {\n             std::cerr << fc::string(probe->_remote) << \" ran into an error!\\n\";\n             continue;\n          }\n          if (!probe->_probe_complete_promise->ready())\n          {\n             running.push_back( probe );\n             continue;\n          }\n\n          if( probe->_node_id.valid() )\n          {\n             graphene::net::address_info this_node_info;\n             this_node_info.direction = graphene::net::peer_connection_direction::outbound;\n             this_node_info.firewalled = graphene::net::firewalled_state::not_firewalled;\n             this_node_info.remote_endpoint = probe->_remote;\n             this_node_info.node_id = probe->_node_id;\n\n             connections_by_node_id[this_node_info.node_id] = probe->_peers;\n             if (address_info_by_node_id.find(this_node_info.node_id) == address_info_by_node_id.end())\n                address_info_by_node_id[this_node_info.node_id] = this_node_info;\n          }\n\n          for (const graphene::net::address_info& info : probe->_peers)\n          {\n             if (nodes_already_visited.find(info.remote_endpoint) == nodes_already_visited.end() &&\n                 info.firewalled == graphene::net::firewalled_state::not_firewalled &&\n                 nodes_to_visit_set.find(info.remote_endpoint) == nodes_to_visit_set.end())\n             {\n                nodes_to_visit.push(info.remote_endpoint);\n                nodes_to_visit_set.insert(info.remote_endpoint);\n             }\n             if (address_info_by_node_id.find(info.node_id) == address_info_by_node_id.end())\n                address_info_by_node_id[info.node_id] = info;\n          }\n       }\n       probes = std::move( running );\n       std::cout << address_info_by_node_id.size() << \" checked, \"\n                 << probes.size() << \" active, \"\n                 << nodes_to_visit.size() << \" to do\\n\";\n    }\n  }\n\n  graphene::net::node_id_t seed_node_id;\n  std::set<graphene::net::node_id_t> non_firewalled_nodes_set;\n  for (const auto& address_info_for_node : address_info_by_node_id)\n  {\n    if (address_info_for_node.second.remote_endpoint == seed_node1)\n      seed_node_id = address_info_for_node.first;\n    if (address_info_for_node.second.firewalled == graphene::net::firewalled_state::not_firewalled)\n      non_firewalled_nodes_set.insert(address_info_for_node.first);\n  }\n  std::set<graphene::net::node_id_t> seed_node_connections;\n  for (const graphene::net::address_info& info : connections_by_node_id[seed_node_id])\n    seed_node_connections.insert(info.node_id);\n  std::set<graphene::net::node_id_t> seed_node_missing_connections;\n  std::set_difference(non_firewalled_nodes_set.begin(), non_firewalled_nodes_set.end(),\n                      seed_node_connections.begin(), seed_node_connections.end(),\n                      std::inserter(seed_node_missing_connections, seed_node_missing_connections.end()));\n  seed_node_missing_connections.erase(seed_node_id);\n\n  std::ofstream dot_stream((data_dir / \"network_graph.dot\").string().c_str());\n\n  dot_stream << \"graph G {\\n\";\n  dot_stream << \"  // Total \" << address_info_by_node_id.size() << \" nodes, firewalled: \" << (address_info_by_node_id.size() - non_firewalled_nodes_set.size())\n                              << \", non-firewalled: \" << non_firewalled_nodes_set.size() << \"\\n\";\n  dot_stream << \"  // Seed node is \" << (std::string)address_info_by_node_id[seed_node_id].remote_endpoint << \" id: \" << fc::variant( seed_node_id, 1 ).as_string() << \"\\n\";\n  dot_stream << \"  // Seed node is connected to \" << connections_by_node_id[seed_node_id].size() << \" nodes\\n\";\n  dot_stream << \"  // Seed node is missing connections to \" << seed_node_missing_connections.size() << \" non-firewalled nodes:\\n\";\n  for (const graphene::net::node_id_t& id : seed_node_missing_connections)\n    dot_stream << \"  //           \" << (std::string)address_info_by_node_id[id].remote_endpoint << \"\\n\";\n\n  dot_stream << \"  layout=\\\"circo\\\";\\n\";\n\n  for (const auto& address_info_for_node : address_info_by_node_id)\n  {\n    dot_stream << \"  \\\"\" << fc::variant( address_info_for_node.first, 1 ).as_string() << \"\\\"[label=\\\"\" << (std::string)address_info_for_node.second.remote_endpoint << \"\\\"\";\n    if (address_info_for_node.second.firewalled != graphene::net::firewalled_state::not_firewalled)\n      dot_stream << \",shape=rectangle\";\n    dot_stream << \"];\\n\";\n  }\n  for (auto& node_and_connections : connections_by_node_id)\n    for (const graphene::net::address_info& this_connection : node_and_connections.second)\n      dot_stream << \"  \\\"\" << fc::variant( node_and_connections.first, 2 ).as_string() << \"\\\" -- \\\"\" << fc::variant( this_connection.node_id, 1 ).as_string() << \"\\\";\\n\";\n\n  dot_stream << \"}\\n\";\n\n  return 0;\n}\n"
  },
  {
    "path": "programs/size_checker/CMakeLists.txt",
    "content": "add_executable( size_checker main.cpp )\nif( UNIX AND NOT APPLE )\n  set(rt_library rt )\nendif()\n\ntarget_link_libraries( size_checker\n                       PRIVATE graphene_chain graphene_egenesis_none fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\ninstall( TARGETS\n   size_checker\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "programs/size_checker/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <fc/io/json.hpp>\n#include <fc/io/raw.hpp>\n#include <fc/variant.hpp>\n#include <fc/variant_object.hpp>\n\n#include <graphene/protocol/block.hpp>\n#include <graphene/protocol/fee_schedule.hpp>\n\n#include <algorithm>\n#include <iostream>\n#include <sstream>\n#include <string>\n#include <vector>\n\nusing namespace graphene::protocol;\n\nvector< fc::variant_object > g_op_types;\n\ntemplate< typename T >\nuint64_t get_wire_size()\n{\n   T data;\n   return fc::raw::pack( data ).size();\n}\n\nstruct size_check_type_visitor\n{\n   typedef void result_type;\n\n   int t = 0;\n   size_check_type_visitor(int _t ):t(_t){}\n\n   template<typename Type>\n   result_type operator()( const Type& op )const\n   {\n      fc::mutable_variant_object vo;\n      vo[\"name\"] = fc::get_typename<Type>::name();\n      vo[\"mem_size\"] = sizeof( Type );\n      vo[\"wire_size\"] = get_wire_size<Type>();\n      g_op_types.push_back( vo );\n   }\n};\n\nint main( int argc, char** argv )\n{\n   try\n   {\n      graphene::protocol::operation op;\n\n\n      vector<uint64_t> witnesses; witnesses.resize(50);\n      for( uint32_t i = 0; i < 60*60*24*30; ++i )\n      {\n         witnesses[ rand() % 50 ]++;\n      }\n\n      std::sort( witnesses.begin(), witnesses.end() );\n      idump((witnesses.back() - witnesses.front()) );\n      idump((60*60*24*30/50));\n      idump((\"deviation: \")((60*60*24*30/50-witnesses.front())/(60*60*24*30/50.0)));\n\n      idump( (witnesses) );\n\n      for( size_t i = 0; i < op.count(); ++i )\n      {\n         op.set_which(i);\n         op.visit( size_check_type_visitor(i) );\n      }\n\n      // sort them by mem size\n      std::stable_sort( g_op_types.begin(), g_op_types.end(),\n      [](const variant_object& oa, const variant_object& ob) {\n      return oa[\"mem_size\"].as_uint64() > ob[\"mem_size\"].as_uint64();\n      });\n      std::cout << \"[\\n\";\n      for( size_t i=0; i<g_op_types.size(); i++ )\n      {\n         std::cout << \"   \" << fc::json::to_string( g_op_types[i] );\n         if( i < g_op_types.size()-1 )\n            std::cout << \",\\n\";\n         else\n            std::cout << \"\\n\";\n      }\n      std::cout << \"]\\n\";\n      std::cerr << \"Size of block header: \" << sizeof( block_header ) << \" \" << fc::raw::pack_size( block_header() ) << \"\\n\";\n   }\n   catch ( const fc::exception& e ){ edump((e.to_detail_string())); }\n   idump((sizeof(signed_block)));\n   idump((fc::raw::pack_size(signed_block())));\n   return 0;\n}\n"
  },
  {
    "path": "programs/witness_node/CMakeLists.txt",
    "content": "add_executable( witness_node main.cpp )\nif( UNIX AND NOT APPLE )\n  set(rt_library rt )\nendif()\n\nfind_package( Gperftools QUIET )\nif( GPERFTOOLS_FOUND )\n    message( STATUS \"Found gperftools; compiling witness_node with TCMalloc\")\n    list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )\nendif()\n\n# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246\ntarget_link_libraries( witness_node\n\nPRIVATE graphene_app graphene_delayed_node graphene_account_history graphene_elasticsearch graphene_market_history graphene_grouped_orders graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full graphene_snapshot graphene_es_objects\n        graphene_api_helper_indexes graphene_custom_operations\n        fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\nif (MSVC)\n    set_target_properties( witness_node PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\ninstall( TARGETS\n   witness_node\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "programs/witness_node/main.cpp",
    "content": "/*\n * Copyright (c) 2015-2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/app/application.hpp>\n#include <graphene/app/config_util.hpp>\n\n#include <graphene/witness/witness.hpp>\n#include <graphene/debug_witness/debug_witness.hpp>\n#include <graphene/account_history/account_history_plugin.hpp>\n#include <graphene/elasticsearch/elasticsearch_plugin.hpp>\n#include <graphene/market_history/market_history_plugin.hpp>\n#include <graphene/delayed_node/delayed_node_plugin.hpp>\n#include <graphene/snapshot/snapshot.hpp>\n#include <graphene/es_objects/es_objects.hpp>\n#include <graphene/grouped_orders/grouped_orders_plugin.hpp>\n#include <graphene/api_helper_indexes/api_helper_indexes.hpp>\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n\n#include <fc/thread/thread.hpp>\n#include <fc/interprocess/signals.hpp>\n#include <fc/stacktrace.hpp>\n\n#include <boost/filesystem.hpp>\n#include <boost/property_tree/ptree.hpp>\n#include <boost/container/flat_set.hpp>\n#include <boost/algorithm/string.hpp>\n\n#include <graphene/utilities/git_revision.hpp>\n#include <boost/algorithm/string/replace.hpp>\n#include <websocketpp/version.hpp>\n\n#include <iostream>\n\n#ifdef WIN32\n# include <signal.h> \n#else\n# include <csignal>\n#endif\n\nusing namespace graphene;\nnamespace bpo = boost::program_options;\n\nint main(int argc, char** argv) {\n   fc::print_stacktrace_on_segfault();\n   app::application* node = new app::application();\n   fc::oexception unhandled_exception;\n   try {\n      bpo::options_description app_options(\"BitShares Witness Node\");\n      bpo::options_description cfg_options(\"BitShares Witness Node\");\n      std::string default_plugins = \"witness account_history market_history grouped_orders \"\n                                    \"api_helper_indexes custom_operations\";\n      app_options.add_options()\n            (\"help,h\", \"Print this help message and exit.\")\n            (\"data-dir,d\", bpo::value<boost::filesystem::path>()->default_value(\"witness_node_data_dir\"),\n                    \"Directory containing databases, configuration file, etc.\")\n            (\"version,v\", \"Display version information\")\n            (\"plugins\", bpo::value<std::string>()->default_value(default_plugins),\n                    \"Space-separated list of plugins to activate\")\n            (\"ignore-api-helper-indexes-warning\", \"Do not exit if api_helper_indexes plugin is not enabled.\");\n\n      bpo::variables_map options;\n\n      bpo::options_description cli, cfg;\n      node->set_program_options(cli, cfg);\n      cfg_options.add(cfg);\n\n      cfg_options.add_options()\n            (\"plugins\", bpo::value<std::string>()->default_value(default_plugins),\n                    \"Space-separated list of plugins to activate\")\n            (\"ignore-api-helper-indexes-warning\", \"Do not exit if api_helper_indexes plugin is not enabled.\");\n\n      auto witness_plug = node->register_plugin<witness_plugin::witness_plugin>();\n      auto debug_witness_plug = node->register_plugin<debug_witness_plugin::debug_witness_plugin>();\n      auto history_plug = node->register_plugin<account_history::account_history_plugin>();\n      auto elasticsearch_plug = node->register_plugin<elasticsearch::elasticsearch_plugin>();\n      auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();\n      auto delayed_plug = node->register_plugin<delayed_node::delayed_node_plugin>();\n      auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();\n      auto es_objects_plug = node->register_plugin<es_objects::es_objects_plugin>();\n      auto grouped_orders_plug = node->register_plugin<grouped_orders::grouped_orders_plugin>();\n      auto api_helper_indexes_plug = node->register_plugin<api_helper_indexes::api_helper_indexes>();\n      auto custom_operations_plug = node->register_plugin<custom_operations::custom_operations_plugin>();\n\n      // add plugin options to config\n      try\n      {\n         bpo::options_description cli, cfg;\n         node->set_program_options(cli, cfg);\n         app_options.add(cli);\n         cfg_options.add(cfg);\n         bpo::store(bpo::parse_command_line(argc, argv, app_options), options);\n      }\n      catch (const boost::program_options::error& e)\n      {\n         std::cerr << \"Error parsing command line: \" << e.what() << \"\\n\";\n         return 1;\n      }\n\n      if( options.count(\"version\") )\n      {\n         std::cout << \"Version: \" << graphene::utilities::git_revision_description << \"\\n\";\n         std::cout << \"SHA: \" << graphene::utilities::git_revision_sha << \"\\n\";\n         std::cout << \"Timestamp: \" << fc::get_approximate_relative_time_string(fc::time_point_sec(graphene::utilities::git_revision_unix_timestamp)) << \"\\n\";\n         std::cout << \"SSL: \" << OPENSSL_VERSION_TEXT << \"\\n\";\n         std::cout << \"Boost: \" << boost::replace_all_copy(std::string(BOOST_LIB_VERSION), \"_\", \".\") << \"\\n\";\n         std::cout << \"Websocket++: \" << websocketpp::major_version << \".\" << websocketpp::minor_version << \".\" << websocketpp::patch_version << \"\\n\";\n         return 0;\n      }\n      if( options.count(\"help\") )\n      {\n         std::cout << app_options << \"\\n\";\n         return 0;\n      }\n\n      fc::path data_dir;\n      if( options.count(\"data-dir\") )\n      {\n         data_dir = options[\"data-dir\"].as<boost::filesystem::path>();\n         if( data_dir.is_relative() )\n            data_dir = fc::current_path() / data_dir;\n      }\n      app::load_configuration_options(data_dir, cfg_options, options);\n\n      std::set<std::string> plugins;\n      boost::split(plugins, options.at(\"plugins\").as<std::string>(), [](char c){return c == ' ';});\n\n      if(plugins.count(\"account_history\") && plugins.count(\"elasticsearch\")) {\n         std::cerr << \"Plugin conflict: Cannot load both account_history plugin and elasticsearch plugin\\n\";\n         return 1;\n      }\n\n      if( !plugins.count(\"api_helper_indexes\") && !options.count(\"ignore-api-helper-indexes-warning\")\n          && ( options.count(\"rpc-endpoint\") || options.count(\"rpc-tls-endpoint\") ) )\n      {\n         std::cerr << \"\\nIf this is an API node, please enable api_helper_indexes plugin.\"\n                      \"\\nIf this is not an API node, please start with \\\"--ignore-api-helper-indexes-warning\\\"\"\n                      \" or enable it in config.ini file.\\n\\n\";\n         return 1;\n      }\n\n      std::for_each(plugins.begin(), plugins.end(), [node](const std::string& plug) mutable {\n         if (!plug.empty()) {\n            node->enable_plugin(plug);\n         }\n      });\n\n      bpo::notify(options);\n\n      node->initialize(data_dir, options);\n      node->initialize_plugins( options );\n\n      node->startup();\n      node->startup_plugins();\n\n      fc::promise<int>::ptr exit_promise = fc::promise<int>::create(\"UNIX Signal Handler\");\n\n      fc::set_signal_handler([&exit_promise](int signal) {\n         elog( \"Caught SIGINT attempting to exit cleanly\" );\n         exit_promise->set_value(signal);\n      }, SIGINT);\n\n      fc::set_signal_handler([&exit_promise](int signal) {\n         elog( \"Caught SIGTERM attempting to exit cleanly\" );\n         exit_promise->set_value(signal);\n      }, SIGTERM);\n\n      ilog(\"Started NBS node on a chain with ${h} blocks.\", (\"h\", node->chain_database()->head_block_num()));\n      ilog(\"Chain ID is ${id}\", (\"id\", node->chain_database()->get_chain_id()) );\n\n      int signal = exit_promise->wait();\n      ilog(\"Exiting from signal ${n}\", (\"n\", signal));\n      node->shutdown_plugins();\n      node->shutdown();\n      delete node;\n      return EXIT_SUCCESS;\n   } catch( const fc::exception& e ) {\n      // deleting the node can yield, so do this outside the exception handler\n      unhandled_exception = e;\n   }\n\n   if (unhandled_exception)\n   {\n      elog(\"Exiting with error:\\n${e}\", (\"e\", unhandled_exception->to_detail_string()));\n      node->shutdown();\n      delete node;\n      return EXIT_FAILURE;\n   }\n}\n\n"
  },
  {
    "path": "programs/witness_node/saltpass.py",
    "content": "#!/usr/bin/env python3\n\nimport base64\nimport getpass\nimport hashlib\nimport json\nimport os\n\npw = getpass.getpass(\"enter your password:  \")\npw_bytes = pw.encode(\"utf-8\")\nsalt_bytes = os.urandom(8)\nsalt_b64 = base64.b64encode( salt_bytes )\npw_hash = hashlib.sha256( pw_bytes + salt_bytes ).digest()\npw_hash_b64 = base64.b64encode( pw_hash )\n\nprint(json.dumps(\n{\n    \"password_hash_b64\" : pw_hash_b64.decode(\"ascii\"),\n    \"password_salt_b64\" : salt_b64.decode(\"ascii\"),\n},\nsort_keys=True,\nindent=3, separators=(',', ' : ')\n))\n"
  },
  {
    "path": "sonar-project.properties",
    "content": "sonar.projectKey=BitShares_Core\nsonar.projectName=BitShares Core\n\nsonar.links.homepage=https://bitshares.org\nsonar.links.ci=https://travis-ci.org/bitshares/bitshares-core/\nsonar.links.issue=https://github.com/bitshares/bitshares-core/issues\nsonar.links.scm=https://github.com/bitshares/bitshares-core/tree/master\n\nsonar.tests=tests\n# Used by the `build_and_test` script for the first pass when building with Travis CI, to skip some files\n#sonar.exclusions.part1=programs/build_helper/**/*,libraries/fc/**/*,libraries/egenesis/egenesis_full.cpp,libraries/chain/**/*,libraries/protocol/**/*\n#sonar.exclusions.part2=programs/build_helper/**/*,libraries/fc/**/*,libraries/egenesis/egenesis_full.cpp,libraries/chain/**/*\nsonar.exclusions=programs/build_helper/**/*,libraries/fc/**/*,libraries/egenesis/egenesis_full.cpp\nsonar.sources=libraries,programs\nsonar.cfamily.build-wrapper-output=bw-output\nsonar.cfamily.gcov.reportsPath=.\nsonar.cfamily.threads=2\nsonar.cfamily.cache.enabled=true\nsonar.cfamily.cache.path=sonar_cache\n\n# Decide which tree the current build belongs to in SonarCloud.\n# Managed by the `set_sonar_branch` script when building with Travis CI.\nsonar.branch.target=develop\n"
  },
  {
    "path": "tests/CMakeLists.txt",
    "content": "file(GLOB COMMON_SOURCES \"common/*.cpp\")\n\nfind_package( Gperftools QUIET )\nif( GPERFTOOLS_FOUND )\n    message( STATUS \"Found gperftools; compiling tests with TCMalloc\")\n    list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )\nendif()\n\nfile(GLOB UNIT_TESTS \"tests/*.cpp\")\nadd_executable( chain_test ${COMMON_SOURCES} ${UNIT_TESTS} )\ntarget_link_libraries( chain_test\n                       graphene_chain graphene_app graphene_witness graphene_account_history graphene_elasticsearch\n                       graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes graphene_custom_operations\n                       fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} )\nif(MSVC)\n  set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\n  set_source_files_properties( tests/common/database_fixture.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\nfile(GLOB PERFORMANCE_TESTS \"performance/*.cpp\")\nadd_executable( performance_test ${COMMON_SOURCES} ${PERFORMANCE_TESTS} )\ntarget_link_libraries( performance_test\n                       graphene_chain graphene_app graphene_account_history graphene_elasticsearch\n                       graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes\n                       graphene_custom_operations\n                       fc ${PLATFORM_SPECIFIC_LIBS} )\n\nfile(GLOB BENCH_MARKS \"benchmarks/*.cpp\")\nadd_executable( chain_bench ${COMMON_SOURCES} ${BENCH_MARKS} )\ntarget_link_libraries( chain_bench\n                       graphene_chain graphene_app graphene_account_history graphene_elasticsearch\n                       graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes\n                       graphene_custom_operations\n                       fc ${PLATFORM_SPECIFIC_LIBS} )\n\nfile(GLOB APP_SOURCES \"app/*.cpp\")\nadd_executable( app_test ${APP_SOURCES} )\ntarget_link_libraries( app_test graphene_app graphene_account_history graphene_net graphene_witness graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )\n\nfile(GLOB CLI_SOURCES \"cli/*.cpp\")\nadd_executable( cli_test ${CLI_SOURCES} )\nif(WIN32)\n   list(APPEND PLATFORM_SPECIFIC_LIBS ws2_32)\nendif()\ntarget_link_libraries( cli_test graphene_app graphene_wallet graphene_witness graphene_account_history graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )\nif(MSVC)\n  set_source_files_properties( cli/main.cpp PROPERTIES COMPILE_FLAGS \"/bigobj\" )\nendif(MSVC)\n\nfile(GLOB ES_SOURCES \"elasticsearch/*.cpp\")\nadd_executable( es_test ${COMMON_SOURCES} ${ES_SOURCES} )\ntarget_link_libraries( es_test\n                       graphene_chain graphene_app graphene_account_history graphene_elasticsearch\n                       graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes\n                       graphene_custom_operations\n                       fc ${PLATFORM_SPECIFIC_LIBS} )\n                       \nadd_subdirectory( generate_empty_blocks )\n"
  },
  {
    "path": "tests/app/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/app/application.hpp>\n#include <graphene/app/plugin.hpp>\n#include <graphene/app/config_util.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <graphene/account_history/account_history_plugin.hpp>\n#include <graphene/market_history/market_history_plugin.hpp>\n#include <graphene/witness/witness.hpp>\n#include <graphene/grouped_orders/grouped_orders_plugin.hpp>\n\n#include <fc/thread/thread.hpp>\n#include <fc/log/appender.hpp>\n#include <fc/log/logger.hpp>\n\n#include <boost/filesystem/path.hpp>\n\n#include \"../../libraries/app/application_impl.hxx\"\n\n#define BOOST_TEST_MODULE Test Application\n#include <boost/test/included/unit_test.hpp>\n\n#include \"../common/genesis_file_util.hpp\"\n\nusing namespace graphene;\nnamespace bpo = boost::program_options;\n\nnamespace fc {\n   extern std::unordered_map<std::string, logger> &get_logger_map();\n   extern std::unordered_map<std::string, appender::ptr> &get_appender_map();\n}\n\nBOOST_AUTO_TEST_CASE(load_configuration_options_test_config_logging_files_created)\n{\n   fc::temp_directory app_dir(graphene::utilities::temp_directory_path());\n   auto dir = app_dir.path();\n   auto config_ini_file = dir / \"config.ini\";\n   auto logging_ini_file = dir / \"logging.ini\";\n\n   /// create default config options\n   auto node = new app::application();\n   bpo::options_description cli, cfg;\n   node->set_program_options(cli, cfg);\n   bpo::options_description cfg_options(\"BitShares Witness Node\");\n   cfg_options.add(cfg);\n\n   /// check preconditions\n   BOOST_CHECK(!fc::exists(config_ini_file));\n   BOOST_CHECK(!fc::exists(logging_ini_file));\n\n   bpo::variables_map options;\n   app::load_configuration_options(dir, cfg_options, options);\n\n   /// check post-conditions\n   BOOST_CHECK(fc::exists(config_ini_file));\n   BOOST_CHECK(fc::exists(logging_ini_file));\n   BOOST_CHECK_GT(fc::file_size(config_ini_file), 0u);\n   BOOST_CHECK_GT(fc::file_size(logging_ini_file), 0u);\n}\n\nBOOST_AUTO_TEST_CASE(load_configuration_options_test_config_ini_options)\n{\n   fc::temp_directory app_dir(graphene::utilities::temp_directory_path());\n   auto dir = app_dir.path();\n   auto config_ini_file = dir / \"config.ini\";\n   auto logging_ini_file = dir / \"logging.ini\";\n\n   /// create config.ini\n   bpo::options_description cfg_options(\"config.ini options\");\n   cfg_options.add_options()\n   (\"option1\", bpo::value<std::string>(), \"\")\n   (\"option2\", bpo::value<int>(), \"\")\n   ;\n   std::ofstream out(config_ini_file.preferred_string());\n   out << \"option1=is present\\n\"\n          \"option2=1\\n\\n\";\n   out.close();\n\n   /// check preconditions\n   BOOST_CHECK(fc::exists(config_ini_file));\n   BOOST_CHECK(!fc::exists(logging_ini_file));\n\n   bpo::variables_map options;\n   app::load_configuration_options(dir, cfg_options, options);\n\n   /// check the options values are parsed into the output map\n   BOOST_CHECK(!options.empty());\n   BOOST_CHECK_EQUAL(options.count(\"option1\"), 1u);\n   BOOST_CHECK_EQUAL(options.count(\"option2\"), 1u);\n   BOOST_CHECK_EQUAL(options[\"option1\"].as<std::string>(), \"is present\");\n   BOOST_CHECK_EQUAL(options[\"option2\"].as<int>(), 1);\n\n   /// when the config.ini exists and doesn't contain logging configuration while the logging.ini doesn't exist\n   /// the logging.ini is not created\n   BOOST_CHECK(!fc::exists(logging_ini_file));\n}\n\nBOOST_AUTO_TEST_CASE(load_configuration_options_test_logging_ini_options)\n{\n   fc::temp_directory app_dir(graphene::utilities::temp_directory_path());\n   auto dir = app_dir.path();\n   auto config_ini_file = dir / \"config.ini\";\n   auto logging_ini_file = dir / \"logging.ini\";\n\n   /// create logging.ini\n   /// configure exactly one logger and appender\n   std::ofstream out(logging_ini_file.preferred_string());\n   out << \"[log.file_appender.default]\\n\"\n          \"filename=test.log\\n\\n\"\n          \"[logger.default]\\n\"\n          \"level=info\\n\"\n          \"appenders=default\\n\\n\"\n          ;\n   out.close();\n\n   /// clear logger and appender state\n   fc::get_logger_map().clear();\n   fc::get_appender_map().clear();\n   BOOST_CHECK(fc::get_logger_map().empty());\n   BOOST_CHECK(fc::get_appender_map().empty());\n\n   bpo::options_description cfg_options(\"empty\");\n   bpo::variables_map options;\n   app::load_configuration_options(dir, cfg_options, options);\n\n   /// check the options values are parsed into the output map\n   /// this is a little bit tricky since load_configuration_options() doesn't provide output variable for logging_config\n   auto logger_map = fc::get_logger_map();\n   auto appender_map = fc::get_appender_map();\n   BOOST_CHECK_EQUAL(logger_map.size(), 1u);\n   BOOST_CHECK(logger_map.count(\"default\"));\n   BOOST_CHECK_EQUAL(appender_map.size(), 1u);\n   BOOST_CHECK(appender_map.count(\"default\"));\n}\n\nBOOST_AUTO_TEST_CASE(load_configuration_options_test_legacy_config_ini_options)\n{\n   fc::temp_directory app_dir(graphene::utilities::temp_directory_path());\n   auto dir = app_dir.path();\n   auto config_ini_file = dir / \"config.ini\";\n   auto logging_ini_file = dir / \"logging.ini\";\n\n   /// create config.ini\n   bpo::options_description cfg_options(\"config.ini options\");\n   cfg_options.add_options()\n   (\"option1\", bpo::value<std::string>(), \"\")\n   (\"option2\", bpo::value<int>(), \"\")\n   ;\n   std::ofstream out(config_ini_file.preferred_string());\n   out << \"option1=is present\\n\"\n          \"option2=1\\n\\n\"\n          \"[log.file_appender.default]\\n\"\n          \"filename=test.log\\n\\n\"\n          \"[logger.default]\\n\"\n          \"level=info\\n\"\n          \"appenders=default\\n\\n\"\n          ;\n   out.close();\n\n   /// clear logger and appender state\n   fc::get_logger_map().clear();\n   fc::get_appender_map().clear();\n   BOOST_CHECK(fc::get_logger_map().empty());\n   BOOST_CHECK(fc::get_appender_map().empty());\n\n   bpo::variables_map options;\n   app::load_configuration_options(dir, cfg_options, options);\n\n   /// check logging.ini not created\n   BOOST_CHECK(!fc::exists(logging_ini_file));\n\n   /// check the options values are parsed into the output map\n   BOOST_CHECK(!options.empty());\n   BOOST_CHECK_EQUAL(options.count(\"option1\"), 1u);\n   BOOST_CHECK_EQUAL(options.count(\"option2\"), 1u);\n   BOOST_CHECK_EQUAL(options[\"option1\"].as<std::string>(), \"is present\");\n   BOOST_CHECK_EQUAL(options[\"option2\"].as<int>(), 1);\n\n   auto logger_map = fc::get_logger_map();\n   auto appender_map = fc::get_appender_map();\n   BOOST_CHECK_EQUAL(logger_map.size(), 1u);\n   BOOST_CHECK(logger_map.count(\"default\"));\n   BOOST_CHECK_EQUAL(appender_map.size(), 1u);\n   BOOST_CHECK(appender_map.count(\"default\"));\n}\n\n/////////////\n/// @brief create a 2 node network\n/////////////\nBOOST_AUTO_TEST_CASE( two_node_network )\n{\n   using namespace graphene::chain;\n   using namespace graphene::app;\n   try {\n      BOOST_TEST_MESSAGE( \"Creating and initializing app1\" );\n\n      fc::temp_directory app_dir( graphene::utilities::temp_directory_path() );\n\n      graphene::app::application app1;\n      app1.register_plugin< graphene::account_history::account_history_plugin>();\n      app1.register_plugin< graphene::market_history::market_history_plugin >();\n      app1.register_plugin< graphene::witness_plugin::witness_plugin >();\n      app1.register_plugin< graphene::grouped_orders::grouped_orders_plugin>();\n      app1.startup_plugins();\n      boost::program_options::variables_map cfg;\n      cfg.emplace(\"p2p-endpoint\", boost::program_options::variable_value(string(\"127.0.0.1:3939\"), false));\n      cfg.emplace(\"genesis-json\", boost::program_options::variable_value(create_genesis_file(app_dir), false));\n      cfg.emplace(\"seed-nodes\", boost::program_options::variable_value(string(\"[]\"), false));\n      app1.initialize(app_dir.path(), cfg);\n      BOOST_TEST_MESSAGE( \"Starting app1 and waiting 500 ms\" );\n      app1.startup();\n      fc::usleep(fc::milliseconds(500));\n\n      BOOST_TEST_MESSAGE( \"Creating and initializing app2\" );\n\n      fc::temp_directory app2_dir( graphene::utilities::temp_directory_path() );\n      graphene::app::application app2;\n      app2.register_plugin<account_history::account_history_plugin>();\n      app2.register_plugin< graphene::market_history::market_history_plugin >();\n      app2.register_plugin< graphene::witness_plugin::witness_plugin >();\n      app2.register_plugin< graphene::grouped_orders::grouped_orders_plugin>();\n      app2.startup_plugins();\n      auto cfg2 = cfg;\n      cfg2.erase(\"p2p-endpoint\");\n      cfg2.emplace(\"p2p-endpoint\", boost::program_options::variable_value(string(\"127.0.0.1:4040\"), false));\n      cfg2.emplace(\"genesis-json\", boost::program_options::variable_value(create_genesis_file(app_dir), false));\n      cfg2.emplace(\"seed-node\", boost::program_options::variable_value(vector<string>{\"127.0.0.1:3939\"}, false));\n      cfg2.emplace(\"seed-nodes\", boost::program_options::variable_value(string(\"[]\"), false));\n      app2.initialize(app2_dir.path(), cfg2);\n\n      BOOST_TEST_MESSAGE( \"Starting app2 and waiting 500 ms\" );\n      app2.startup();\n      fc::usleep(fc::milliseconds(500));\n\n      BOOST_REQUIRE_EQUAL(app1.p2p_node()->get_connection_count(), 1u);\n      BOOST_CHECK_EQUAL(std::string(app1.p2p_node()->get_connected_peers().front().host.get_address()), \"127.0.0.1\");\n      BOOST_TEST_MESSAGE( \"app1 and app2 successfully connected\" );\n\n      std::shared_ptr<chain::database> db1 = app1.chain_database();\n      std::shared_ptr<chain::database> db2 = app2.chain_database();\n\n      BOOST_CHECK_EQUAL( db1->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 0 );\n      BOOST_CHECK_EQUAL( db2->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 0 );\n\n      BOOST_TEST_MESSAGE( \"Creating transfer tx\" );\n      graphene::chain::precomputable_transaction trx;\n      {\n         account_id_type nathan_id = db2->get_index_type<account_index>().indices().get<by_name>().find( \"nathan\" )->id;\n         fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n\n         balance_claim_operation claim_op;\n         balance_id_type bid = balance_id_type();\n         claim_op.deposit_to_account = nathan_id;\n         claim_op.balance_to_claim = bid;\n         claim_op.balance_owner_key = nathan_key.get_public_key();\n         claim_op.total_claimed = bid(*db1).balance;\n         trx.operations.push_back( claim_op );\n         db1->current_fee_schedule().set_fee( trx.operations.back() );\n\n         transfer_operation xfer_op;\n         xfer_op.from = nathan_id;\n         xfer_op.to = GRAPHENE_NULL_ACCOUNT;\n         xfer_op.amount = asset( 1000000 );\n         trx.operations.push_back( xfer_op );\n         db1->current_fee_schedule().set_fee( trx.operations.back() );\n\n         trx.set_expiration( db1->get_slot_time( 10 ) );\n         trx.sign( nathan_key, db1->get_chain_id() );\n         trx.validate();\n      }\n\n      BOOST_TEST_MESSAGE( \"Pushing tx locally on db1\" );\n      processed_transaction ptrx = db1->push_transaction(trx);\n\n      BOOST_CHECK_EQUAL( db1->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 1000000 );\n      BOOST_CHECK_EQUAL( db2->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 0 );\n\n      BOOST_TEST_MESSAGE( \"Broadcasting tx\" );\n      app1.p2p_node()->broadcast(graphene::net::trx_message(trx));\n\n      fc::usleep(fc::milliseconds(500));\n\n      BOOST_CHECK_EQUAL( db1->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 1000000 );\n      BOOST_CHECK_EQUAL( db2->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 1000000 );\n\n      BOOST_TEST_MESSAGE( \"Generating block on db2\" );\n      fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n\n      auto block_1 = db2->generate_block(\n         db2->get_slot_time(1),\n         db2->get_scheduled_witness(1),\n         committee_key,\n         database::skip_nothing);\n\n      BOOST_TEST_MESSAGE( \"Broadcasting block\" );\n      app2.p2p_node()->broadcast(graphene::net::block_message( block_1 ));\n\n      fc::usleep(fc::milliseconds(500));\n      BOOST_TEST_MESSAGE( \"Verifying nodes are still connected\" );\n      BOOST_CHECK_EQUAL(app1.p2p_node()->get_connection_count(), 1u);\n      BOOST_CHECK_EQUAL(app1.chain_database()->head_block_num(), 1u);\n\n      BOOST_TEST_MESSAGE( \"Checking GRAPHENE_NULL_ACCOUNT has balance\" );\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n// a contrived example to test the breaking out of application_impl to a header file\n\nBOOST_AUTO_TEST_CASE(application_impl_breakout) {\n   class test_impl : public graphene::app::detail::application_impl {\n      // override the constructor, just to test that we can\n   public:\n      test_impl() : application_impl(nullptr) {}\n      bool has_item(const net::item_id& id) override {\n         return true;\n      }\n   };\n\n   test_impl impl;\n   graphene::net::item_id id;\n   BOOST_CHECK(impl.has_item(id));\n}\n"
  },
  {
    "path": "tests/benchmarks/genesis_allocation.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/account_object.hpp>\n#include <graphene/utilities/tempdir.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include <boost/test/auto_unit_test.hpp>\n\nusing namespace graphene::chain;\n\nBOOST_AUTO_TEST_CASE( operation_sanity_check )\n{\n   try {\n      operation op = account_create_operation();\n      op.get<account_create_operation>().active.add_authority(account_id_type(), 123);\n      operation tmp = std::move(op);\n      wdump((tmp.which()));\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( genesis_and_persistence_bench )\n{\n   try {\n      genesis_state_type genesis_state;\n\n#ifdef NDEBUG\n      ilog(\"Running in release mode.\");\n      const int account_count = 2000000;\n      const int blocks_to_produce = 1000000;\n#else\n      ilog(\"Running in debug mode.\");\n      const int account_count = 30000;\n      const int blocks_to_produce = 1000;\n#endif\n\n      for( int i = 0; i < account_count; ++i )\n         genesis_state.initial_accounts.emplace_back(\"target\"+fc::to_string(i),\n                                                     public_key_type(fc::ecc::private_key::regenerate(fc::digest(i)).get_public_key()));\n\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n\n      {\n         database db;\n         db.open(data_dir.path(), [&]{return genesis_state;}, \"test\");\n\n         for( int i = 11; i < account_count + 11; ++i)\n            BOOST_CHECK(db.get_balance(account_id_type(i), asset_id_type()).amount == GRAPHENE_MAX_SHARE_SUPPLY / account_count);\n\n         fc::time_point start_time = fc::time_point::now();\n         db.close();\n         ilog(\"Closed database in ${t} milliseconds.\", (\"t\", (fc::time_point::now() - start_time).count() / 1000));\n      }\n      {\n         database db;\n\n         fc::time_point start_time = fc::time_point::now();\n         db.open(data_dir.path(), [&]{return genesis_state;}, \"test\");\n         ilog(\"Opened database in ${t} milliseconds.\", (\"t\", (fc::time_point::now() - start_time).count() / 1000));\n\n         for( int i = 11; i < account_count + 11; ++i)\n            BOOST_CHECK(db.get_balance(account_id_type(i), asset_id_type()).amount == GRAPHENE_MAX_SHARE_SUPPLY / account_count);\n\n         int blocks_out = 0;\n         auto witness_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n         auto aw = db.get_global_properties().active_witnesses;\n         auto b =  db.generate_block( db.get_slot_time( 1 ), db.get_scheduled_witness( 1 ), witness_priv_key, ~0 );\n\n         start_time = fc::time_point::now();\n         /* TODO: get this buliding again\n         for( int i = 0; i < blocks_to_produce; ++i )\n         {\n            signed_transaction trx;\n            trx.operations.emplace_back(transfer_operation(asset(1), account_id_type(i + 11), account_id_type(), asset(1), memo_data()));\n            db.push_transaction(trx, ~0);\n\n            aw = db.get_global_properties().active_witnesses;\n            b =  db.generate_block( db.get_slot_time( 1 ), db.get_scheduled_witness( 1 ), witness_priv_key, ~0 );\n         }\n         */\n         ilog(\"Pushed ${c} blocks (1 op each, no validation) in ${t} milliseconds.\",\n              (\"c\", blocks_out)(\"t\", (fc::time_point::now() - start_time).count() / 1000));\n\n         start_time = fc::time_point::now();\n         db.close();\n         ilog(\"Closed database in ${t} milliseconds.\", (\"t\", (fc::time_point::now() - start_time).count() / 1000));\n      }\n      {\n         database db;\n\n         auto start_time = fc::time_point::now();\n         wlog( \"about to start reindex...\" );\n         db.open(data_dir.path(), [&]{return genesis_state;}, \"force_wipe\");\n         ilog(\"Replayed database in ${t} milliseconds.\", (\"t\", (fc::time_point::now() - start_time).count() / 1000));\n\n         for( int i = 0; i < blocks_to_produce; ++i )\n            BOOST_CHECK(db.get_balance(account_id_type(i + 11), asset_id_type()).amount == GRAPHENE_MAX_SHARE_SUPPLY / account_count - 2);\n      }\n\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n"
  },
  {
    "path": "tests/benchmarks/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#define BOOST_TEST_MODULE \"C++ Benchmarks for BitShares Blockchain Database\"\n#include <boost/test/included/unit_test.hpp>\n\n"
  },
  {
    "path": "tests/cli/main.cpp",
    "content": "/*\n * Copyright (c) 2018 John Jones, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <graphene/app/application.hpp>\n#include <graphene/app/plugin.hpp>\n\n#include <graphene/utilities/key_conversion.hpp>\n#include <graphene/utilities/tempdir.hpp>\n\n#include <graphene/account_history/account_history_plugin.hpp>\n#include <graphene/api_helper_indexes/api_helper_indexes.hpp>\n#include <graphene/market_history/market_history_plugin.hpp>\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n#include <graphene/egenesis/egenesis.hpp>\n#include <graphene/wallet/wallet.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/thread/thread.hpp>\n#include <fc/network/http/websocket.hpp>\n#include <fc/rpc/websocket_api.hpp>\n#include <fc/rpc/cli.hpp>\n#include <fc/crypto/base58.hpp>\n#include <fc/crypto/hex.hpp>\n\n#include <fc/crypto/aes.hpp>\n\n#ifdef _WIN32\n   #ifndef _WIN32_WINNT\n      #define _WIN32_WINNT 0x0501\n   #endif\n   #include <winsock2.h>\n   #include <ws2tcpip.h>\n#else\n   #include <sys/types.h>\n   #include <sys/socket.h>\n   #include <netinet/in.h>\n   #include <netinet/ip.h>\n#endif\n#include <thread>\n\n#include <boost/filesystem/path.hpp>\n\n#define BOOST_TEST_MODULE Test Application\n#include <boost/test/included/unit_test.hpp>\n\n/*****\n * Global Initialization for Windows\n * ( sets up Winsock stuf )\n */\n#ifdef _WIN32\nint sockInit(void)\n{\n   WSADATA wsa_data;\n   return WSAStartup(MAKEWORD(1,1), &wsa_data);\n}\nint sockQuit(void)\n{\n   return WSACleanup();\n}\n#endif\n\n/*********************\n * Helper Methods\n *********************/\n\n#include \"../common/genesis_file_util.hpp\"\n\nusing std::exception;\nusing std::cerr;\n\n#define INVOKE(test) ((struct test*)this)->test_method();\n\n//////\n/// @brief attempt to find an available port on localhost\n/// @returns an available port number, or -1 on error\n/////\nint get_available_port()\n{\n   struct sockaddr_in sin;\n   int socket_fd = socket(AF_INET, SOCK_STREAM, 0);\n   if (socket_fd == -1)\n      return -1;\n   sin.sin_family = AF_INET;\n   sin.sin_port = 0;\n   sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n   if (::bind(socket_fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) == -1)\n      return -1;\n   socklen_t len = sizeof(sin);\n   if (getsockname(socket_fd, (struct sockaddr *)&sin, &len) == -1)\n      return -1;\n#ifdef _WIN32\n   closesocket(socket_fd);\n#else\n   close(socket_fd);\n#endif\n   return ntohs(sin.sin_port);\n}\n\n///////////\n/// @brief Start the application\n/// @param app_dir the temporary directory to use\n/// @param server_port_number to be filled with the rpc endpoint port number\n/// @returns the application object\n//////////\nstd::shared_ptr<graphene::app::application> start_application(fc::temp_directory& app_dir, int& server_port_number) {\n   std::shared_ptr<graphene::app::application> app1(new graphene::app::application{});\n\n   app1->register_plugin<graphene::account_history::account_history_plugin>(true);\n   app1->register_plugin< graphene::market_history::market_history_plugin >(true);\n   app1->register_plugin< graphene::grouped_orders::grouped_orders_plugin>(true);\n   app1->register_plugin< graphene::api_helper_indexes::api_helper_indexes>(true);\n   app1->register_plugin<graphene::custom_operations::custom_operations_plugin>(true);\n\n   app1->startup_plugins();\n   boost::program_options::variables_map cfg;\n#ifdef _WIN32\n   sockInit();\n#endif\n   server_port_number = get_available_port();\n   cfg.emplace(\n      \"rpc-endpoint\",\n      boost::program_options::variable_value(string(\"127.0.0.1:\" + std::to_string(server_port_number)), false)\n   );\n   cfg.emplace(\"genesis-json\", boost::program_options::variable_value(create_genesis_file(app_dir), false));\n   cfg.emplace(\"seed-nodes\", boost::program_options::variable_value(string(\"[]\"), false));\n   cfg.emplace(\"custom-operations-start-block\", boost::program_options::variable_value(uint32_t(1), false));\n   app1->initialize(app_dir.path(), cfg);\n\n   app1->initialize_plugins(cfg);\n   app1->startup_plugins();\n\n   app1->startup();\n   fc::usleep(fc::milliseconds(500));\n\n   return app1;\n}\n\n///////////\n/// Send a block to the db\n/// @param app the application\n/// @param returned_block the signed block\n/// @returns true on success\n///////////\nbool generate_block(std::shared_ptr<graphene::app::application> app, graphene::chain::signed_block& returned_block) \n{\n   try {\n      fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n      auto db = app->chain_database();\n      returned_block = db->generate_block( db->get_slot_time(1),\n                                         db->get_scheduled_witness(1),\n                                         committee_key,\n                                         database::skip_nothing );\n      return true;\n   } catch (exception &e) {\n      return false;\n   }\n}\n\nbool generate_block(std::shared_ptr<graphene::app::application> app)\n{\n   graphene::chain::signed_block returned_block;\n   return generate_block(app, returned_block);\n}\n\n\nsigned_block generate_block(std::shared_ptr<graphene::app::application> app, uint32_t skip, const fc::ecc::private_key& key, int miss_blocks)\n{\n   // skip == ~0 will skip checks specified in database::validation_steps\n   skip |= database::skip_undo_history_check;\n\n   auto db = app->chain_database();\n   auto block = db->generate_block(db->get_slot_time(miss_blocks + 1),\n                                   db->get_scheduled_witness(miss_blocks + 1),\n                                   key, skip);\n   db->clear_pending();\n   return block;\n}\n\n\n//////\n// Generate blocks until the timestamp\n//////\nuint32_t generate_blocks(std::shared_ptr<graphene::app::application> app, fc::time_point_sec timestamp)\n{\n   fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n   uint32_t skip = ~0;\n   auto db = app->chain_database();\n\n   generate_block(app);\n   auto slots_to_miss = db->get_slot_at_time(timestamp);\n   if( slots_to_miss <= 1 )\n      return 1;\n   --slots_to_miss;\n   generate_block(app, skip, committee_key, slots_to_miss);\n   return 2;\n}\n\n\n///////////\n/// @brief Skip intermediate blocks, and generate a maintenance block\n/// @param app the application\n/// @returns true on success\n///////////\nbool generate_maintenance_block(std::shared_ptr<graphene::app::application> app) {\n   try {\n      fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n      uint32_t skip = ~0;\n      auto db = app->chain_database();\n      auto maint_time = db->get_dynamic_global_properties().next_maintenance_time;\n      auto slots_to_miss = db->get_slot_at_time(maint_time);\n      db->generate_block(db->get_slot_time(slots_to_miss),\n            db->get_scheduled_witness(slots_to_miss),\n            committee_key,\n            skip);\n      return true;\n   } catch (exception& e)\n   {\n      return false;\n   }\n}\n\n///////////\n/// @brief a class to make connecting to the application server easier\n///////////\nclass client_connection\n{\npublic:\n   /////////\n   // constructor\n   /////////\n   client_connection(\n      std::shared_ptr<graphene::app::application> app,\n      const fc::temp_directory& data_dir,\n      const int server_port_number,\n      const std::string custom_wallet_filename = \"wallet.json\"\n   )\n   {\n      wallet_data.chain_id = app->chain_database()->get_chain_id();\n      wallet_data.ws_server = \"ws://127.0.0.1:\" + std::to_string(server_port_number);\n      wallet_data.ws_user = \"\";\n      wallet_data.ws_password = \"\";\n      websocket_connection  = websocket_client.connect( wallet_data.ws_server );\n\n      api_connection = std::make_shared<fc::rpc::websocket_api_connection>( websocket_connection,\n                                                                            GRAPHENE_MAX_NESTED_OBJECTS );\n\n      remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1);\n      BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) );\n\n      wallet_api_ptr = std::make_shared<graphene::wallet::wallet_api>(wallet_data, remote_login_api);\n      wallet_filename = data_dir.path().generic_string() + \"/\" + custom_wallet_filename;\n      wallet_api_ptr->set_wallet_filename(wallet_filename);\n\n      wallet_api = fc::api<graphene::wallet::wallet_api>(wallet_api_ptr);\n\n      wallet_cli = std::make_shared<fc::rpc::cli>(GRAPHENE_MAX_NESTED_OBJECTS);\n      for( auto& name_formatter : wallet_api_ptr->get_result_formatters() )\n         wallet_cli->format_result( name_formatter.first, name_formatter.second );\n   }\n   ~client_connection()\n   {\n      wallet_cli->stop();\n   }\npublic:\n   fc::http::websocket_client websocket_client;\n   graphene::wallet::wallet_data wallet_data;\n   fc::http::websocket_connection_ptr websocket_connection;\n   std::shared_ptr<fc::rpc::websocket_api_connection> api_connection;\n   fc::api<login_api> remote_login_api;\n   std::shared_ptr<graphene::wallet::wallet_api> wallet_api_ptr;\n   fc::api<graphene::wallet::wallet_api> wallet_api;\n   std::shared_ptr<fc::rpc::cli> wallet_cli;\n   std::string wallet_filename;\n};\n\n///////////////////////////////\n// Cli Wallet Fixture\n///////////////////////////////\n\nstruct cli_fixture\n{\n   class dummy\n   {\n   public:\n      ~dummy()\n      {\n         // wait for everything to finish up\n         fc::usleep(fc::milliseconds(500));\n      }\n   };\n   dummy dmy;\n   int server_port_number;\n   fc::temp_directory app_dir;\n   std::shared_ptr<graphene::app::application> app1;\n   client_connection con;\n   std::vector<std::string> nathan_keys;\n\n   cli_fixture() :\n      server_port_number(0),\n      app_dir( graphene::utilities::temp_directory_path() ),\n      app1( start_application(app_dir, server_port_number) ),\n      con( app1, app_dir, server_port_number ),\n      nathan_keys( {\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"} )\n   {\n      BOOST_TEST_MESSAGE(\"Setup cli_wallet::boost_fixture_test_case\");\n\n      using namespace graphene::chain;\n      using namespace graphene::app;\n\n      try\n      {\n         BOOST_TEST_MESSAGE(\"Setting wallet password\");\n         con.wallet_api_ptr->set_password(\"supersecret\");\n         con.wallet_api_ptr->unlock(\"supersecret\");\n\n         // import Nathan account\n         BOOST_TEST_MESSAGE(\"Importing nathan key\");\n         BOOST_CHECK_EQUAL(nathan_keys[0], \"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\");\n         BOOST_CHECK(con.wallet_api_ptr->import_key(\"nathan\", nathan_keys[0]));\n      } catch( fc::exception& e ) {\n         edump((e.to_detail_string()));\n         throw;\n      }\n   }\n\n   ~cli_fixture()\n   {\n      BOOST_TEST_MESSAGE(\"Cleanup cli_wallet::boost_fixture_test_case\");\n      app1->shutdown();\n#ifdef _WIN32\n      sockQuit();\n#endif\n   }\n};\n\n///////////////////////////////\n// Tests\n///////////////////////////////\n\n////////////////\n// Start a server and connect using the same calls as the CLI\n////////////////\nBOOST_FIXTURE_TEST_CASE( cli_connect, cli_fixture )\n{\n   BOOST_TEST_MESSAGE(\"Testing wallet connection.\");\n}\n\n////////////////\n// Start a server and connect using the same calls as the CLI\n// Quit wallet and be sure that file was saved correctly\n////////////////\nBOOST_FIXTURE_TEST_CASE( cli_quit, cli_fixture )\n{\n   BOOST_TEST_MESSAGE(\"Testing wallet connection and quit command.\");\n   BOOST_CHECK_THROW( con.wallet_api_ptr->quit(), fc::canceled_exception );\n}\n\nBOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture )\n{\n   try\n   {\n      BOOST_TEST_MESSAGE(\"Upgrade Nathan's account\");\n\n      account_object nathan_acct_before_upgrade, nathan_acct_after_upgrade;\n      std::vector<signed_transaction> import_txs;\n      signed_transaction upgrade_tx;\n\n      BOOST_TEST_MESSAGE(\"Importing nathan's balance\");\n      import_txs = con.wallet_api_ptr->import_balance(\"nathan\", nathan_keys, true);\n      nathan_acct_before_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // upgrade nathan\n      BOOST_TEST_MESSAGE(\"Upgrading Nathan to LTM\");\n      upgrade_tx = con.wallet_api_ptr->upgrade_account(\"nathan\", true);\n      nathan_acct_after_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // verify that the upgrade was successful\n      BOOST_CHECK_PREDICATE(\n         std::not_equal_to<uint32_t>(),\n         (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())\n         (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch())\n      );\n      BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture )\n{\n   try\n   {\n      INVOKE(upgrade_nathan_account);\n\n      // create a new account\n      graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key();\n      BOOST_CHECK(!bki.brain_priv_key.empty());\n      signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(\n         bki.brain_priv_key, \"jmjatlanta\", \"nathan\", \"nathan\", true\n      );\n      // save the private key for this new account in the wallet file\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"jmjatlanta\", bki.wif_priv_key));\n      con.wallet_api_ptr->save_wallet_file(con.wallet_filename);\n\n      // attempt to give jmjatlanta some bitshares\n      BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to jmjatlanta\");\n      signed_transaction transfer_tx = con.wallet_api_ptr->transfer(\n         \"nathan\", \"jmjatlanta\", \"10000\", \"1.3.0\", \"Here are some CORE token for your new account\", true\n      );\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n///////////////////////\n// Start a server and connect using the same calls as the CLI\n// Vote for two witnesses, and make sure they both stay there\n// after a maintenance block\n///////////////////////\nBOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )\n{\n   try\n   {\n      BOOST_TEST_MESSAGE(\"Cli Vote Test for 2 Witnesses\");\n\n      INVOKE(create_new_account);\n\n      // get the details for init1\n      witness_object init1_obj = con.wallet_api_ptr->get_witness(\"init1\");\n      int init1_start_votes = init1_obj.total_votes;\n      // Vote for a witness\n      signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness(\"jmjatlanta\", \"init1\", true, true);\n\n      // generate a block to get things started\n      BOOST_CHECK(generate_block(app1));\n      // wait for a maintenance interval\n      BOOST_CHECK(generate_maintenance_block(app1));\n\n      // Verify that the vote is there\n      init1_obj = con.wallet_api_ptr->get_witness(\"init1\");\n      witness_object init2_obj = con.wallet_api_ptr->get_witness(\"init2\");\n      int init1_middle_votes = init1_obj.total_votes;\n      BOOST_CHECK(init1_middle_votes > init1_start_votes);\n\n      // Vote for a 2nd witness\n      int init2_start_votes = init2_obj.total_votes;\n      signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness(\"jmjatlanta\", \"init2\", true, true);\n\n      // send another block to trigger maintenance interval\n      BOOST_CHECK(generate_maintenance_block(app1));\n\n      // Verify that both the first vote and the 2nd are there\n      init2_obj = con.wallet_api_ptr->get_witness(\"init2\");\n      init1_obj = con.wallet_api_ptr->get_witness(\"init1\");\n\n      int init2_middle_votes = init2_obj.total_votes;\n      BOOST_CHECK(init2_middle_votes > init2_start_votes);\n      int init1_last_votes = init1_obj.total_votes;\n      BOOST_CHECK(init1_last_votes > init1_start_votes);\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture )\n{\n   try\n   {\n      INVOKE(upgrade_nathan_account);\n\n      // register account and transfer funds\n      const auto test_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n         \"test\", test_bki.pub_key, test_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      con.wallet_api_ptr->transfer(\"nathan\", \"test\", \"1000\", \"1.3.0\", \"\", true);\n\n      // import key and save wallet\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"test\", test_bki.wif_priv_key));\n      con.wallet_api_ptr->save_wallet_file(con.wallet_filename);\n\n      // create transaction and check expected result\n      auto signed_trx = con.wallet_api_ptr->transfer(\"test\", \"nathan\", \"10\", \"1.3.0\", \"\", true);\n\n      const auto &test_acc = con.wallet_api_ptr->get_account(\"test\");\n      flat_set<public_key_type> expected_signers = {test_bki.pub_key};\n      vector<flat_set<account_id_type> > expected_key_refs{{test_acc.id, test_acc.id}};\n\n      auto signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);\n      BOOST_CHECK(signers == expected_signers);\n\n      auto key_refs = con.wallet_api_ptr->get_key_references({expected_signers.begin(), expected_signers.end()});\n      BOOST_CHECK(key_refs == expected_key_refs);\n\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\n///////////////////////\n// Wallet RPC\n// Test adding an unnecessary signature to a transaction\n///////////////////////\nBOOST_FIXTURE_TEST_CASE(cli_sign_tx_with_unnecessary_signature, cli_fixture) {\n   try {\n      auto db = app1->chain_database();\n\n      account_object nathan_acct = con.wallet_api_ptr->get_account(\"nathan\");\n      INVOKE(upgrade_nathan_account);\n\n      // Register Bob account\n      const auto bob_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"bob\", bob_bki.pub_key, bob_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n\n      // Register Charlie account\n      const graphene::wallet::brain_key_info charlie_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"charlie\", charlie_bki.pub_key, charlie_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      const account_object &charlie_acc = con.wallet_api_ptr->get_account(\"charlie\");\n\n      // Import Bob's key\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"bob\", bob_bki.wif_priv_key));\n\n      // Create transaction with a transfer operation from Nathan to Charlie\n      transfer_operation top;\n      top.from = nathan_acct.id;\n      top.to = charlie_acc.id;\n      top.amount = asset(5000);\n      top.fee = db->current_fee_schedule().calculate_fee(top);\n\n      signed_transaction test_tx;\n      test_tx.operations.push_back(top);\n\n      // Sign the transaction with the implied nathan's key and the explicitly yet unnecessary Bob's key\n      auto signed_trx = con.wallet_api_ptr->sign_transaction2(test_tx, {bob_bki.pub_key}, false);\n\n      // Check for two signatures on the transaction\n      BOOST_CHECK_EQUAL(signed_trx.signatures.size(), 2);\n      flat_set<public_key_type> signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);\n\n      // Check that the signed transaction contains both Nathan's required signature and Bob's unnecessary signature\n      BOOST_CHECK_EQUAL(nathan_acct.active.get_keys().size(), 1);\n      flat_set<public_key_type> expected_signers = {bob_bki.pub_key, nathan_acct.active.get_keys().front()};\n      flat_set<public_key_type> actual_signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);\n      BOOST_CHECK(signers == expected_signers);\n\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\n///////////////////////\n// Wallet RPC\n// Test adding an unnecessary signature to a transaction builder\n///////////////////////\nBOOST_FIXTURE_TEST_CASE(cli_sign_tx_builder_with_unnecessary_signature, cli_fixture) {\n   try {\n      auto db = app1->chain_database();\n\n      account_object nathan_acct = con.wallet_api_ptr->get_account(\"nathan\");\n      INVOKE(upgrade_nathan_account);\n\n      // Register Bob account\n      const auto bob_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"bob\", bob_bki.pub_key, bob_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n\n      // Register Charlie account\n      const graphene::wallet::brain_key_info charlie_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"charlie\", charlie_bki.pub_key, charlie_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      const account_object &charlie_acc = con.wallet_api_ptr->get_account(\"charlie\");\n\n      // Import Bob's key\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"bob\", bob_bki.wif_priv_key));\n\n      // Use transaction builder to build a transaction with a transfer operation from Nathan to Charlie\n      graphene::wallet::transaction_handle_type tx_handle = con.wallet_api_ptr->begin_builder_transaction();\n\n      transfer_operation top;\n      top.from = nathan_acct.id;\n      top.to = charlie_acc.id;\n      top.amount = asset(5000);\n\n      con.wallet_api_ptr->add_operation_to_builder_transaction(tx_handle, top);\n      con.wallet_api_ptr->set_fees_on_builder_transaction(tx_handle, GRAPHENE_SYMBOL);\n\n      // Sign the transaction with the implied nathan's key and the explicitly yet unnecessary Bob's key\n      auto signed_trx = con.wallet_api_ptr->sign_builder_transaction2(tx_handle, {bob_bki.pub_key}, false);\n\n      // Check for two signatures on the transaction\n      BOOST_CHECK_EQUAL(signed_trx.signatures.size(), 2);\n      flat_set<public_key_type> signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);\n\n      // Check that the signed transaction contains both Nathan's required signature and Bob's unnecessary signature\n      BOOST_CHECK_EQUAL(nathan_acct.active.get_keys().size(), 1);\n      flat_set<public_key_type> expected_signers = {bob_bki.pub_key, nathan_acct.active.get_keys().front()};\n      flat_set<public_key_type> actual_signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);\n      BOOST_CHECK(signers == expected_signers);\n\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\nBOOST_FIXTURE_TEST_CASE( cli_get_available_transaction_signers, cli_fixture )\n{\n   try\n   {\n      INVOKE(upgrade_nathan_account);\n\n      // register account\n      const auto test_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n         \"test\", test_bki.pub_key, test_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      const auto &test_acc = con.wallet_api_ptr->get_account(\"test\");\n\n      // create and sign transaction\n      signed_transaction trx;\n      trx.operations = {transfer_operation()};\n\n      // sign with test key\n      const auto test_privkey = wif_to_key( test_bki.wif_priv_key );\n      BOOST_REQUIRE( test_privkey );\n      trx.sign( *test_privkey, con.wallet_data.chain_id );\n\n      // sign with other keys\n      const auto privkey_1 = fc::ecc::private_key::generate();\n      trx.sign( privkey_1, con.wallet_data.chain_id );\n\n      const auto privkey_2 = fc::ecc::private_key::generate();\n      trx.sign( privkey_2, con.wallet_data.chain_id );\n\n      // verify expected result\n      flat_set<public_key_type> expected_signers = {test_bki.pub_key, \n                                                    privkey_1.get_public_key(), \n                                                    privkey_2.get_public_key()};\n\n      auto signers = con.wallet_api_ptr->get_transaction_signers(trx);\n      BOOST_CHECK(signers == expected_signers);\n\n      // blockchain has no references to unknown accounts (privkey_1, privkey_2)\n      // only test account available\n      vector<flat_set<account_id_type> > expected_key_refs;\n      expected_key_refs.push_back(flat_set<account_id_type>());\n      expected_key_refs.push_back(flat_set<account_id_type>());\n      expected_key_refs.push_back({test_acc.id});\n\n      auto key_refs = con.wallet_api_ptr->get_key_references({expected_signers.begin(), expected_signers.end()});\n      std::sort(key_refs.begin(), key_refs.end());\n\n      BOOST_CHECK(key_refs == expected_key_refs);\n\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( cli_cant_get_signers_from_modified_transaction, cli_fixture )\n{\n   try\n   {\n      INVOKE(upgrade_nathan_account);\n\n      // register account\n      const auto test_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n         \"test\", test_bki.pub_key, test_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n\n      // create and sign transaction\n      signed_transaction trx;\n      trx.operations = {transfer_operation()};\n\n      // sign with test key\n      const auto test_privkey = wif_to_key( test_bki.wif_priv_key );\n      BOOST_REQUIRE( test_privkey );\n      trx.sign( *test_privkey, con.wallet_data.chain_id );\n\n      // modify transaction (MITM-attack)\n      trx.operations.clear();\n\n      // verify if transaction has no valid signature of test account \n      flat_set<public_key_type> expected_signers_of_valid_transaction = {test_bki.pub_key};\n      auto signers = con.wallet_api_ptr->get_transaction_signers(trx);\n      BOOST_CHECK(signers != expected_signers_of_valid_transaction);\n\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n///////////////////\n// Start a server and connect using the same calls as the CLI\n// Set a voting proxy and be assured that it sticks\n///////////////////\nBOOST_FIXTURE_TEST_CASE( cli_set_voting_proxy, cli_fixture )\n{\n   try {\n      INVOKE(create_new_account);\n\n      // grab account for comparison\n      account_object prior_voting_account = con.wallet_api_ptr->get_account(\"jmjatlanta\");\n      // set the voting proxy to nathan\n      BOOST_TEST_MESSAGE(\"About to set voting proxy.\");\n      signed_transaction voting_tx = con.wallet_api_ptr->set_voting_proxy(\"jmjatlanta\", \"nathan\", true);\n      account_object after_voting_account = con.wallet_api_ptr->get_account(\"jmjatlanta\");\n      // see if it changed\n      BOOST_CHECK(prior_voting_account.options.voting_account != after_voting_account.options.voting_account);\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n///////////////////\n// Test blind transactions and mantissa length of range proofs.\n///////////////////\nBOOST_FIXTURE_TEST_CASE( cli_confidential_tx_test, cli_fixture )\n{\n   using namespace graphene::wallet;\n   try {\n      // we need to increase the default max transaction size to run this test.\n      this->app1->chain_database()->modify(\n         this->app1->chain_database()->get_global_properties(), \n         []( global_property_object& p) {\n            p.parameters.maximum_transaction_size = 8192;\n      });      \n      std::vector<signed_transaction> import_txs;\n\n      BOOST_TEST_MESSAGE(\"Importing nathan's balance\");\n      import_txs = con.wallet_api_ptr->import_balance(\"nathan\", nathan_keys, true);\n\n      unsigned int head_block = 0;\n      auto & W = *con.wallet_api_ptr; // Wallet alias\n\n      BOOST_TEST_MESSAGE(\"Creating blind accounts\");\n      graphene::wallet::brain_key_info bki_nathan = W.suggest_brain_key();\n      graphene::wallet::brain_key_info bki_alice = W.suggest_brain_key();\n      graphene::wallet::brain_key_info bki_bob = W.suggest_brain_key();\n      W.create_blind_account(\"nathan\", bki_nathan.brain_priv_key);\n      W.create_blind_account(\"alice\", bki_alice.brain_priv_key);\n      W.create_blind_account(\"bob\", bki_bob.brain_priv_key);\n      BOOST_CHECK(W.get_blind_accounts().size() == 3);\n\n      // ** Block 1: Import Nathan account:\n      BOOST_TEST_MESSAGE(\"Importing nathan key and balance\");\n      std::vector<std::string> nathan_keys{\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"};\n      W.import_key(\"nathan\", nathan_keys[0]);\n      W.import_balance(\"nathan\", nathan_keys, true);\n      generate_block(app1); head_block++;\n\n      // ** Block 2: Nathan will blind 100M CORE token:\n      BOOST_TEST_MESSAGE(\"Blinding a large balance\");\n      W.transfer_to_blind(\"nathan\", GRAPHENE_SYMBOL, {{\"nathan\",\"100000000\"}}, true);\n      BOOST_CHECK( W.get_blind_balances(\"nathan\")[0].amount == 10000000000000 );\n      generate_block(app1); head_block++;\n\n      // ** Block 3: Nathan will send 1M CORE token to alice and 10K CORE token to bob. We\n      // then confirm that balances are received, and then analyze the range\n      // prooofs to make sure the mantissa length does not reveal approximate\n      // balance (issue #480).\n      std::map<std::string, share_type> to_list = {{\"alice\",100000000000},\n                                                   {\"bob\",    1000000000}};\n      vector<blind_confirmation> bconfs;\n      asset_object core_asset = W.get_asset(\"1.3.0\");\n      BOOST_TEST_MESSAGE(\"Sending blind transactions to alice and bob\");\n      for (auto to : to_list) {\n         string amount = core_asset.amount_to_string(to.second);\n         bconfs.push_back(W.blind_transfer(\"nathan\",to.first,amount,core_asset.symbol,true));\n         BOOST_CHECK( W.get_blind_balances(to.first)[0].amount == to.second );\n      }\n      BOOST_TEST_MESSAGE(\"Inspecting range proof mantissa lengths\");\n      vector<int> rp_mantissabits;\n      for (auto conf : bconfs) {\n         for (auto out : conf.trx.operations[0].get<blind_transfer_operation>().outputs) {\n            rp_mantissabits.push_back(1+out.range_proof[1]); // 2nd byte encodes mantissa length\n         }\n      }\n      // We are checking the mantissa length of the range proofs for several Pedersen\n      // commitments of varying magnitude.  We don't want the mantissa lengths to give\n      // away magnitude.  Deprecated wallet behavior was to use \"just enough\" mantissa\n      // bits to prove range, but this gives away value to within a factor of two. As a\n      // naive test, we assume that if all mantissa lengths are equal, then they are not\n      // revealing magnitude.  However, future more-sophisticated wallet behavior\n      // *might* randomize mantissa length to achieve some space savings in the range\n      // proof.  The following test will fail in that case and a more sophisticated test\n      // will be needed.\n      auto adjacent_unequal = std::adjacent_find(rp_mantissabits.begin(),\n                                                 rp_mantissabits.end(),         // find unequal adjacent values\n                                                 std::not_equal_to<int>());\n      BOOST_CHECK(adjacent_unequal == rp_mantissabits.end());\n      generate_block(app1); head_block++;\n\n      // ** Check head block:\n      BOOST_TEST_MESSAGE(\"Check that all expected blocks have processed\");\n      dynamic_global_property_object dgp = W.get_dynamic_global_properties();\n      BOOST_CHECK(dgp.head_block_number == head_block);\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/******\n * Check account history pagination (see bitshares-core/issue/1176)\n */\nBOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )\n{\n   try\n   {\n      INVOKE(create_new_account);\n\n      // attempt to give jmjatlanta some bitshares\n      BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to jmjatlanta\");\n      for(int i = 1; i <= 199; i++)\n      {\n         signed_transaction transfer_tx = con.wallet_api_ptr->transfer(\"nathan\", \"jmjatlanta\", std::to_string(i),\n                                                \"1.3.0\", \"Here are some CORE token for your new account\", true);\n      }\n\n      BOOST_CHECK(generate_block(app1));\n\n      // now get account history and make sure everything is there (and no duplicates)\n      std::vector<graphene::wallet::operation_detail> history = con.wallet_api_ptr->get_account_history(\"jmjatlanta\", 300);\n      BOOST_CHECK_EQUAL(201u, history.size() );\n\n      std::set<object_id_type> operation_ids;\n\n      for(auto& op : history)\n      {\n         if( operation_ids.find(op.op.id) != operation_ids.end() )\n         {\n            BOOST_FAIL(\"Duplicate found\");\n         }\n         operation_ids.insert(op.op.id);\n      }\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\n///////////////////////\n// Create a multi-sig account and verify that only when all signatures are\n// signed, the transaction could be broadcast\n///////////////////////\nBOOST_AUTO_TEST_CASE( cli_multisig_transaction )\n{\n   using namespace graphene::chain;\n   using namespace graphene::app;\n   std::shared_ptr<graphene::app::application> app1;\n   try {\n      fc::temp_directory app_dir( graphene::utilities::temp_directory_path() );\n\n      int server_port_number = 0;\n      app1 = start_application(app_dir, server_port_number);\n\n      // connect to the server\n      client_connection con(app1, app_dir, server_port_number);\n\n      BOOST_TEST_MESSAGE(\"Setting wallet password\");\n      con.wallet_api_ptr->set_password(\"supersecret\");\n      con.wallet_api_ptr->unlock(\"supersecret\");\n\n      // import Nathan account\n      BOOST_TEST_MESSAGE(\"Importing nathan key\");\n      std::vector<std::string> nathan_keys{\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"};\n      BOOST_CHECK_EQUAL(nathan_keys[0], \"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\");\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"nathan\", nathan_keys[0]));\n\n      BOOST_TEST_MESSAGE(\"Importing nathan's balance\");\n      std::vector<signed_transaction> import_txs = con.wallet_api_ptr->import_balance(\"nathan\", nathan_keys, true);\n      account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // upgrade nathan\n      BOOST_TEST_MESSAGE(\"Upgrading Nathan to LTM\");\n      signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account(\"nathan\", true);\n      account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // verify that the upgrade was successful\n      BOOST_CHECK_PREDICATE( std::not_equal_to<uint32_t>(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())(nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) );\n      BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());\n\n      // create a new multisig account\n      graphene::wallet::brain_key_info bki1 = con.wallet_api_ptr->suggest_brain_key();\n      graphene::wallet::brain_key_info bki2 = con.wallet_api_ptr->suggest_brain_key();\n      graphene::wallet::brain_key_info bki3 = con.wallet_api_ptr->suggest_brain_key();\n      graphene::wallet::brain_key_info bki4 = con.wallet_api_ptr->suggest_brain_key();\n      BOOST_CHECK(!bki1.brain_priv_key.empty());\n      BOOST_CHECK(!bki2.brain_priv_key.empty());\n      BOOST_CHECK(!bki3.brain_priv_key.empty());\n      BOOST_CHECK(!bki4.brain_priv_key.empty());\n\n      signed_transaction create_multisig_acct_tx;\n      account_create_operation account_create_op;\n\n      account_create_op.referrer = nathan_acct_after_upgrade.id;\n      account_create_op.referrer_percent = nathan_acct_after_upgrade.referrer_rewards_percentage;\n      account_create_op.registrar = nathan_acct_after_upgrade.id;\n      account_create_op.name = \"cifer.test\";\n      account_create_op.owner = authority(1, bki1.pub_key, 1);\n      account_create_op.active = authority(2, bki2.pub_key, 1, bki3.pub_key, 1);\n      account_create_op.options.memo_key = bki4.pub_key;\n      account_create_op.fee = asset(1000000);  // should be enough for creating account\n\n      create_multisig_acct_tx.operations.push_back(account_create_op);\n      con.wallet_api_ptr->sign_transaction(create_multisig_acct_tx, true);\n\n      // attempt to give cifer.test some bitshares\n      BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to cifer.test\");\n      signed_transaction transfer_tx1 = con.wallet_api_ptr->transfer(\"nathan\", \"cifer.test\", \"10000\", \"1.3.0\", \"Here are some BTS for your new account\", true);\n\n      // transfer bts from cifer.test to nathan\n      BOOST_TEST_MESSAGE(\"Transferring bitshares from cifer.test to nathan\");\n      auto dyn_props = app1->chain_database()->get_dynamic_global_properties();\n      account_object cifer_test = con.wallet_api_ptr->get_account(\"cifer.test\");\n\n      // construct a transfer transaction\n      signed_transaction transfer_tx2;\n      transfer_operation xfer_op;\n      xfer_op.from = cifer_test.id;\n      xfer_op.to = nathan_acct_after_upgrade.id;\n      xfer_op.amount = asset(100000000);\n      xfer_op.fee = asset(3000000);  // should be enough for transfer\n      transfer_tx2.operations.push_back(xfer_op);\n\n      // case1: sign a transaction without TaPoS and expiration fields\n      // expect: return a transaction with TaPoS and expiration filled\n      transfer_tx2 =\n         con.wallet_api_ptr->add_transaction_signature( transfer_tx2, false );\n      BOOST_CHECK( ( transfer_tx2.ref_block_num != 0 &&\n                     transfer_tx2.ref_block_prefix != 0 ) ||\n                   ( transfer_tx2.expiration != fc::time_point_sec() ) );\n\n      // case2: broadcast without signature\n      // expect: exception with missing active authority\n      BOOST_CHECK_THROW(con.wallet_api_ptr->broadcast_transaction(transfer_tx2), fc::exception);\n\n      // case3:\n      // import one of the private keys for this new account in the wallet file,\n      // sign and broadcast with partial signatures\n      //\n      // expect: exception with missing active authority\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"cifer.test\", bki2.wif_priv_key));\n      BOOST_CHECK_THROW(con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true), fc::exception);\n\n      // case4: sign again as signature exists\n      // expect: num of signatures not increase\n      transfer_tx2 = con.wallet_api_ptr->add_transaction_signature(transfer_tx2, false);\n      BOOST_CHECK_EQUAL(transfer_tx2.signatures.size(), 1);\n\n      // case5:\n      // import another private key, sign and broadcast without full signatures\n      //\n      // expect: transaction broadcast successfully\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"cifer.test\", bki3.wif_priv_key));\n      con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true);\n      auto balances = con.wallet_api_ptr->list_account_balances( \"cifer.test\" );\n      for (auto b : balances) {\n         if (b.asset_id == asset_id_type()) {\n            BOOST_CHECK(b == asset(900000000 - 3000000));\n         }\n      }\n\n      // wait for everything to finish up\n      fc::usleep(fc::seconds(1));\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n   app1->shutdown();\n   app1.reset();\n   // Intentional delay after app1->shutdown\n   std::cout << \"cli_multisig_transaction conclusion: Intentional delay\" << std::endl;\n   fc::usleep(fc::seconds(1));\n}\n\ngraphene::wallet::plain_keys decrypt_keys( const std::string& password, const vector<char>& cipher_keys )\n{\n   auto pw = fc::sha512::hash( password.c_str(), password.size() );\n   vector<char> decrypted = fc::aes_decrypt( pw, cipher_keys );\n   return fc::raw::unpack<graphene::wallet::plain_keys>( decrypted );\n}\n\nBOOST_AUTO_TEST_CASE( saving_keys_wallet_test ) {\n   cli_fixture cli;\n\n   cli.con.wallet_api_ptr->import_balance( \"nathan\", cli.nathan_keys, true );\n   cli.con.wallet_api_ptr->upgrade_account( \"nathan\", true );\n   std::string brain_key( \"FICTIVE WEARY MINIBUS LENS HAWKIE MAIDISH MINTY GLYPH GYTE KNOT COCKSHY LENTIGO PROPS BIFORM KHUTBAH BRAZIL\" );\n   cli.con.wallet_api_ptr->create_account_with_brain_key( brain_key, \"account1\", \"nathan\", \"nathan\", true );\n\n   BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( \"nathan\", \"account1\", \"9000\", \"1.3.0\", \"\", true ) );\n\n   std::string path( cli.app_dir.path().generic_string() + \"/wallet.json\" );\n   graphene::wallet::wallet_data wallet = fc::json::from_file( path ).as<graphene::wallet::wallet_data>( 2 * GRAPHENE_MAX_NESTED_OBJECTS );\n   BOOST_CHECK( wallet.extra_keys.size() == 1 ); // nathan\n   BOOST_CHECK( wallet.pending_account_registrations.size() == 1 ); // account1\n   BOOST_CHECK( wallet.pending_account_registrations[\"account1\"].size() == 2 ); // account1 active key + account1 memo key\n\n   graphene::wallet::plain_keys pk = decrypt_keys( \"supersecret\", wallet.cipher_keys );\n   BOOST_CHECK( pk.keys.size() == 1 ); // nathan key\n\n   BOOST_CHECK( generate_block( cli.app1 ) );\n   fc::usleep( fc::seconds(1) );\n\n   wallet = fc::json::from_file( path ).as<graphene::wallet::wallet_data>( 2 * GRAPHENE_MAX_NESTED_OBJECTS );\n   BOOST_CHECK( wallet.extra_keys.size() == 2 ); // nathan + account1\n   BOOST_CHECK( wallet.pending_account_registrations.empty() );\n   BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( \"account1\", \"nathan\", \"1000\", \"1.3.0\", \"\", true ) );\n\n   pk = decrypt_keys( \"supersecret\", wallet.cipher_keys );\n   BOOST_CHECK( pk.keys.size() == 3 ); // nathan key + account1 active key + account1 memo key\n}\n\n\n///////////////////////\n// Start a server and connect using the same calls as the CLI\n// Create an HTLC\n///////////////////////\nBOOST_AUTO_TEST_CASE( cli_create_htlc )\n{\n   using namespace graphene::chain;\n   using namespace graphene::app;\n   std::shared_ptr<graphene::app::application> app1;\n   try {\n      fc::temp_directory app_dir( graphene::utilities::temp_directory_path() );\n\n      int server_port_number = 0;\n      app1 = start_application(app_dir, server_port_number);\n      // set committee parameters\n      app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p) {\n         graphene::chain::htlc_options params;\n         params.max_preimage_size = 1024;\n         params.max_timeout_secs = 60 * 60 * 24 * 28;\n         p.parameters.extensions.value.updatable_htlc_options = params;\n      });\n\n      // connect to the server\n      client_connection con(app1, app_dir, server_port_number);\n\n      BOOST_TEST_MESSAGE(\"Setting wallet password\");\n      con.wallet_api_ptr->set_password(\"supersecret\");\n      con.wallet_api_ptr->unlock(\"supersecret\");\n\n      // import Nathan account\n      BOOST_TEST_MESSAGE(\"Importing nathan key\");\n      std::vector<std::string> nathan_keys{\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"};\n      BOOST_CHECK_EQUAL(nathan_keys[0], \"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\");\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"nathan\", nathan_keys[0]));\n\n      BOOST_TEST_MESSAGE(\"Importing nathan's balance\");\n      std::vector<signed_transaction> import_txs = con.wallet_api_ptr->import_balance(\"nathan\", nathan_keys, true);\n      account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // upgrade nathan\n      BOOST_TEST_MESSAGE(\"Upgrading Nathan to LTM\");\n      signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account(\"nathan\", true);\n      account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // verify that the upgrade was successful\n      BOOST_CHECK_PREDICATE( std::not_equal_to<uint32_t>(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())\n            (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) );\n      BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());\n\n      // Create new asset called BOBCOIN\n      try \n      {\n         graphene::chain::asset_options asset_ops;\n         asset_ops.max_supply = 1000000;\n         asset_ops.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1)));\n         fc::optional<graphene::chain::bitasset_options> bit_opts;\n         con.wallet_api_ptr->create_asset(\"nathan\", \"BOBCOIN\", 5, asset_ops, bit_opts, true);\n      }\n      catch(exception& e)\n      {\n         BOOST_FAIL(e.what());\n      }\n      catch(...)\n      {\n         BOOST_FAIL(\"Unknown exception creating BOBCOIN\");\n      }\n\n      // create a new account for Alice\n      {\n         graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key();\n         BOOST_CHECK(!bki.brain_priv_key.empty());\n         signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, \"alice\", \n               \"nathan\", \"nathan\", true);\n         con.wallet_api_ptr->save_wallet_file(con.wallet_filename);\n         // attempt to give alice some bitshares\n         BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to alice\");\n         signed_transaction transfer_tx = con.wallet_api_ptr->transfer(\"nathan\", \"alice\", \"10000\", \"1.3.0\", \n               \"Here are some CORE token for your new account\", true);\n      }\n\n      // create a new account for Bob\n      {\n         graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key();\n         BOOST_CHECK(!bki.brain_priv_key.empty());\n         signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, \"bob\", \n               \"nathan\", \"nathan\", true);\n         // this should cause resync which will import the keys of alice and bob\n         generate_block(app1);\n         // attempt to give bob some bitshares\n         BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to Bob\");\n         signed_transaction transfer_tx = con.wallet_api_ptr->transfer(\"nathan\", \"bob\", \"10000\", \"1.3.0\", \n               \"Here are some CORE token for your new account\", true);\n         con.wallet_api_ptr->issue_asset(\"bob\", \"5\", \"BOBCOIN\", \"Here are your BOBCOINs\", true);\n      }\n\n      BOOST_TEST_MESSAGE(\"Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC\");\n      // create an HTLC\n      std::string preimage_string = \"My Secret\";\n      fc::sha256 preimage_md = fc::sha256::hash(preimage_string);\n      std::stringstream ss;\n      for(size_t i = 0; i < preimage_md.data_size(); i++)\n      {\n         char d = preimage_md.data()[i];\n         unsigned char uc = static_cast<unsigned char>(d);\n         ss << std::setfill('0') << std::setw(2) << std::hex << (int)uc;\n      }\n      std::string hash_str = ss.str();\n      BOOST_TEST_MESSAGE(\"Secret is \" + preimage_string + \" and hash is \" + hash_str);\n      uint32_t timelock = fc::days(1).to_seconds();\n      graphene::chain::signed_transaction result_tx \n            = con.wallet_api_ptr->htlc_create(\"alice\", \"bob\", \n            \"3\", \"1.3.0\", \"SHA256\", hash_str, preimage_string.size(), timelock, \"\", true);\n\n      // normally, a wallet would watch block production, and find the transaction. Here, we can cheat:\n      std::string alice_htlc_id_as_string;\n      {\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         graphene::chain::signed_block result_block;\n         BOOST_CHECK(generate_block(app1, result_block));\n\n         // get the ID:\n         htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get<object_id_type>();\n         alice_htlc_id_as_string = (std::string)(object_id_type)htlc_id;\n         BOOST_TEST_MESSAGE(\"Alice shares the HTLC ID with Bob. The HTLC ID is: \" + alice_htlc_id_as_string);\n      }\n\n      // Bob can now look over Alice's HTLC, to see if it is what was agreed to.\n      BOOST_TEST_MESSAGE(\"Bob retrieves the HTLC Object by ID to examine it.\");\n      auto alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string);\n      BOOST_TEST_MESSAGE(\"The HTLC Object is: \" + fc::json::to_pretty_string(alice_htlc));\n\n      // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC\n      con.wallet_api_ptr->htlc_create(\"bob\", \"alice\",\n            \"3\", \"BOBCOIN\", \"SHA256\", hash_str, preimage_string.size(), timelock, \"\", true);\n\n      // normally, a wallet would watch block production, and find the transaction. Here, we can cheat:\n      std::string bob_htlc_id_as_string;\n      {\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         graphene::chain::signed_block result_block;\n         BOOST_CHECK(generate_block(app1, result_block));\n\n         // get the ID:\n         htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get<object_id_type>();\n         bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id;\n         BOOST_TEST_MESSAGE(\"Bob shares the HTLC ID with Alice. The HTLC ID is: \" + bob_htlc_id_as_string);\n      }\n\n      // Alice can now look over Bob's HTLC, to see if it is what was agreed to:\n      BOOST_TEST_MESSAGE(\"Alice retrieves the HTLC Object by ID to examine it.\");\n      auto bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string);\n      BOOST_TEST_MESSAGE(\"The HTLC Object is: \" + fc::json::to_pretty_string(bob_htlc));\n\n      // Alice likes what she sees, so uses her preimage to get her BOBCOIN\n      {\n         BOOST_TEST_MESSAGE(\"Alice uses her preimage to retrieve the BOBCOIN\");\n         std::string secret = \"My Secret\";\n         con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, \"alice\", secret, true);\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         BOOST_CHECK(generate_block(app1));\n      }\n\n      // TODO: Bob can look at Alice's history to see her preimage\n      // Bob can use the preimage to retrieve his BTS\n      {\n         BOOST_TEST_MESSAGE(\"Bob uses Alice's preimage to retrieve the BOBCOIN\");\n         std::string secret = \"My Secret\";\n         con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, \"bob\", secret, true);\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         BOOST_CHECK(generate_block(app1));\n      }\n\n      // wait for everything to finish up\n      fc::usleep(fc::seconds(1));\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n   app1->shutdown();\n   app1.reset();\n   // Intentional delay after app1->shutdown\n   std::cout << \"cli_create_htlc conclusion: Intentional delay\" << std::endl;\n   fc::usleep(fc::seconds(1));\n}\n\nstatic string encapsulate( const graphene::wallet::signed_message& msg )\n{\n   fc::stringstream encapsulated;\n   encapsulated << \"-----BEGIN BITSHARES SIGNED MESSAGE-----\\n\"\n                << msg.message << '\\n'\n                << \"-----BEGIN META-----\\n\"\n                << \"account=\" << msg.meta.account << '\\n'\n                << \"memokey=\" << std::string( msg.meta.memo_key ) << '\\n'\n                << \"block=\" << msg.meta.block << '\\n'\n                << \"timestamp=\" << msg.meta.time << '\\n'\n                << \"-----BEGIN SIGNATURE-----\\n\"\n                << fc::to_hex( (const char*)msg.signature->data(), msg.signature->size() ) << '\\n'\n                << \"-----END BITSHARES SIGNED MESSAGE-----\";\n   return encapsulated.str();\n}\n\n/******\n * Check signing/verifying a message with a memo key\n */\nBOOST_FIXTURE_TEST_CASE( cli_sign_message, cli_fixture )\n{ try {\n   const auto nathan_priv = *wif_to_key( nathan_keys[0] );\n   const public_key_type nathan_pub( nathan_priv.get_public_key() );\n\n   // account does not exist\n   BOOST_REQUIRE_THROW( con.wallet_api_ptr->sign_message( \"dan\", \"123\" ), fc::assert_exception );\n\n   // success\n   graphene::wallet::signed_message msg = con.wallet_api_ptr->sign_message( \"nathan\", \"123\" );\n   BOOST_CHECK_EQUAL( \"123\", msg.message );\n   BOOST_CHECK_EQUAL( \"nathan\", msg.meta.account );\n   BOOST_CHECK_EQUAL( std::string( nathan_pub ), std::string( msg.meta.memo_key ) );\n   BOOST_CHECK( msg.signature.valid() );\n\n   // change message, verify failure\n   msg.message = \"124\";\n   BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time,\n                                                     *msg.signature ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n   msg.message = \"123\";\n\n   // change account, verify failure\n   // nonexistent account:\n   msg.meta.account = \"dan\";\n   BOOST_REQUIRE_THROW( con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block,\n                                                            msg.meta.time, *msg.signature ), fc::assert_exception );\n   BOOST_REQUIRE_THROW( con.wallet_api_ptr->verify_signed_message( msg ), fc::assert_exception );\n   BOOST_REQUIRE_THROW( con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ), fc::assert_exception);\n   // existing, but wrong account:\n   msg.meta.account = \"committee-account\";\n   BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block,\n                                                     msg.meta.time, *msg.signature ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n   msg.meta.account = \"nathan\";\n\n   // change key, verify failure\n   ++msg.meta.memo_key.key_data.data()[1];\n   //BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time,\n   //                                                  *msg.signature ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n   --msg.meta.memo_key.key_data.data()[1];\n\n   // change block, verify failure\n   ++msg.meta.block;\n   BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time,\n                                                     *msg.signature ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n   --msg.meta.block;\n\n   // change time, verify failure\n   ++msg.meta.time[0];\n   BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time,\n                                                     *msg.signature ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) );\n   BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n   --msg.meta.time[0];\n\n   // change signature, verify failure\n   ++msg.signature->data()[1];\n   try {\n      BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time,\n                                                        *msg.signature ) );\n   } catch( const fc::assert_exception& ) {} // failure to reconstruct key from signature is ok as well\n   try {\n      BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) );\n   } catch( const fc::assert_exception& ) {} // failure to reconstruct key from signature is ok as well\n   try {\n      BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n   } catch( const fc::assert_exception& ) {} // failure to reconstruct key from signature is ok as well\n   --msg.signature->data()[1];\n\n   // verify success\n   BOOST_CHECK( con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time,\n                                                    *msg.signature ) );\n   BOOST_CHECK( con.wallet_api_ptr->verify_signed_message( msg ) );\n   BOOST_CHECK( con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) );\n\n} FC_LOG_AND_RETHROW() }\n\n///////////////////\n// Test the general storage by custom operations plugin\n///////////////////\nBOOST_FIXTURE_TEST_CASE( general_storage, cli_fixture )\n{\n   try {\n      // create the taker account\n      INVOKE(create_new_account);\n\n      auto db = app1->chain_database();\n\n      BOOST_TEST_MESSAGE(\"Storing in a map.\");\n\n      flat_map<string, optional<string>> pairs;\n      pairs[\"key1\"] = fc::json::to_string(\"value1\");\n      pairs[\"key2\"] = fc::json::to_string(\"value2\");\n\n      con.wallet_api_ptr->account_store_map(\"nathan\", \"any\", false, pairs, true);\n\n      BOOST_TEST_MESSAGE(\"The system is generating a block.\");\n      BOOST_CHECK(generate_block(app1));\n\n      BOOST_TEST_MESSAGE(\"Get current map for nathan.\");\n      auto nathan_map = con.wallet_api_ptr->get_account_storage(\"nathan\", \"any\");\n\n      BOOST_CHECK_EQUAL(nathan_map[0].id.instance(), 0);\n      BOOST_CHECK_EQUAL(nathan_map[0].account.instance.value, 17);\n      BOOST_CHECK_EQUAL(nathan_map[0].catalog, \"any\");\n      BOOST_CHECK_EQUAL(nathan_map[0].key, \"key1\");\n      BOOST_CHECK_EQUAL(nathan_map[0].value->as_string(), \"value1\");\n      BOOST_CHECK_EQUAL(nathan_map[1].id.instance(), 1);\n      BOOST_CHECK_EQUAL(nathan_map[1].account.instance.value, 17);\n      BOOST_CHECK_EQUAL(nathan_map[1].catalog, \"any\");\n      BOOST_CHECK_EQUAL(nathan_map[1].key, \"key2\");\n      BOOST_CHECK_EQUAL(nathan_map[1].value->as_string(), \"value2\");\n\n      BOOST_TEST_MESSAGE(\"Storing in a list.\");\n\n      flat_map<string, optional<string>> favs;\n      favs[\"chocolate\"];\n      favs[\"milk\"];\n      favs[\"banana\"];\n\n      con.wallet_api_ptr->account_store_map(\"nathan\", \"favourites\", false, favs, true);\n\n      BOOST_TEST_MESSAGE(\"The system is generating a block.\");\n      BOOST_CHECK(generate_block(app1));\n\n      BOOST_TEST_MESSAGE(\"Get current list for nathan.\");\n      auto nathan_list = con.wallet_api_ptr->get_account_storage(\"nathan\", \"favourites\");\n\n      BOOST_CHECK_EQUAL(nathan_list[0].id.instance(), 2);\n      BOOST_CHECK_EQUAL(nathan_list[0].account.instance.value, 17);\n      BOOST_CHECK_EQUAL(nathan_list[0].catalog, \"favourites\");\n      BOOST_CHECK_EQUAL(nathan_list[0].key, \"banana\");\n      BOOST_CHECK_EQUAL(nathan_list[1].id.instance(), 3);\n      BOOST_CHECK_EQUAL(nathan_list[1].account.instance.value, 17);\n      BOOST_CHECK_EQUAL(nathan_list[1].catalog, \"favourites\");\n      BOOST_CHECK_EQUAL(nathan_list[1].key, \"chocolate\");\n      BOOST_CHECK_EQUAL(nathan_list[2].id.instance(), 4);\n      BOOST_CHECK_EQUAL(nathan_list[2].account.instance.value, 17);\n      BOOST_CHECK_EQUAL(nathan_list[2].catalog, \"favourites\");\n      BOOST_CHECK_EQUAL(nathan_list[2].key, \"milk\");\n\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n//////\n// Template copied\n//////\ntemplate<typename Object>\nunsigned_int member_index(string name) {\n   unsigned_int index;\n   fc::typelist::runtime::for_each(typename fc::reflector<Object>::native_members(), [&name, &index](auto t) mutable {\n      if (name == decltype(t)::type::get_name())\n      index = decltype(t)::type::index;\n   });\n   return index;\n}\n\n///////////////////////\n// Wallet RPC\n// Test sign_builder_transaction2 with an account (bob) that has received a custom authorization\n// to transfer funds from another account (alice)\n///////////////////////\nBOOST_FIXTURE_TEST_CASE(cli_use_authorized_transfer, cli_fixture) {\n   try {\n      //////\n      // Initialize the blockchain\n      //////\n      auto db = app1->chain_database();\n\n      account_object nathan_acct = con.wallet_api_ptr->get_account(\"nathan\");\n      INVOKE(upgrade_nathan_account);\n\n      // Register Alice account\n      const auto alice_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"alice\", alice_bki.pub_key, alice_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      const account_object &alice_acc = con.wallet_api_ptr->get_account(\"alice\");\n\n      // Register Bob account\n      const auto bob_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"bob\", bob_bki.pub_key, bob_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      const account_object &bob_acc = con.wallet_api_ptr->get_account(\"bob\");\n\n      // Register Charlie account\n      const graphene::wallet::brain_key_info charlie_bki = con.wallet_api_ptr->suggest_brain_key();\n      con.wallet_api_ptr->register_account(\n              \"charlie\", charlie_bki.pub_key, charlie_bki.pub_key, \"nathan\", \"nathan\", 0, true\n      );\n      const account_object &charlie_acc = con.wallet_api_ptr->get_account(\"charlie\");\n\n      // Fund Alice's account\n      con.wallet_api_ptr->transfer(\"nathan\", \"alice\", \"450000\", \"1.3.0\", \"\", true);\n\n      // Initialize common variables\n      signed_transaction signed_trx;\n\n\n      //////\n      // Initialize Alice's CLI wallet\n      //////\n      client_connection con_alice(app1, app_dir, server_port_number, \"wallet_alice.json\");\n      con_alice.wallet_api_ptr->set_password(\"supersecret\");\n      con_alice.wallet_api_ptr->unlock(\"supersecret\");\n\n      // Import Alice's key\n      BOOST_CHECK(con_alice.wallet_api_ptr->import_key(\"alice\", alice_bki.wif_priv_key));\n\n\n      //////\n      // Initialize the blockchain for BSIP 40\n      //////\n      generate_blocks(app1, HARDFORK_BSIP_40_TIME);\n      // Set committee parameters\n      app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p) {\n         p.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n      });\n\n\n      //////\n      // Alice authorizes Bob to transfer funds from her account to Charlie's account\n      //////\n      graphene::wallet::transaction_handle_type tx_alice_handle = con_alice.wallet_api_ptr->begin_builder_transaction();\n\n      custom_authority_create_operation caop;\n      caop.account = alice_acc.get_id();\n      caop.auth.add_authority(bob_acc.get_id(), 1);\n      caop.auth.weight_threshold = 1;\n      caop.enabled = true;\n      caop.valid_to = db->head_block_time() + 1000;\n      caop.operation_type = operation::tag<transfer_operation>::value;\n\n      // Restriction should have \"to\" equal Charlie\n      vector<restriction> restrictions;\n      auto to_index = member_index<transfer_operation>(\"to\");\n      restrictions.emplace_back(to_index, restriction::func_eq, charlie_acc.get_id());\n\n      con_alice.wallet_api_ptr->add_operation_to_builder_transaction(tx_alice_handle, caop);\n      asset ca_fee = con_alice.wallet_api_ptr->set_fees_on_builder_transaction(tx_alice_handle, GRAPHENE_SYMBOL);\n\n      // Sign the transaction with the inferred Alice key\n      signed_trx = con_alice.wallet_api_ptr->sign_builder_transaction2(tx_alice_handle, {}, true);\n\n      // Check for one signatures on the transaction\n      BOOST_CHECK_EQUAL(signed_trx.signatures.size(), 1);\n\n      // Check that the signed transaction contains Alice's signature\n      flat_set<public_key_type> expected_signers = {alice_bki.pub_key};\n      flat_set<public_key_type> actual_signers = con_alice.wallet_api_ptr->get_transaction_signers(signed_trx);\n      BOOST_CHECK(actual_signers == expected_signers);\n\n\n      //////\n      // Initialize Bob's CLI wallet\n      //////\n      client_connection con_bob(app1, app_dir, server_port_number, \"wallet_bob.json\");\n      con_bob.wallet_api_ptr->set_password(\"supersecret\");\n      con_bob.wallet_api_ptr->unlock(\"supersecret\");\n\n      // Import Bob's key\n      BOOST_CHECK(con_bob.wallet_api_ptr->import_key(\"bob\", bob_bki.wif_priv_key));\n\n\n      //////\n      // Bob attempt to transfer funds from Alice to Charlie while using Bob's wallet\n      // This should succeed because Bob is authorized to transfer by Alice\n      //////\n      graphene::wallet::transaction_handle_type tx_bob_handle = con_bob.wallet_api_ptr->begin_builder_transaction();\n\n      const asset transfer_amount = asset(123 * GRAPHENE_BLOCKCHAIN_PRECISION);\n      transfer_operation top;\n      top.from = alice_acc.id;\n      top.to = charlie_acc.id;\n      top.amount = transfer_amount;\n\n      con_bob.wallet_api_ptr->add_operation_to_builder_transaction(tx_bob_handle, top);\n      asset transfer_fee = con_bob.wallet_api_ptr->set_fees_on_builder_transaction(tx_bob_handle, GRAPHENE_SYMBOL);\n\n      // Sign the transaction with the explicit Bob key\n      signed_trx = con_bob.wallet_api_ptr->sign_builder_transaction2(tx_bob_handle, {bob_bki.pub_key}, true);\n\n      // Check for one signatures on the transaction\n      BOOST_CHECK_EQUAL(signed_trx.signatures.size(), 1);\n\n      // Check that the signed transaction contains Bob's signature\n      BOOST_CHECK_EQUAL(nathan_acct.active.get_keys().size(), 1);\n      expected_signers = {bob_bki.pub_key};\n      actual_signers = con_bob.wallet_api_ptr->get_transaction_signers(signed_trx);\n      BOOST_CHECK(actual_signers == expected_signers);\n\n\n      //////\n      // Check account balances\n      //////\n      // Check Charlie's balances\n      vector<asset> charlie_balances = con.wallet_api_ptr->list_account_balances(\"charlie\");\n      BOOST_CHECK_EQUAL(charlie_balances.size(), 1);\n      asset charlie_core_balance = charlie_balances.front();\n      asset expected_charlie_core_balance = transfer_amount;\n      BOOST_CHECK(charlie_core_balance == expected_charlie_core_balance);\n\n      // Check Bob's balances\n      vector<asset> bob_balances = con.wallet_api_ptr->list_account_balances(\"bob\");\n      BOOST_CHECK_EQUAL(bob_balances.size(), 0);\n\n      // Check Alice's balance\n      vector<asset> alice_balances = con.wallet_api_ptr->list_account_balances(\"alice\");\n      BOOST_CHECK_EQUAL(alice_balances.size(), 1);\n      asset alice_core_balance = alice_balances.front();\n      asset expected_alice_balance =  asset(450000 * GRAPHENE_BLOCKCHAIN_PRECISION)\n                                      - expected_charlie_core_balance\n                                      - ca_fee - transfer_fee;\n      BOOST_CHECK(alice_core_balance.asset_id == expected_alice_balance.asset_id);\n      BOOST_CHECK_EQUAL(alice_core_balance.amount.value, expected_alice_balance.amount.value);\n\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 )\n{\n   using namespace graphene::chain;\n   using namespace graphene::app;\n   std::shared_ptr<graphene::app::application> app1;\n   try {\n      fc::temp_directory app_dir( graphene::utilities::temp_directory_path() );\n\n      int server_port_number = 0;\n      app1 = start_application(app_dir, server_port_number);\n      // set committee parameters\n      app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p)\n      {\n         graphene::chain::htlc_options params;\n         params.max_preimage_size = 1024;\n         params.max_timeout_secs = 60 * 60 * 24 * 28;\n         p.parameters.extensions.value.updatable_htlc_options = params;\n      });\n\n      // connect to the server\n      client_connection con(app1, app_dir, server_port_number);\n\n      // get past hardforks\n      generate_blocks( app1, HARDFORK_CORE_BSIP64_TIME + 10);\n\n      BOOST_TEST_MESSAGE(\"Setting wallet password\");\n      con.wallet_api_ptr->set_password(\"supersecret\");\n      con.wallet_api_ptr->unlock(\"supersecret\");\n\n      // import Nathan account\n      BOOST_TEST_MESSAGE(\"Importing nathan key\");\n      std::vector<std::string> nathan_keys{\"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\"};\n      BOOST_CHECK_EQUAL(nathan_keys[0], \"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\");\n      BOOST_CHECK(con.wallet_api_ptr->import_key(\"nathan\", nathan_keys[0]));\n\n      BOOST_TEST_MESSAGE(\"Importing nathan's balance\");\n      std::vector<signed_transaction> import_txs = con.wallet_api_ptr->import_balance(\"nathan\", nathan_keys, true);\n      account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // upgrade nathan\n      BOOST_TEST_MESSAGE(\"Upgrading Nathan to LTM\");\n      signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account(\"nathan\", true);\n      account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account(\"nathan\");\n\n      // verify that the upgrade was successful\n      BOOST_CHECK_PREDICATE( std::not_equal_to<uint32_t>(), \n            (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())\n            (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) );\n      BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());\n\n      // Create new asset called BOBCOIN\n      try \n      {\n         graphene::chain::asset_options asset_ops;\n         asset_ops.max_supply = 1000000;\n         asset_ops.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1)));\n         fc::optional<graphene::chain::bitasset_options> bit_opts;\n         con.wallet_api_ptr->create_asset(\"nathan\", \"BOBCOIN\", 5, asset_ops, bit_opts, true);\n      }\n      catch(exception& e)\n      {\n         BOOST_FAIL(e.what());\n      }\n      catch(...)\n      {\n         BOOST_FAIL(\"Unknown exception creating BOBCOIN\");\n      }\n\n      // create a new account for Alice\n      {\n         graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key();\n         BOOST_CHECK(!bki.brain_priv_key.empty());\n         signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key,\n               \"alice\", \"nathan\", \"nathan\", true);\n         con.wallet_api_ptr->save_wallet_file(con.wallet_filename);\n         // attempt to give alice some bitshares\n         BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to alice\");\n         signed_transaction transfer_tx = con.wallet_api_ptr->transfer(\"nathan\", \"alice\", \"10000\", \"1.3.0\", \n               \"Here are some CORE token for your new account\", true);\n      }\n\n      // create a new account for Bob\n      {\n         graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key();\n         BOOST_CHECK(!bki.brain_priv_key.empty());\n         signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key,\n               \"bob\", \"nathan\", \"nathan\", true);\n         // this should cause resync which will import the keys of alice and bob\n         generate_block(app1);\n         // attempt to give bob some bitshares\n         BOOST_TEST_MESSAGE(\"Transferring bitshares from Nathan to Bob\");\n         signed_transaction transfer_tx = con.wallet_api_ptr->transfer(\"nathan\", \"bob\", \"10000\", \"1.3.0\", \n               \"Here are some CORE token for your new account\", true);\n         con.wallet_api_ptr->issue_asset(\"bob\", \"5\", \"BOBCOIN\", \"Here are your BOBCOINs\", true);\n      }\n\n      BOOST_TEST_MESSAGE(\"Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC\");\n      // create an HTLC\n      std::string preimage_string = \"My Super Long Secret that is larger than 50 charaters. How do I look?\\n\";\n      fc::hash160 preimage_md = fc::hash160::hash(preimage_string);\n      std::stringstream ss;\n      for(size_t i = 0; i < preimage_md.data_size(); i++)\n      {\n         char d = preimage_md.data()[i];\n         unsigned char uc = static_cast<unsigned char>(d);\n         ss << std::setfill('0') << std::setw(2) << std::hex << (int)uc;\n      }\n      std::string hash_str = ss.str();\n      BOOST_TEST_MESSAGE(\"Secret is \" + preimage_string + \" and hash is \" + hash_str);\n      uint32_t timelock = fc::days(1).to_seconds();\n      graphene::chain::signed_transaction result_tx \n            = con.wallet_api_ptr->htlc_create(\"alice\", \"bob\", \n            \"3\", \"1.3.0\", \"HASH160\", hash_str, preimage_string.size(), timelock, \"Alice to Bob\", true);\n\n      // normally, a wallet would watch block production, and find the transaction. Here, we can cheat:\n      std::string alice_htlc_id_as_string;\n      {\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         graphene::chain::signed_block result_block;\n         BOOST_CHECK(generate_block(app1, result_block));\n\n         // get the ID:\n         htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1]\n               .operation_results[0].get<object_id_type>();\n         alice_htlc_id_as_string = (std::string)(object_id_type)htlc_id;\n         BOOST_TEST_MESSAGE(\"Alice shares the HTLC ID with Bob. The HTLC ID is: \" + alice_htlc_id_as_string);\n      }\n\n      // Bob can now look over Alice's HTLC, to see if it is what was agreed to.\n      BOOST_TEST_MESSAGE(\"Bob retrieves the HTLC Object by ID to examine it.\");\n      auto alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string);\n      BOOST_TEST_MESSAGE(\"The HTLC Object is: \" + fc::json::to_pretty_string(alice_htlc));\n\n      // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC\n      con.wallet_api_ptr->htlc_create(\"bob\", \"alice\",\n            \"3\", \"BOBCOIN\", \"HASH160\", hash_str, preimage_string.size(), fc::hours(12).to_seconds(),\n            \"Bob to Alice\", true);\n\n      // normally, a wallet would watch block production, and find the transaction. Here, we can cheat:\n      std::string bob_htlc_id_as_string;\n      {\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         graphene::chain::signed_block result_block;\n         BOOST_CHECK(generate_block(app1, result_block));\n\n         // get the ID:\n         htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1]\n               .operation_results[0].get<object_id_type>();\n         bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id;\n         BOOST_TEST_MESSAGE(\"Bob shares the HTLC ID with Alice. The HTLC ID is: \" + bob_htlc_id_as_string);\n      }\n\n      // Alice can now look over Bob's HTLC, to see if it is what was agreed to:\n      BOOST_TEST_MESSAGE(\"Alice retrieves the HTLC Object by ID to examine it.\");\n      auto bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string);\n      BOOST_TEST_MESSAGE(\"The HTLC Object is: \" + fc::json::to_pretty_string(bob_htlc));\n\n      // Alice likes what she sees, so uses her preimage to get her BOBCOIN\n      {\n         BOOST_TEST_MESSAGE(\"Alice uses her preimage to retrieve the BOBCOIN\");\n         con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, \"alice\", preimage_string, true);\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         BOOST_CHECK(generate_block(app1));\n      }\n\n      // Bob can look at Alice's history to see her preimage\n      {\n         BOOST_TEST_MESSAGE(\"Bob can look at the history of Alice to see the preimage\");\n         std::vector<graphene::wallet::operation_detail> hist = con.wallet_api_ptr->get_account_history(\"alice\", 1);\n         BOOST_CHECK( hist[0].description.find(\"with preimage \\\"4d792\") != hist[0].description.npos);\n      }\n      \n      // Bob can also look at his own history to see Alice's preimage\n      {\n         BOOST_TEST_MESSAGE(\"Bob can look at his own history to see the preimage\");\n         std::vector<graphene::wallet::operation_detail> hist = con.wallet_api_ptr->get_account_history(\"bob\", 1);\n         BOOST_CHECK( hist[0].description.find(\"with preimage \\\"4d792\") != hist[0].description.npos);\n      }\n\n      // Bob can use the preimage to retrieve his BTS\n      {\n         BOOST_TEST_MESSAGE(\"Bob uses Alice's preimage to retrieve the BOBCOIN\");\n         con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, \"bob\", preimage_string, true);\n         BOOST_TEST_MESSAGE(\"The system is generating a block\");\n         BOOST_CHECK(generate_block(app1));\n      }\n\n      // test operation_printer\n      auto hist = con.wallet_api_ptr->get_account_history(\"alice\", 10);\n      for(size_t i = 0; i < hist.size(); ++i)\n      {\n         auto obj = hist[i];\n         std::stringstream ss;\n         ss << \"Description: \" << obj.description << \"\\n\";\n         auto str = ss.str();\n         BOOST_TEST_MESSAGE( str );\n         if (i == 3 || i == 4)\n         {\n            BOOST_CHECK( str.find(\"HASH160 620e4d5ba\") != std::string::npos );\n         }\n      }\n      con.wallet_api_ptr->lock();\n      hist = con.wallet_api_ptr->get_account_history(\"alice\", 10);\n      for(size_t i = 0; i < hist.size(); ++i)\n      {\n         auto obj = hist[i];\n         std::stringstream ss;\n         ss << \"Description: \" << obj.description << \"\\n\";\n         auto str = ss.str();\n         BOOST_TEST_MESSAGE( str );\n         if (i == 3 || i == 4)\n         {\n            BOOST_CHECK( str.find(\"HASH160 620e4d5ba\") != std::string::npos );\n         }\n      } \n      con.wallet_api_ptr->unlock(\"supersecret\");        \n\n      // wait for everything to finish up\n      fc::usleep(fc::seconds(1));\n   } catch( fc::exception& e ) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n   app1->shutdown();\n   app1.reset();\n   // Intentional delay after app1->shutdown\n   std::cout << \"cli_create_htlc conclusion: Intentional delay\" << std::endl;\n   fc::usleep(fc::seconds(1));\n}\n"
  },
  {
    "path": "tests/common/database_fixture.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <boost/test/unit_test.hpp>\n#include <boost/program_options.hpp>\n#include <boost/range/algorithm.hpp>\n\n#include <graphene/account_history/account_history_plugin.hpp>\n#include <graphene/market_history/market_history_plugin.hpp>\n#include <graphene/grouped_orders/grouped_orders_plugin.hpp>\n#include <graphene/elasticsearch/elasticsearch_plugin.hpp>\n#include <graphene/api_helper_indexes/api_helper_indexes.hpp>\n#include <graphene/es_objects/es_objects.hpp>\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/hardfork_visitor.hpp>\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include <iomanip>\n\n#include \"database_fixture.hpp\"\n\nusing namespace graphene::chain::test;\n\nuint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1431700000;\n\nnamespace graphene { namespace chain {\n\nusing std::cout;\nusing std::cerr;\n\nnamespace buf = boost::unit_test::framework;\n\nvoid clearable_block::clear()\n{\n   _calculated_merkle_root = checksum_type();\n   _signee = fc::ecc::public_key();\n   _block_id = block_id_type();\n}\n\ndatabase_fixture::database_fixture(const fc::time_point_sec &initial_timestamp)\n   : app(), db( *app.chain_database() )\n{\n   try {\n   int argc = buf::master_test_suite().argc;\n   char** argv = buf::master_test_suite().argv;\n   for( int i=1; i<argc; i++ )\n   {\n      const std::string arg = argv[i];\n      if( arg == \"--record-assert-trip\" )\n         fc::enable_record_assert_trip = true;\n      if( arg == \"--show-test-names\" )\n         std::cout << \"running test \" << buf::current_test_case().p_name << std::endl;\n   }\n\n   const auto current_test_name = buf::current_test_case().p_name.value;\n   const auto current_test_suite_id = buf::current_test_case().p_parent_id;\n   const auto current_suite_name = buf::get<boost::unit_test::test_suite>(current_test_suite_id).p_name.value;\n   auto mhplugin = app.register_plugin<graphene::market_history::market_history_plugin>();\n   auto goplugin = app.register_plugin<graphene::grouped_orders::grouped_orders_plugin>();\n   init_account_pub_key = init_account_priv_key.get_public_key();\n\n   boost::program_options::variables_map options;\n\n   genesis_state.initial_timestamp = initial_timestamp;\n\n   if(current_test_name == \"hf_1270_test\")\n   {\n      genesis_state.initial_active_witnesses = 20;\n   }\n   else {\n      genesis_state.initial_active_witnesses = 10;\n      genesis_state.immutable_parameters.min_committee_member_count = INITIAL_COMMITTEE_MEMBER_COUNT;\n      genesis_state.immutable_parameters.min_witness_count = INITIAL_WITNESS_COUNT;\n   }\n\n   for( unsigned int i = 0; i < genesis_state.initial_active_witnesses; ++i )\n   {\n      auto name = \"init\"+fc::to_string(i);\n      genesis_state.initial_accounts.emplace_back(name,\n                                                  init_account_priv_key.get_public_key(),\n                                                  init_account_priv_key.get_public_key(),\n                                                  true);\n      genesis_state.initial_committee_candidates.push_back({name});\n      genesis_state.initial_witness_candidates.push_back({name, init_account_priv_key.get_public_key()});\n   }\n   genesis_state.initial_parameters.get_mutable_fees().zero_all_fees();\n\n   genesis_state_type::initial_asset_type init_mpa1;\n   init_mpa1.symbol = \"INITMPA\";\n   init_mpa1.issuer_name = \"committee-account\";\n   init_mpa1.description = \"Initial MPA\";\n   init_mpa1.precision = 4;\n   init_mpa1.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n   init_mpa1.accumulated_fees = 0;\n   init_mpa1.is_bitasset = true;\n   // TODO add initial UIA's; add initial short positions; test non-zero accumulated_fees\n   genesis_state.initial_assets.push_back( init_mpa1 );\n\n   open_database();\n\n   /**\n    * Test specific settings\n    */\n   if (current_test_name == \"get_account_history_operations\")\n   {\n      options.insert(std::make_pair(\"max-ops-per-account\", boost::program_options::variable_value((uint64_t)75, false)));\n   }\n   if (current_test_name == \"api_limit_get_account_history_operations\")\n   {\n    options.insert(std::make_pair(\"max-ops-per-account\", boost::program_options::variable_value((uint64_t)125, false)));\n    options.insert(std::make_pair(\"api-limit-get-account-history-operations\", boost::program_options::variable_value((uint64_t)300, false)));\n   }\n   if(current_test_name ==\"api_limit_get_account_history\")\n   {\n    options.insert(std::make_pair(\"max-ops-per-account\", boost::program_options::variable_value((uint64_t)125, false)));\n    options.insert(std::make_pair(\"api-limit-get-account-history\", boost::program_options::variable_value((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_grouped_limit_orders\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-grouped-limit-orders\", boost::program_options::variable_value((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_relative_account_history\")\n   {\n    options.insert(std::make_pair(\"max-ops-per-account\", boost::program_options::variable_value((uint64_t)125, false)));\n    options.insert(std::make_pair(\"api-limit-get-relative-account-history\", boost::program_options::variable_value((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_account_history_by_operations\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-account-history-by-operations\", boost::program_options::variable_value((uint64_t)250, false)));\n    options.insert(std::make_pair(\"api-limit-get-relative-account-history\", boost::program_options::variable_value((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_asset_holders\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-asset-holders\", boost::program_options::variable_value((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_key_references\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-key-references\", boost::program_options::variable_value((uint64_t)200, false)));\n   }\n   if(current_test_name ==\"api_limit_get_limit_orders\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-limit-orders\", boost::program_options::variable_value(\n       (uint64_t)350, false)));\n   }\n   if(current_test_name ==\"api_limit_get_limit_orders_by_account\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-limit-orders-by-account\", boost::program_options::variable_value(\n       (uint64_t)150, false)));\n   }\n   if(current_test_name ==\"api_limit_get_call_orders\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-call-orders\", boost::program_options::variable_value(\n       (uint64_t)350, false)));\n   }\n   if(current_test_name ==\"api_limit_get_settle_orders\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-settle-orders\", boost::program_options::variable_value(\n       (uint64_t)350, false)));\n   }\n   if(current_test_name ==\"api_limit_get_order_book\")\n   {\n    options.insert(std::make_pair(\"api-limit-get-order-book\", boost::program_options::variable_value(\n       (uint64_t)80, false)));\n   }\n   if( current_test_name == \"asset_in_collateral\" )\n   {\n    options.insert( std::make_pair( \"plugins\",\n                                    boost::program_options::variable_value( string(\"api_helper_indexes\"), false ) ) );\n   }\n   if(current_test_name ==\"api_limit_lookup_accounts\")\n   {\n      options.insert(std::make_pair(\"api-limit-lookup-accounts\", boost::program_options::variable_value\n         ((uint64_t)200, false)));\n   }\n   if(current_test_name ==\"api_limit_lookup_witness_accounts\")\n   {\n      options.insert(std::make_pair(\"api-limit-lookup-witness-accounts\", boost::program_options::variable_value\n         ((uint64_t)200, false)));\n   }\n   if(current_test_name ==\"api_limit_lookup_committee_member_accounts\")\n   {\n      options.insert(std::make_pair(\"api-limit-lookup-committee-member-accounts\", boost::program_options::variable_value\n         ((uint64_t)200, false)));\n   }\n   if(current_test_name ==\"api_limit_lookup_committee_member_accounts\")\n   {\n      options.insert(std::make_pair(\"api-limit-lookup-committee-member-accounts\", boost::program_options::variable_value\n         ((uint64_t)200, false)));\n   }\n   if(current_test_name ==\"api_limit_lookup_vote_ids\")\n   {\n      options.insert(std::make_pair(\"api-limit-lookup-vote-ids\", boost::program_options::variable_value\n         ((uint64_t)2, false)));\n   }\n   if(current_test_name ==\"api_limit_get_account_limit_orders\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-account-limit-orders\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_collateral_bids\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-collateral-bids\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_top_markets\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-top-markets\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_trade_history\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-trade-history\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_trade_history_by_sequence\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-trade-history-by-sequence\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_withdraw_permissions_by_giver\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-withdraw-permissions-by-giver\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_withdraw_permissions_by_recipient\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-withdraw-permissions-by-recipient\", boost::program_options::variable_value\n         ((uint64_t)250, false)));\n   }\n   if(current_test_name ==\"api_limit_get_full_accounts2\")\n   {\n      options.insert(std::make_pair(\"api-limit-get-full-accounts\", boost::program_options::variable_value\n         ((uint64_t)200, false)));\n      options.insert(std::make_pair(\"api-limit-get-full-accounts-lists\", boost::program_options::variable_value\n         ((uint64_t)120, false)));\n   }\n      // add account tracking for ahplugin for special test case with track-account enabled\n   if( !options.count(\"track-account\") && current_test_name == \"track_account\") {\n      std::vector<std::string> track_account;\n      std::string track = \"\\\"1.2.17\\\"\";\n      track_account.push_back(track);\n      options.insert(std::make_pair(\"track-account\", boost::program_options::variable_value(track_account, false)));\n      options.insert(std::make_pair(\"partial-operations\", boost::program_options::variable_value(true, false)));\n   }\n   // account tracking 2 accounts\n   if( !options.count(\"track-account\") && current_test_name == \"track_account2\") {\n      std::vector<std::string> track_account;\n      std::string track = \"\\\"1.2.0\\\"\";\n      track_account.push_back(track);\n      track = \"\\\"1.2.16\\\"\";\n      track_account.push_back(track);\n      options.insert(std::make_pair(\"track-account\", boost::program_options::variable_value(track_account, false)));\n   }\n   // standby votes tracking\n   if( current_test_name == \"track_votes_witnesses_disabled\"\n          || current_test_name == \"track_votes_committee_disabled\") {\n      app.chain_database()->enable_standby_votes_tracking( false );\n   }\n   if(current_test_name == \"elasticsearch_account_history\" || current_test_name == \"elasticsearch_suite\" ||\n      current_test_name == \"elasticsearch_history_api\") {\n      auto esplugin = app.register_plugin<graphene::elasticsearch::elasticsearch_plugin>();\n      esplugin->plugin_set_app(&app);\n\n      options.insert(std::make_pair(\"elasticsearch-node-url\", boost::program_options::variable_value(string(\"http://localhost:9200/\"), false)));\n      options.insert(std::make_pair(\"elasticsearch-bulk-replay\", boost::program_options::variable_value(uint32_t(2), false)));\n      options.insert(std::make_pair(\"elasticsearch-bulk-sync\", boost::program_options::variable_value(uint32_t(2), false)));\n      options.insert(std::make_pair(\"elasticsearch-start-es-after-block\", boost::program_options::variable_value(uint32_t(0), false)));\n      options.insert(std::make_pair(\"elasticsearch-visitor\", boost::program_options::variable_value(false, false)));\n      options.insert(std::make_pair(\"elasticsearch-operation-object\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"elasticsearch-operation-string\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"elasticsearch-mode\", boost::program_options::variable_value(uint16_t(2), false)));\n\n      esplugin->plugin_initialize(options);\n      esplugin->plugin_startup();\n   }\n   else if( current_suite_name != \"performance_tests\" )\n   {\n      auto ahplugin = app.register_plugin<graphene::account_history::account_history_plugin>();\n      ahplugin->plugin_set_app(&app);\n      ahplugin->plugin_initialize(options);\n      ahplugin->plugin_startup();\n\n      if(validation_current_test_name_for_setting_api_limit(current_test_name))\n      {\n          app.initialize(graphene::utilities::temp_directory_path(), options);\n          app.set_api_limit();\n      }\n   }\n\n   if(current_test_name == \"elasticsearch_objects\" || current_test_name == \"elasticsearch_suite\") {\n      auto esobjects_plugin = app.register_plugin<graphene::es_objects::es_objects_plugin>();\n      esobjects_plugin->plugin_set_app(&app);\n\n      options.insert(std::make_pair(\"es-objects-elasticsearch-url\", boost::program_options::variable_value(string(\"http://localhost:9200/\"), false)));\n      options.insert(std::make_pair(\"es-objects-bulk-replay\", boost::program_options::variable_value(uint32_t(2), false)));\n      options.insert(std::make_pair(\"es-objects-bulk-sync\", boost::program_options::variable_value(uint32_t(2), false)));\n      options.insert(std::make_pair(\"es-objects-proposals\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"es-objects-accounts\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"es-objects-assets\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"es-objects-balances\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"es-objects-limit-orders\", boost::program_options::variable_value(true, false)));\n      options.insert(std::make_pair(\"es-objects-asset-bitasset\", boost::program_options::variable_value(true, false)));\n\n      esobjects_plugin->plugin_initialize(options);\n      esobjects_plugin->plugin_startup();\n   }\n   else if( current_test_name == \"asset_in_collateral\"\n            || current_test_name == \"htlc_database_api\"\n            || current_suite_name == \"database_api_tests\"\n            || current_suite_name == \"api_limit_tests\" )\n   {\n      auto ahiplugin = app.register_plugin<graphene::api_helper_indexes::api_helper_indexes>();\n      ahiplugin->plugin_set_app(&app);\n      ahiplugin->plugin_initialize(options);\n      ahiplugin->plugin_startup();\n   }\n\n   if(current_test_name == \"custom_operations_account_storage_map_test\" ||\n      current_test_name == \"custom_operations_account_storage_list_test\") {\n      auto custom_operations_plugin = app.register_plugin<graphene::custom_operations::custom_operations_plugin>();\n      custom_operations_plugin->plugin_set_app(&app);\n      options.insert(std::make_pair(\"custom-operations-start-block\", boost::program_options::variable_value(uint32_t(1), false)));\n      custom_operations_plugin->plugin_initialize(options);\n      custom_operations_plugin->plugin_startup();\n   }\n\n   options.insert(std::make_pair(\"bucket-size\", boost::program_options::variable_value(string(\"[15]\"),false)));\n   mhplugin->plugin_set_app(&app);\n   mhplugin->plugin_initialize(options);\n\n   goplugin->plugin_set_app(&app);\n   goplugin->plugin_initialize(options);\n\n   mhplugin->plugin_startup();\n   goplugin->plugin_startup();\n\n   generate_block();\n\n   asset_id_type mpa1_id(1);\n   BOOST_REQUIRE( mpa1_id(db).is_market_issued() );\n   BOOST_CHECK( mpa1_id(db).bitasset_data(db).asset_id == mpa1_id );\n\n   set_expiration( db, trx );\n   } catch ( const fc::exception& e )\n   {\n      edump( (e.to_detail_string()) );\n      throw;\n   }\n\n   return;\n}\n\ndatabase_fixture::~database_fixture()\n{ \n   try {\n      // If we're unwinding due to an exception, don't do any more checks.\n      // This way, boost test's last checkpoint tells us approximately where the error was.\n      if( !std::uncaught_exception() )\n      {\n         verify_asset_supplies(db);\n         BOOST_CHECK( db.get_node_properties().skip_flags == database::skip_nothing );\n      }\n      return;\n   } catch (fc::exception& ex) {\n      BOOST_FAIL( std::string(\"fc::exception in ~database_fixture: \") + ex.to_detail_string() );\n   } catch (std::exception& e) {\n      BOOST_FAIL( std::string(\"std::exception in ~database_fixture:\") + e.what() );\n   } catch (...) {\n      BOOST_FAIL( \"Uncaught exception in ~database_fixture\" );\n   }\n} \n\nvoid database_fixture::vote_for_committee_and_witnesses(uint16_t num_committee, uint16_t num_witness)\n{ try {\n\n   auto &init0 = get_account(\"init0\");\n   fund(init0, asset(10));\n\n   flat_set<vote_id_type> votes;\n\n   const auto& wits = db.get_index_type<witness_index>().indices().get<by_id>();\n   num_witness = std::min(num_witness, (uint16_t) wits.size());\n   auto wit_end = wits.begin();\n   std::advance(wit_end, num_witness);\n   std::transform(wits.begin(), wit_end,\n                  std::inserter(votes, votes.end()),\n                  [](const witness_object& w) { return w.vote_id; });\n\n   const auto& comms = db.get_index_type<committee_member_index>().indices().get<by_id>();\n   num_committee = std::min(num_committee, (uint16_t) comms.size());\n   auto comm_end = comms.begin();\n   std::advance(comm_end, num_committee);\n   std::transform(comms.begin(), comm_end,\n                  std::inserter(votes, votes.end()),\n                  [](const committee_member_object& cm) { return cm.vote_id; });\n\n   account_update_operation op;\n   op.account = init0.get_id();\n   op.new_options = init0.options;\n   op.new_options->votes = votes;\n   op.new_options->num_witness = num_witness;\n   op.new_options->num_committee = num_committee;\n\n   op.fee = db.current_fee_schedule().calculate_fee( op );\n\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n\n} FC_CAPTURE_AND_RETHROW() }\n\nfc::ecc::private_key database_fixture::generate_private_key(string seed)\n{\n   static const fc::ecc::private_key committee = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")));\n   if( seed == \"null_key\" )\n      return committee;\n   return fc::ecc::private_key::regenerate(fc::sha256::hash(seed));\n}\n\nstring database_fixture::generate_anon_acct_name()\n{\n   // names of the form \"anon-acct-x123\" ; the \"x\" is necessary\n   //    to workaround issue #46\n   return \"anon-acct-x\" + std::to_string( anon_acct_count++ );\n}\nbool database_fixture::validation_current_test_name_for_setting_api_limit( const string& current_test_name ) const\n{\n   vector <string> valid_testcase {\"api_limit_get_account_history_operations\",\"api_limit_get_account_history\"\n      ,\"api_limit_get_grouped_limit_orders\",\"api_limit_get_relative_account_history\"\n      ,\"api_limit_get_account_history_by_operations\",\"api_limit_get_asset_holders\"\n      ,\"api_limit_get_key_references\",\"api_limit_get_limit_orders\",\"api_limit_get_limit_orders_by_account\"\n      ,\"api_limit_get_call_orders\",\"api_limit_get_settle_orders\"\n      ,\"api_limit_get_order_book\",\"api_limit_lookup_accounts\"\n      ,\"api_limit_lookup_witness_accounts\",\"api_limit_lookup_committee_member_accounts\"\n      ,\"api_limit_lookup_vote_ids\",\"api_limit_get_account_limit_orders\"\n      ,\"api_limit_get_collateral_bids\",\"api_limit_get_top_markets\"\n      ,\"api_limit_get_trade_history\", \"api_limit_get_trade_history_by_sequence\"\n      ,\"api_limit_get_withdraw_permissions_by_giver\",\"api_limit_get_withdraw_permissions_by_recipient\"\n      ,\"api_limit_get_full_accounts2\"};\n   for(string i_valid_testcase: valid_testcase)\n   {\n      if(i_valid_testcase.compare(current_test_name)==0)\n      {\n         return true;\n      }\n   }\n\n   return false;\n}\nvoid database_fixture::verify_asset_supplies( const database& db )\n{\n   //wlog(\"*** Begin asset supply verification ***\");\n   const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db);\n   BOOST_CHECK(core_asset_data.fee_pool == 0);\n\n   const auto& statistics_index = db.get_index_type<account_stats_index>().indices();\n   const auto& acct_balance_index = db.get_index_type<account_balance_index>().indices();\n   const auto& settle_index = db.get_index_type<force_settlement_index>().indices();\n   const auto& bids = db.get_index_type<collateral_bid_index>().indices();\n   map<asset_id_type,share_type> total_balances;\n   map<asset_id_type,share_type> total_debts;\n   share_type core_in_orders;\n   share_type core_inactive;\n   share_type core_pob;\n   share_type core_pol;\n   share_type pob_value;\n   share_type pol_value;\n   share_type reported_core_in_orders;\n   share_type reported_core_inactive;\n   share_type reported_core_pob;\n   share_type reported_core_pol;\n   share_type reported_pob_value;\n   share_type reported_pol_value;\n\n   for( const account_balance_object& b : acct_balance_index )\n      total_balances[b.asset_type] += b.balance;\n   for( const force_settlement_object& s : settle_index )\n      total_balances[s.balance.asset_id] += s.balance.amount;\n   for( const collateral_bid_object& b : bids )\n      total_balances[b.inv_swan_price.base.asset_id] += b.inv_swan_price.base.amount;\n   for( const account_statistics_object& a : statistics_index )\n   {\n      reported_core_in_orders += a.total_core_in_orders;\n      reported_core_inactive += a.total_core_inactive;\n      reported_core_pob += a.total_core_pob;\n      reported_core_pol += a.total_core_pol;\n      reported_pob_value += a.total_pob_value;\n      reported_pol_value += a.total_pol_value;\n      total_balances[asset_id_type()] += a.pending_fees + a.pending_vested_fees;\n   }\n   for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )\n   {\n      asset for_sale = o.amount_for_sale();\n      if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount;\n      total_balances[for_sale.asset_id] += for_sale.amount;\n      total_balances[asset_id_type()] += o.deferred_fee;\n      total_balances[o.deferred_paid_fee.asset_id] += o.deferred_paid_fee.amount;\n   }\n   for( const call_order_object& o : db.get_index_type<call_order_index>().indices() )\n   {\n      asset col = o.get_collateral();\n      if( col.asset_id == asset_id_type() ) core_in_orders += col.amount;\n      total_balances[col.asset_id] += col.amount;\n      total_debts[o.get_debt().asset_id] += o.get_debt().amount;\n   }\n   for( const asset_object& asset_obj : db.get_index_type<asset_index>().indices() )\n   {\n      const auto& dasset_obj = asset_obj.dynamic_asset_data_id(db);\n      total_balances[asset_obj.id] += dasset_obj.accumulated_fees;\n      total_balances[asset_id_type()] += dasset_obj.fee_pool;\n      if( asset_obj.is_market_issued() )\n      {\n         const auto& bad = asset_obj.bitasset_data(db);\n         total_balances[bad.options.short_backing_asset] += bad.settlement_fund;\n         total_balances[bad.options.short_backing_asset] += dasset_obj.accumulated_collateral_fees;\n      }\n      total_balances[asset_obj.id] += dasset_obj.confidential_supply.value;\n   }\n   for( const vesting_balance_object& vbo : db.get_index_type< vesting_balance_index >().indices() )\n      total_balances[ vbo.balance.asset_id ] += vbo.balance.amount;\n   for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() )\n      total_balances[ asset_id_type() ] += fba.accumulated_fba_fees;\n   for( const balance_object& bo : db.get_index_type< balance_index >().indices() )\n      total_balances[ bo.balance.asset_id ] += bo.balance.amount;\n   for( const ticket_object& to : db.get_index_type< ticket_index >().indices() )\n   {\n      if( to.amount.asset_id == asset_id_type() )\n      {\n         if( to.current_type == lock_forever && to.value == 0 )\n            core_inactive += to.amount.amount;\n         else if( to.current_type == lock_forever && to.value != 0 )\n         {\n            core_pob += to.amount.amount;\n            pob_value += to.value;\n         }\n         else\n         {\n            core_pol += to.amount.amount;\n            pol_value += to.value;\n         }\n      }\n      total_balances[ to.amount.asset_id ] += to.amount.amount;\n   }\n\n   total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;\n\n   for( const auto& item : total_debts )\n   {\n      BOOST_CHECK_EQUAL(item.first(db).dynamic_asset_data_id(db).current_supply.value, item.second.value);\n   }\n\n   // htlc\n   const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >();\n   for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr )\n   {\n      total_balances[itr->transfer.asset_id] += itr->transfer.amount;\n   }\n\n   for( const asset_object& asset_obj : db.get_index_type<asset_index>().indices() )\n   {\n      BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value);\n   }\n\n   BOOST_CHECK_EQUAL( core_in_orders.value , reported_core_in_orders.value );\n   BOOST_CHECK_EQUAL( core_inactive.value , reported_core_inactive.value );\n   BOOST_CHECK_EQUAL( core_pob.value , reported_core_pob.value );\n   BOOST_CHECK_EQUAL( core_pol.value , reported_core_pol.value );\n   BOOST_CHECK_EQUAL( pob_value.value , reported_pob_value.value );\n   BOOST_CHECK_EQUAL( pol_value.value , reported_pol_value.value );\n   BOOST_CHECK_EQUAL( core_pob.value , db.get_dynamic_global_properties().total_pob.value );\n   BOOST_CHECK_EQUAL( core_inactive.value , db.get_dynamic_global_properties().total_inactive.value );\n//   wlog(\"***  End  asset supply verification ***\");\n}\n\nvoid database_fixture::open_database()\n{\n   if( !data_dir ) {\n      data_dir = fc::temp_directory( graphene::utilities::temp_directory_path() );\n      db.open(data_dir->path(), [this]{return genesis_state;}, \"test\");\n   }\n}\n\nsigned_block database_fixture::generate_block(uint32_t skip, const fc::ecc::private_key& key, int miss_blocks)\n{\n   skip |= database::skip_undo_history_check;\n   // skip == ~0 will skip checks specified in database::validation_steps\n   auto block = db.generate_block(db.get_slot_time(miss_blocks + 1),\n                            db.get_scheduled_witness(miss_blocks + 1),\n                            key, skip);\n   db.clear_pending();\n   verify_asset_supplies(db);\n   return block;\n}\n\nvoid database_fixture::generate_blocks( uint32_t block_count )\n{\n   for( uint32_t i = 0; i < block_count; ++i )\n      generate_block();\n}\n\nuint32_t database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks, uint32_t skip)\n{\n   if( miss_intermediate_blocks )\n   {\n      generate_block(skip);\n      auto slots_to_miss = db.get_slot_at_time(timestamp);\n      if( slots_to_miss <= 1 )\n         return 1;\n      --slots_to_miss;\n      generate_block(skip, init_account_priv_key, slots_to_miss);\n      return 2;\n   }\n   uint32_t blocks = 0;\n   while( db.head_block_time() < timestamp )\n   {\n      generate_block(skip);\n      ++blocks;\n   }\n   return blocks;\n}\n\naccount_create_operation database_fixture::make_account(\n   const std::string& name /* = \"nathan\" */,\n   public_key_type key /* = key_id_type() */\n   )\n{ try {\n   account_create_operation create_account;\n   create_account.registrar = account_id_type();\n\n   create_account.name = name;\n   create_account.owner = authority(123, key, 123);\n   create_account.active = authority(321, key, 321);\n   create_account.options.memo_key = key;\n   create_account.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n\n   auto& active_committee_members = db.get_global_properties().active_committee_members;\n   if( active_committee_members.size() > 0 )\n   {\n      set<vote_id_type> votes;\n      votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n      votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n      votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n      votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n      votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n      create_account.options.votes = flat_set<vote_id_type>(votes.begin(), votes.end());\n   }\n   create_account.options.num_committee = create_account.options.votes.size();\n\n   create_account.fee = db.current_fee_schedule().calculate_fee( create_account );\n   return create_account;\n} FC_CAPTURE_AND_RETHROW() }\n\naccount_create_operation database_fixture::make_account(\n   const std::string& name,\n   const account_object& registrar,\n   const account_object& referrer,\n   uint16_t referrer_percent /* = 100 */,\n   public_key_type key /* = public_key_type() */\n   )\n{\n   try\n   {\n      account_create_operation          create_account;\n\n      create_account.registrar          = registrar.id;\n      create_account.referrer           = referrer.id;\n      create_account.referrer_percent   = referrer_percent;\n\n      create_account.name = name;\n      create_account.owner = authority(123, key, 123);\n      create_account.active = authority(321, key, 321);\n      create_account.options.memo_key = key;\n      create_account.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n\n      const vector<committee_member_id_type>& active_committee_members = db.get_global_properties().active_committee_members;\n      if( active_committee_members.size() > 0 )\n      {\n         set<vote_id_type> votes;\n         votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n         votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n         votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n         votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n         votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id);\n         create_account.options.votes = flat_set<vote_id_type>(votes.begin(), votes.end());\n      }\n      create_account.options.num_committee = create_account.options.votes.size();\n\n      create_account.fee = db.current_fee_schedule().calculate_fee( create_account );\n      return create_account;\n   }\n   FC_CAPTURE_AND_RETHROW((name)(referrer_percent))\n}\n\nconst asset_object& database_fixture::get_asset( const string& symbol )const\n{\n   const auto& idx = db.get_index_type<asset_index>().indices().get<by_symbol>();\n   const auto itr = idx.find(symbol);\n   assert( itr != idx.end() );\n   return *itr;\n}\n\nconst account_object& database_fixture::get_account( const string& name )const\n{\n   const auto& idx = db.get_index_type<account_index>().indices().get<by_name>();\n   const auto itr = idx.find(name);\n   assert( itr != idx.end() );\n   return *itr;\n}\n\nasset_create_operation database_fixture::make_bitasset(\n   const string& name,\n   account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */,\n   uint16_t market_fee_percent /* = 100 */ /* 1% */,\n   uint16_t flags /* = charge_market_fee */,\n   uint16_t precision /* = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS */,\n   asset_id_type backing_asset /* = CORE */,\n   share_type max_supply,  /* = GRAPHENE_MAX_SHARE_SUPPLY */\n   optional<uint16_t> initial_cr, /* = {} */\n   optional<uint16_t> margin_call_fee_ratio /* = {} */\n   )\n{\n   asset_create_operation creator;\n   creator.issuer = issuer;\n   creator.fee = asset();\n   creator.symbol = name;\n   creator.common_options.max_supply = max_supply;\n   creator.precision = precision;\n   creator.common_options.market_fee_percent = market_fee_percent;\n   if( issuer == GRAPHENE_WITNESS_ACCOUNT )\n      flags |= witness_fed_asset;\n   creator.common_options.issuer_permissions = flags;\n   creator.common_options.flags = flags & ~global_settle;\n   creator.common_options.core_exchange_rate = price(asset(1,asset_id_type(1)),asset(1));\n   creator.bitasset_opts = bitasset_options();\n   creator.bitasset_opts->short_backing_asset = backing_asset;\n   creator.bitasset_opts->extensions.value.initial_collateral_ratio = initial_cr;\n   creator.bitasset_opts->extensions.value.margin_call_fee_ratio = margin_call_fee_ratio;\n   return creator;\n}\n\nconst asset_object& database_fixture::create_bitasset(\n   const string& name,\n   account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */,\n   uint16_t market_fee_percent /* = 100 */ /* 1% */,\n   uint16_t flags /* = charge_market_fee */,\n   uint16_t precision /* = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS */,\n   asset_id_type backing_asset /* = CORE */,\n   share_type max_supply,  /* = GRAPHENE_MAX_SHARE_SUPPLY */\n   optional<uint16_t> initial_cr, /* = {} */\n   optional<uint16_t> margin_call_fee_ratio /* = {} */\n   )\n{ try {\n   asset_create_operation creator = make_bitasset( name, issuer, market_fee_percent, flags,\n                                                   precision, backing_asset, max_supply, initial_cr,\n                                                   margin_call_fee_ratio );\n   trx.operations.clear();\n   trx.operations.push_back(std::move(creator));\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n} FC_CAPTURE_AND_RETHROW( (name)(flags) ) }\n\nconst asset_object& database_fixture::create_prediction_market(\n   const string& name,\n   account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */,\n   uint16_t market_fee_percent /* = 100 */ /* 1% */,\n   uint16_t flags /* = charge_market_fee */,\n   uint16_t precision /* = 2, which seems arbitrary, but historically chosen */,\n   asset_id_type backing_asset /* = CORE */\n   )\n{ try {\n   asset_create_operation creator;\n   creator.issuer = issuer;\n   creator.fee = asset();\n   creator.symbol = name;\n   creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n   creator.precision = precision;\n   creator.common_options.market_fee_percent = market_fee_percent;\n   creator.common_options.issuer_permissions = flags | global_settle;\n   creator.common_options.flags = flags & ~global_settle;\n   if( issuer == GRAPHENE_WITNESS_ACCOUNT )\n      creator.common_options.flags |= witness_fed_asset;\n   creator.common_options.core_exchange_rate = price(asset(1,asset_id_type(1)),asset(1));\n   creator.bitasset_opts = bitasset_options();\n   creator.bitasset_opts->short_backing_asset = backing_asset;\n   creator.is_prediction_market = true;\n   trx.operations.clear();\n   trx.operations.push_back(std::move(creator));\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n} FC_CAPTURE_AND_RETHROW( (name)(flags) ) }\n\n\nconst asset_object& database_fixture::create_user_issued_asset( const string& name )\n{\n   asset_create_operation creator;\n   creator.issuer = account_id_type();\n   creator.fee = asset();\n   creator.symbol = name;\n   creator.common_options.max_supply = 0;\n   creator.precision = 2;\n   creator.common_options.core_exchange_rate = price(asset(1,asset_id_type(1)),asset(1));\n   creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n   creator.common_options.flags = charge_market_fee;\n   creator.common_options.issuer_permissions = charge_market_fee;\n   trx.operations.clear();\n   trx.operations.push_back(std::move(creator));\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n}\n\nconst asset_object& database_fixture::create_user_issued_asset( const string& name, const account_object& issuer,\n                                                               uint16_t flags, const price& core_exchange_rate,\n                                                               uint8_t precision, uint16_t market_fee_percent,\n                                                               additional_asset_options_t additional_options)\n{\n   asset_create_operation creator;\n   creator.issuer = issuer.id;\n   creator.fee = asset();\n   creator.symbol = name;\n   creator.common_options.max_supply = 0;\n   creator.precision = precision;\n   creator.common_options.core_exchange_rate = core_exchange_rate;\n   creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n   creator.common_options.flags = flags;\n   creator.common_options.issuer_permissions = flags;\n   creator.common_options.market_fee_percent = market_fee_percent;\n   creator.common_options.extensions = std::move(additional_options);\n   trx.operations.clear();\n   trx.operations.push_back(std::move(creator));\n   set_expiration( db, trx );\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n}\n\nvoid database_fixture::issue_uia( const account_object& recipient, asset amount )\n{\n   BOOST_TEST_MESSAGE( \"Issuing UIA\" );\n   asset_issue_operation op;\n   op.issuer = amount.asset_id(db).issuer;\n   op.asset_to_issue = amount;\n   op.issue_to_account = recipient.id;\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   PUSH_TX( db, trx, ~0 );\n   trx.operations.clear();\n}\n\nvoid database_fixture::issue_uia( account_id_type recipient_id, asset amount )\n{\n   issue_uia( recipient_id(db), amount );\n}\n\nvoid database_fixture::reserve_asset( account_id_type account, asset amount )\n{\n   BOOST_TEST_MESSAGE( \"Reserving asset\" );\n   asset_reserve_operation op;\n   op.payer = account;\n   op.amount_to_reserve = amount;\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   set_expiration( db, trx );\n   trx.validate();\n   PUSH_TX( db, trx, ~0 );\n   trx.operations.clear();\n}\n\nvoid database_fixture::change_fees(\n   const fee_parameters::flat_set_type& new_params,\n   uint32_t new_scale /* = 0 */\n   )\n{\n   const chain_parameters& current_chain_params = db.get_global_properties().parameters;\n   const fee_schedule& current_fees = current_chain_params.get_current_fees();\n\n   flat_map< int, fee_parameters > fee_map;\n   fee_map.reserve( current_fees.parameters.size() );\n   for( const fee_parameters& op_fee : current_fees.parameters )\n      fee_map[ op_fee.which() ] = op_fee;\n   for( const fee_parameters& new_fee : new_params )\n      fee_map[ new_fee.which() ] = new_fee;\n\n   fee_schedule_type new_fees;\n\n   for( const std::pair< int, fee_parameters >& item : fee_map )\n      new_fees.parameters.insert( item.second );\n   if( new_scale != 0 )\n      new_fees.scale = new_scale;\n\n   chain_parameters new_chain_params = current_chain_params;\n   new_chain_params.get_mutable_fees() = new_fees;\n\n   db.modify(db.get_global_properties(), [&](global_property_object& p) {\n      p.parameters = new_chain_params;\n   });\n}\n\nconst account_object& database_fixture::create_account(\n   const string& name,\n   const public_key_type& key /* = public_key_type() */\n   )\n{\n   trx.operations.clear();\n   trx.operations.push_back(make_account(name, key));\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   auto& result = db.get<account_object>(ptx.operation_results[0].get<object_id_type>());\n   trx.operations.clear();\n   return result;\n}\n\nconst account_object& database_fixture::create_account(\n   const string& name,\n   const account_object& registrar,\n   const account_object& referrer,\n   uint16_t referrer_percent /* = 100 (1%)*/,\n   const public_key_type& key /*= public_key_type()*/\n   )\n{\n   try\n   {\n      trx.operations.resize(1);\n      trx.operations.back() = (make_account(name, registrar, referrer, referrer_percent, key));\n      trx.validate();\n      auto r = PUSH_TX(db, trx, ~0);\n      const auto& result = db.get<account_object>(r.operation_results[0].get<object_id_type>());\n      trx.operations.clear();\n      return result;\n   }\n   FC_CAPTURE_AND_RETHROW( (name)(registrar)(referrer) )\n}\n\nconst account_object& database_fixture::create_account(\n   const string& name,\n   const private_key_type& key,\n   const account_id_type& registrar_id /* = account_id_type() */,\n   const account_id_type& referrer_id /* = account_id_type() */,\n   uint16_t referrer_percent /* = 100 (1%)*/\n   )\n{\n   try\n   {\n      trx.operations.clear();\n\n      account_create_operation account_create_op;\n\n      account_create_op.registrar = registrar_id;\n      account_create_op.referrer = referrer_id;\n      account_create_op.referrer_percent = referrer_percent;\n      account_create_op.name = name;\n      account_create_op.owner = authority(1234, public_key_type(key.get_public_key()), 1234);\n      account_create_op.active = authority(5678, public_key_type(key.get_public_key()), 5678);\n      account_create_op.options.memo_key = key.get_public_key();\n      account_create_op.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n      trx.operations.push_back( account_create_op );\n\n      trx.validate();\n\n      processed_transaction ptx = PUSH_TX(db, trx, ~0);\n      const account_object& result = db.get<account_object>(ptx.operation_results[0].get<object_id_type>());\n      trx.operations.clear();\n      return result;\n   }\n   FC_CAPTURE_AND_RETHROW( (name)(registrar_id)(referrer_id) )\n}\n\nconst committee_member_object& database_fixture::create_committee_member( const account_object& owner )\n{\n   committee_member_create_operation op;\n   op.committee_member_account = owner.id;\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   return db.get<committee_member_object>(ptx.operation_results[0].get<object_id_type>());\n}\n\nconst witness_object&database_fixture::create_witness(account_id_type owner,\n                                                        const fc::ecc::private_key& signing_private_key,\n                                                        uint32_t skip_flags )\n{\n   return create_witness(owner(db), signing_private_key, skip_flags );\n}\n\nconst witness_object& database_fixture::create_witness( const account_object& owner,\n                                                        const fc::ecc::private_key& signing_private_key,\n                                                        uint32_t skip_flags )\n{ try {\n   witness_create_operation op;\n   op.witness_account = owner.id;\n   op.block_signing_key = signing_private_key.get_public_key();\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, skip_flags );\n   trx.clear();\n   return db.get<witness_object>(ptx.operation_results[0].get<object_id_type>());\n} FC_CAPTURE_AND_RETHROW() }\n\nconst worker_object& database_fixture::create_worker( const account_id_type owner, const share_type daily_pay, const fc::microseconds& duration )\n{ try {\n   worker_create_operation op;\n   op.owner = owner;\n   op.daily_pay = daily_pay;\n   op.initializer = burn_worker_initializer();\n   op.work_begin_date = db.head_block_time();\n   op.work_end_date = op.work_begin_date + duration;\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   trx.clear();\n   return db.get<worker_object>(ptx.operation_results[0].get<object_id_type>());\n} FC_CAPTURE_AND_RETHROW() }\n\nuint64_t database_fixture::fund(\n   const account_object& account,\n   const asset& amount /* = asset(500000) */\n   )\n{\n   transfer(account_id_type()(db), account, amount);\n   return get_balance(account, amount.asset_id(db));\n}\n\nvoid database_fixture::sign(signed_transaction& trx, const fc::ecc::private_key& key)\n{\n   trx.sign( key, db.get_chain_id() );\n}\n\ndigest_type database_fixture::digest( const transaction& tx )\n{\n   return tx.digest();\n}\n\nconst limit_order_object*database_fixture::create_sell_order(account_id_type user, const asset& amount, const asset& recv,\n                                                const time_point_sec order_expiration,\n                                                const price& fee_core_exchange_rate )\n{\n   auto r =  create_sell_order( user(db), amount, recv, order_expiration, fee_core_exchange_rate );\n   verify_asset_supplies(db);\n   return r;\n}\n\nconst limit_order_object* database_fixture::create_sell_order( const account_object& user, const asset& amount, const asset& recv,\n                                                const time_point_sec order_expiration,\n                                                const price& fee_core_exchange_rate )\n{\n   set_expiration( db, trx );\n   trx.operations.clear();\n\n   limit_order_create_operation buy_order;\n   buy_order.seller = user.id;\n   buy_order.amount_to_sell = amount;\n   buy_order.min_to_receive = recv;\n   buy_order.expiration = order_expiration;\n   trx.operations.push_back(buy_order);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op, fee_core_exchange_rate);\n   trx.validate();\n   auto processed = PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n   return db.find<limit_order_object>( processed.operation_results[0].get<object_id_type>() );\n}\n\nasset database_fixture::cancel_limit_order( const limit_order_object& order )\n{\n  limit_order_cancel_operation cancel_order;\n  cancel_order.fee_paying_account = order.seller;\n  cancel_order.order = order.id;\n  trx.operations.clear();\n  trx.operations.push_back(cancel_order);\n  for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n  trx.validate();\n  auto processed = PUSH_TX(db, trx, ~0);\n  trx.operations.clear();\n   verify_asset_supplies(db);\n  return processed.operation_results[0].get<asset>();\n}\n\nvoid database_fixture::transfer(\n   account_id_type from,\n   account_id_type to,\n   const asset& amount,\n   const asset& fee /* = asset() */\n   )\n{\n   transfer(from(db), to(db), amount, fee);\n}\n\nvoid database_fixture::transfer(\n   const account_object& from,\n   const account_object& to,\n   const asset& amount,\n   const asset& fee /* = asset() */ )\n{\n   try\n   {\n      set_expiration( db, trx );\n      transfer_operation trans;\n      trans.from = from.id;\n      trans.to   = to.id;\n      trans.amount = amount;\n      trx.operations.clear();\n      trx.operations.push_back(trans);\n\n      if( fee == asset() )\n      {\n         for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n      }\n      trx.validate();\n      PUSH_TX(db, trx, ~0);\n      verify_asset_supplies(db);\n      trx.operations.clear();\n   } FC_CAPTURE_AND_RETHROW( (from.id)(to.id)(amount)(fee) )\n}\n\nvoid database_fixture::update_feed_producers( const asset_object& mia, flat_set<account_id_type> producers )\n{ try {\n   set_expiration( db, trx );\n   trx.operations.clear();\n   asset_update_feed_producers_operation op;\n   op.asset_to_update = mia.id;\n   op.issuer = mia.issuer;\n   op.new_feed_producers = std::move(producers);\n   trx.operations = {std::move(op)};\n\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n} FC_CAPTURE_AND_RETHROW( (mia)(producers) ) }\n\nvoid database_fixture::publish_feed( const asset_object& mia, const account_object& by, const price_feed& f,\n                                     const optional<uint16_t> icr )\n{\n   set_expiration( db, trx );\n   trx.operations.clear();\n\n   asset_publish_feed_operation op;\n   op.publisher = by.id;\n   op.asset_id = mia.id;\n   op.feed = f;\n   if( op.feed.core_exchange_rate.is_null() )\n   {\n      op.feed.core_exchange_rate = op.feed.settlement_price;\n      if( db.head_block_time() > HARDFORK_480_TIME )\n         op.feed.core_exchange_rate.quote.asset_id = asset_id_type();\n   }\n   op.extensions.value.initial_collateral_ratio = icr;\n   trx.operations.emplace_back( std::move(op) );\n\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n}\n\nvoid database_fixture::publish_feed(const account_id_type& publisher,\n      const asset_id_type& asset1, int64_t amount1,\n      const asset_id_type& asset2, int64_t amount2,\n      const asset_id_type& core_id, const optional<uint16_t> icr)\n{\n   const asset_object& a1 = asset1(db);\n   const asset_object& a2 = asset2(db);\n   const asset_object& core = core_id(db);\n   asset_publish_feed_operation op;\n   op.publisher = publisher;\n   op.asset_id = asset2;\n   op.feed.settlement_price = ~price(a1.amount(amount1),a2.amount(amount2));\n   op.feed.core_exchange_rate = ~price(core.amount(amount1), a2.amount(amount2));\n   op.extensions.value.initial_collateral_ratio = icr;\n   trx.operations.clear();\n   trx.operations.emplace_back(std::move(op));\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   set_expiration( db, trx );\n   PUSH_TX( db, trx, ~0);\n   verify_asset_supplies(db);\n   generate_block();\n   trx.clear();\n}\n\nvoid database_fixture::force_global_settle( const asset_object& what, const price& p )\n{ try {\n   set_expiration( db, trx );\n   trx.operations.clear();\n   asset_global_settle_operation sop;\n   sop.issuer = what.issuer;\n   sop.asset_to_settle = what.id;\n   sop.settle_price = p;\n   trx.operations.push_back(sop);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n} FC_CAPTURE_AND_RETHROW( (what)(p) ) }\n\noperation_result database_fixture::force_settle( const account_object& who, asset what )\n{ try {\n   set_expiration( db, trx );\n   trx.operations.clear();\n   asset_settle_operation sop;\n   sop.account = who.id;\n   sop.amount = what;\n   trx.operations.push_back(sop);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   const operation_result& op_result = ptx.operation_results.front();\n   trx.operations.clear();\n   verify_asset_supplies(db);\n   return op_result;\n} FC_CAPTURE_AND_RETHROW( (who)(what) ) }\n\nconst call_order_object* database_fixture::borrow( const account_object& who, asset what, asset collateral,\n                                                   optional<uint16_t> target_cr )\n{ try {\n   set_expiration( db, trx );\n   trx.operations.clear();\n   call_order_update_operation update = {};\n   update.funding_account = who.id;\n   update.delta_collateral = collateral;\n   update.delta_debt = what;\n   update.extensions.value.target_collateral_ratio = target_cr;\n   trx.operations.push_back(update);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n\n   auto& call_idx = db.get_index_type<call_order_index>().indices().get<by_account>();\n   auto itr = call_idx.find( boost::make_tuple(who.id, what.asset_id) );\n   const call_order_object* call_obj = nullptr;\n\n   if( itr != call_idx.end() )\n      call_obj = &*itr;\n   return call_obj;\n} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral)(target_cr) ) }\n\nvoid database_fixture::cover(const account_object& who, asset what, asset collateral, optional<uint16_t> target_cr)\n{ try {\n   set_expiration( db, trx );\n   trx.operations.clear();\n   call_order_update_operation update = {};\n   update.funding_account = who.id;\n   update.delta_collateral = -collateral;\n   update.delta_debt = -what;\n   update.extensions.value.target_collateral_ratio = target_cr;\n   trx.operations.push_back(update);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral)(target_cr) ) }\n\nvoid database_fixture::bid_collateral(const account_object& who, const asset& to_bid, const asset& to_cover)\n{ try {\n   set_expiration( db, trx );\n   trx.operations.clear();\n   bid_collateral_operation bid;\n   bid.bidder = who.id;\n   bid.additional_collateral = to_bid;\n   bid.debt_covered = to_cover;\n   trx.operations.push_back(bid);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n} FC_CAPTURE_AND_RETHROW( (who.name)(to_bid)(to_cover) ) }\n\nvoid database_fixture::fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount )\n{\n   asset_fund_fee_pool_operation fund;\n   fund.from_account = from.id;\n   fund.asset_id = asset_to_fund.id;\n   fund.amount = amount;\n   trx.operations.clear();\n   trx.operations.push_back( fund );\n\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   set_expiration( db, trx );\n   PUSH_TX(db, trx, ~0);\n   trx.operations.clear();\n   verify_asset_supplies(db);\n}\n\nticket_create_operation database_fixture::make_ticket_create_op( account_id_type account, ticket_type type,\n                                                                 const asset& amount ) const\n{\n   ticket_create_operation op;\n   op.account = account;\n   op.target_type = static_cast<uint8_t>(type);\n   op.amount = amount;\n   return op;\n}\n\nconst ticket_object& database_fixture::create_ticket( account_id_type account, ticket_type type,\n                                                      const asset& amount )\n{\n   ticket_create_operation op = make_ticket_create_op( account, type, amount );\n   trx.operations.clear();\n   trx.operations.push_back( op );\n\n   for( auto& o : trx.operations ) db.current_fee_schedule().set_fee(o);\n   trx.validate();\n   set_expiration( db, trx );\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   const operation_result& op_result = ptx.operation_results.front();\n   trx.operations.clear();\n   verify_asset_supplies(db);\n   return db.get<ticket_object>( op_result.get<object_id_type>() );\n}\n\nticket_update_operation database_fixture::make_ticket_update_op( const ticket_object& ticket, ticket_type type,\n                                                                 const optional<asset>& amount ) const\n{\n   ticket_update_operation op;\n   op.ticket = ticket.id;\n   op.account = ticket.account;\n   op.target_type = static_cast<uint8_t>(type);\n   op.amount_for_new_target = amount;\n   return op;\n}\n\ngeneric_operation_result database_fixture::update_ticket( const ticket_object& ticket, ticket_type type,\n                                                          const optional<asset>& amount )\n{\n   ticket_update_operation op = make_ticket_update_op( ticket, type, amount );\n   trx.operations.clear();\n   trx.operations.push_back( op );\n\n   for( auto& o : trx.operations ) db.current_fee_schedule().set_fee(o);\n   trx.validate();\n   set_expiration( db, trx );\n   processed_transaction ptx = PUSH_TX(db, trx, ~0);\n   const operation_result& op_result = ptx.operation_results.front();\n   trx.operations.clear();\n   verify_asset_supplies(db);\n   return op_result.get<generic_operation_result>();\n}\n\nvoid database_fixture::enable_fees()\n{\n   db.modify(global_property_id_type()(db), [](global_property_object& gpo)\n   {\n      gpo.parameters.get_mutable_fees() = fee_schedule::get_default();\n   });\n}\n\nvoid database_fixture::upgrade_to_lifetime_member(account_id_type account)\n{\n   upgrade_to_lifetime_member(account(db));\n}\n\nvoid database_fixture::upgrade_to_lifetime_member( const account_object& account )\n{\n   try\n   {\n      account_upgrade_operation op;\n      op.account_to_upgrade = account.get_id();\n      op.upgrade_to_lifetime_member = true;\n      op.fee = db.get_global_properties().parameters.get_current_fees().calculate_fee(op);\n      trx.operations = {op};\n      PUSH_TX(db, trx, ~0);\n      FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() );\n      trx.clear();\n      verify_asset_supplies(db);\n   }\n   FC_CAPTURE_AND_RETHROW((account))\n}\n\nvoid database_fixture::upgrade_to_annual_member(account_id_type account)\n{\n   upgrade_to_annual_member(account(db));\n}\n\nvoid database_fixture::upgrade_to_annual_member(const account_object& account)\n{\n   try {\n      account_upgrade_operation op;\n      op.account_to_upgrade = account.get_id();\n      op.fee = db.get_global_properties().parameters.get_current_fees().calculate_fee(op);\n      trx.operations = {op};\n      PUSH_TX(db, trx, ~0);\n      FC_ASSERT( op.account_to_upgrade(db).is_member(db.head_block_time()) );\n      trx.clear();\n      verify_asset_supplies(db);\n   } FC_CAPTURE_AND_RETHROW((account))\n}\n\nvoid database_fixture::print_market( const string& syma, const string& symb )const\n{\n   const auto& limit_idx = db.get_index_type<limit_order_index>();\n   const auto& price_idx = limit_idx.indices().get<by_price>();\n\n   cerr << std::fixed;\n   cerr.precision(5);\n   cerr << std::setw(10) << std::left  << \"NAME\"      << \" \";\n   cerr << std::setw(16) << std::right << \"FOR SALE\"  << \" \";\n   cerr << std::setw(16) << std::right << \"FOR WHAT\"  << \" \";\n   cerr << std::setw(10) << std::right << \"PRICE (S/W)\"   << \" \";\n   cerr << std::setw(10) << std::right << \"1/PRICE (W/S)\" << \"\\n\";\n   cerr << string(70, '=') << std::endl;\n   auto cur = price_idx.begin();\n   while( cur != price_idx.end() )\n   {\n      cerr << std::setw( 10 ) << std::left   << cur->seller(db).name << \" \";\n      cerr << std::setw( 10 ) << std::right  << cur->for_sale.value << \" \";\n      cerr << std::setw( 5 )  << std::left   << cur->amount_for_sale().asset_id(db).symbol << \" \";\n      cerr << std::setw( 10 ) << std::right  << cur->amount_to_receive().amount.value << \" \";\n      cerr << std::setw( 5 )  << std::left   << cur->amount_to_receive().asset_id(db).symbol << \" \";\n      cerr << std::setw( 10 ) << std::right  << cur->sell_price.to_real() << \" \";\n      cerr << std::setw( 10 ) << std::right  << (~cur->sell_price).to_real() << \" \";\n      cerr << \"\\n\";\n      ++cur;\n   }\n}\n\nstring database_fixture::pretty( const asset& a )const\n{\n  std::stringstream ss;\n  ss << a.amount.value << \" \";\n  ss << a.asset_id(db).symbol;\n  return ss.str();\n}\n\nvoid database_fixture::print_limit_order( const limit_order_object& cur )const\n{\n  std::cout << std::setw(10) << cur.seller(db).name << \" \";\n  std::cout << std::setw(10) << \"LIMIT\" << \" \";\n  std::cout << std::setw(16) << pretty( cur.amount_for_sale() ) << \" \";\n  std::cout << std::setw(16) << pretty( cur.amount_to_receive() ) << \" \";\n  std::cout << std::setw(16) << cur.sell_price.to_real() << \" \";\n}\n\nvoid database_fixture::print_call_orders()const\n{\n  cout << std::fixed;\n  cout.precision(5);\n  cout << std::setw(10) << std::left  << \"NAME\"      << \" \";\n  cout << std::setw(10) << std::right << \"TYPE\"      << \" \";\n  cout << std::setw(16) << std::right << \"DEBT\"  << \" \";\n  cout << std::setw(16) << std::right << \"COLLAT\"  << \" \";\n  cout << std::setw(16) << std::right << \"CALL PRICE(D/C)\"     << \" \";\n  cout << std::setw(16) << std::right << \"~CALL PRICE(C/D)\"     << \" \";\n  cout << std::setw(16) << std::right << \"SWAN(D/C)\"     << \" \";\n  cout << std::setw(16) << std::right << \"SWAN(C/D)\"     << \"\\n\";\n  cout << string(70, '=');\n\n  for( const call_order_object& o : db.get_index_type<call_order_index>().indices() )\n  {\n     std::cout << \"\\n\";\n     cout << std::setw( 10 ) << std::left   << o.borrower(db).name << \" \";\n     cout << std::setw( 16 ) << std::right  << pretty( o.get_debt() ) << \" \";\n     cout << std::setw( 16 ) << std::right  << pretty( o.get_collateral() ) << \" \";\n     cout << std::setw( 16 ) << std::right  << o.call_price.to_real() << \" \";\n     cout << std::setw( 16 ) << std::right  << (~o.call_price).to_real() << \" \";\n     cout << std::setw( 16 ) << std::right  << (o.get_debt()/o.get_collateral()).to_real() << \" \";\n     cout << std::setw( 16 ) << std::right  << (~(o.get_debt()/o.get_collateral())).to_real() << \" \";\n  }\n     std::cout << \"\\n\";\n}\n\nvoid database_fixture::print_joint_market( const string& syma, const string& symb )const\n{\n  cout << std::fixed;\n  cout.precision(5);\n\n  cout << std::setw(10) << std::left  << \"NAME\"      << \" \";\n  cout << std::setw(10) << std::right << \"TYPE\"      << \" \";\n  cout << std::setw(16) << std::right << \"FOR SALE\"  << \" \";\n  cout << std::setw(16) << std::right << \"FOR WHAT\"  << \" \";\n  cout << std::setw(16) << std::right << \"PRICE (S/W)\" << \"\\n\";\n  cout << string(70, '=');\n\n  const auto& limit_idx = db.get_index_type<limit_order_index>();\n  const auto& limit_price_idx = limit_idx.indices().get<by_price>();\n\n  auto limit_itr = limit_price_idx.begin();\n  while( limit_itr != limit_price_idx.end() )\n  {\n     std::cout << std::endl;\n     print_limit_order( *limit_itr );\n     ++limit_itr;\n  }\n}\n\nint64_t database_fixture::get_balance( account_id_type account, asset_id_type a )const\n{\n  return db.get_balance(account, a).amount.value;\n}\n\nint64_t database_fixture::get_balance( const account_object& account, const asset_object& a )const\n{\n  return db.get_balance(account.get_id(), a.get_id()).amount.value;\n}\n\nint64_t database_fixture::get_market_fee_reward( account_id_type account_id, asset_id_type asset_id)const\n{\n   return db.get_market_fee_vesting_balance(account_id, asset_id).amount.value;\n}\n\nint64_t database_fixture::get_market_fee_reward( const account_object& account, const asset_object& asset )const\n{\n  return get_market_fee_reward(account.get_id(), asset.get_id());\n}\n\nvector< operation_history_object > database_fixture::get_operation_history( account_id_type account_id )const\n{\n   vector< operation_history_object > result;\n   const auto& stats = account_id(db).statistics(db);\n   if(stats.most_recent_op == account_transaction_history_id_type())\n      return result;\n\n   const account_transaction_history_object* node = &stats.most_recent_op(db);\n   while( true )\n   {\n      result.push_back( node->operation_id(db) );\n      if(node->next == account_transaction_history_id_type())\n         break;\n      node = db.find(node->next);\n   }\n   return result;\n}\n\nvector< graphene::market_history::order_history_object > database_fixture::get_market_order_history( asset_id_type a, asset_id_type b )const\n{\n   const auto& history_idx = db.get_index_type<graphene::market_history::history_index>().indices().get<graphene::market_history::by_key>();\n   graphene::market_history::history_key hkey;\n   if( a > b ) std::swap(a,b);\n   hkey.base = a;\n   hkey.quote = b;\n   hkey.sequence = std::numeric_limits<int64_t>::min();\n   auto itr = history_idx.lower_bound( hkey );\n   vector<graphene::market_history::order_history_object> result;\n   while( itr != history_idx.end())\n   {\n       result.push_back( *itr );\n       ++itr;\n   }\n   return result;\n}\n\nflat_map< uint64_t, graphene::chain::fee_parameters > database_fixture::get_htlc_fee_parameters()\n{\n   flat_map<uint64_t, graphene::chain::fee_parameters> ret_val;\n\n   htlc_create_operation::fee_parameters_type create_param;\n   create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   ret_val[((operation)htlc_create_operation()).which()] = create_param;\n\n   htlc_redeem_operation::fee_parameters_type redeem_param;\n   redeem_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   redeem_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   ret_val[((operation)htlc_redeem_operation()).which()] = redeem_param;\n\n   htlc_extend_operation::fee_parameters_type extend_param;\n   extend_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   extend_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   ret_val[((operation)htlc_extend_operation()).which()] = extend_param;\n\n   // set the transfer kb fee to something other than default, to verify we're looking\n   // at the correct fee\n   transfer_operation::fee_parameters_type transfer_param;\n   transfer_param.price_per_kbyte *= 2;\n   ret_val[ ((operation)transfer_operation()).which() ] = transfer_param;\n\n   return ret_val;\n}\n\nvoid database_fixture::set_htlc_committee_parameters()\n{\n   // htlc fees\n   // get existing fee_schedule\n   const chain_parameters& existing_params = db.get_global_properties().parameters;\n   const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees);\n   // create a new fee_shedule\n   std::shared_ptr<fee_schedule_type> new_fee_schedule = std::make_shared<fee_schedule_type>();\n   new_fee_schedule->scale = GRAPHENE_100_PERCENT;\n   // replace the old with the new\n   flat_map<uint64_t, graphene::chain::fee_parameters> htlc_fees = get_htlc_fee_parameters();\n   for(auto param : existing_fee_schedule.parameters)\n   {\n      auto itr = htlc_fees.find(param.which());\n      if (itr == htlc_fees.end()) {\n         // Only define fees for operations which are already forked in!\n         if (hardfork_visitor(db.head_block_time()).visit(param.which()))\n            new_fee_schedule->parameters.insert(param);\n      } else {\n         new_fee_schedule->parameters.insert( (*itr).second);\n      }\n   }\n   // htlc parameters\n   proposal_create_operation cop = proposal_create_operation::committee_proposal(\n         db.get_global_properties().parameters, db.head_block_time());\n   cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n   cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n   committee_member_update_global_parameters_operation uop;\n   graphene::chain::htlc_options new_params;\n   new_params.max_preimage_size = 19200;\n   new_params.max_timeout_secs = 60 * 60 * 24 * 28;\n   uop.new_parameters.extensions.value.updatable_htlc_options = new_params;\n   uop.new_parameters.current_fees = new_fee_schedule;\n   cop.proposed_ops.emplace_back(uop);\n\n   trx.operations.clear();\n   trx.operations.push_back(cop);\n   graphene::chain::processed_transaction proc_trx = db.push_transaction(trx);\n   trx.clear();\n   proposal_id_type good_proposal_id = proc_trx.operation_results[0].get<object_id_type>();\n\n   proposal_update_operation puo;\n   puo.proposal = good_proposal_id;\n   puo.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n   puo.key_approvals_to_add.emplace( init_account_priv_key.get_public_key() );\n   trx.operations.push_back(puo);\n   sign( trx, init_account_priv_key );\n   db.push_transaction(trx);\n   trx.clear();\n\n   generate_blocks( good_proposal_id( db ).expiration_time + 5 );\n   generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n   generate_block();   // get the maintenance skip slots out of the way\n\n}\n\nnamespace test {\n\nvoid set_expiration( const database& db, transaction& tx )\n{\n   const chain_parameters& params = db.get_global_properties().parameters;\n   tx.set_reference_block(db.head_block_id());\n   tx.set_expiration( db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3 ) );\n   return;\n}\n\nbool _push_block( database& db, const signed_block& b, uint32_t skip_flags /* = 0 */ )\n{\n   return db.push_block( b, skip_flags);\n}\n\nprocessed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags /* = 0 */ )\n{ try {\n   auto pt = db.push_transaction( precomputable_transaction(tx), skip_flags );\n   database_fixture::verify_asset_supplies(db);\n   return pt;\n} FC_CAPTURE_AND_RETHROW((tx)) }\n\n} // graphene::chain::test\n\n} } // graphene::chain\n"
  },
  {
    "path": "tests/common/database_fixture.hpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include <fc/io/json.hpp>\n\n#include <graphene/protocol/types.hpp>\n#include <graphene/protocol/market.hpp>\n\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/ticket_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/operation_history_object.hpp>\n#include <graphene/chain/database.hpp>\n#include <graphene/app/application.hpp>\n#include <graphene/market_history/market_history_plugin.hpp>\n\n#include <iostream>\n\nusing namespace graphene::db;\n\nextern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;\n\n#define PUSH_TX \\\n   graphene::chain::test::_push_transaction\n\n#define PUSH_BLOCK \\\n   graphene::chain::test::_push_block\n\n// See below\n#define REQUIRE_OP_VALIDATION_SUCCESS( op, field, value ) \\\n{ \\\n   const auto temp = op.field; \\\n   op.field = value; \\\n   op.validate(); \\\n   op.field = temp; \\\n}\n#define REQUIRE_OP_EVALUATION_SUCCESS( op, field, value ) \\\n{ \\\n   const auto temp = op.field; \\\n   op.field = value; \\\n   trx.operations.back() = op; \\\n   op.field = temp; \\\n   db.push_transaction( trx, ~0 ); \\\n}\n\n#define GRAPHENE_REQUIRE_THROW( expr, exc_type )          \\\n{                                                         \\\n   std::string req_throw_info = fc::json::to_string(      \\\n      fc::mutable_variant_object()                        \\\n      (\"source_file\", __FILE__)                           \\\n      (\"source_lineno\", __LINE__)                         \\\n      (\"expr\", #expr)                                     \\\n      (\"exc_type\", #exc_type)                             \\\n      );                                                  \\\n   if( fc::enable_record_assert_trip )                    \\\n      std::cout << \"GRAPHENE_REQUIRE_THROW begin \"        \\\n         << req_throw_info << std::endl;                  \\\n   BOOST_REQUIRE_THROW( expr, exc_type );                 \\\n   if( fc::enable_record_assert_trip )                    \\\n      std::cout << \"GRAPHENE_REQUIRE_THROW end \"          \\\n         << req_throw_info << std::endl;                  \\\n}\n\n#define GRAPHENE_CHECK_THROW( expr, exc_type )            \\\n{                                                         \\\n   std::string req_throw_info = fc::json::to_string(      \\\n      fc::mutable_variant_object()                        \\\n      (\"source_file\", __FILE__)                           \\\n      (\"source_lineno\", __LINE__)                         \\\n      (\"expr\", #expr)                                     \\\n      (\"exc_type\", #exc_type)                             \\\n      );                                                  \\\n   if( fc::enable_record_assert_trip )                    \\\n      std::cout << \"GRAPHENE_CHECK_THROW begin \"          \\\n         << req_throw_info << std::endl;                  \\\n   BOOST_CHECK_THROW( expr, exc_type );                   \\\n   if( fc::enable_record_assert_trip )                    \\\n      std::cout << \"GRAPHENE_CHECK_THROW end \"            \\\n         << req_throw_info << std::endl;                  \\\n}\n\n#define REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, exc_type ) \\\n{ \\\n   const auto temp = op.field; \\\n   op.field = value; \\\n   GRAPHENE_REQUIRE_THROW( op.validate(), exc_type ); \\\n   op.field = temp; \\\n}\n#define REQUIRE_OP_VALIDATION_FAILURE( op, field, value ) \\\n   REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, fc::exception )\n\n#define REQUIRE_EXCEPTION_WITH_TEXT(op, exc_text)                 \\\n{                                                                 \\\n   try                                                            \\\n   {                                                              \\\n      op;                                                         \\\n      BOOST_FAIL(std::string(\"Expected an exception with \\\"\") +   \\\n         std::string(exc_text) +                                  \\\n         std::string(\"\\\" but none thrown\"));                      \\\n   }                                                              \\\n   catch (fc::exception& ex)                                      \\\n   {                                                              \\\n      std::string what = ex.to_string(                            \\\n            fc::log_level(fc::log_level::all));                   \\\n      if (what.find(exc_text) == std::string::npos)               \\\n      {                                                           \\\n         BOOST_FAIL( std::string(\"Expected \\\"\") +                 \\\n            std::string(exc_text) +                               \\\n            std::string(\"\\\" but got \\\"\") +                        \\\n            std::string(what) );                                  \\\n      }                                                           \\\n   }                                                              \\\n}                                                                 \\\n\n#define REQUIRE_THROW_WITH_VALUE_2(op, field, value, exc_type) \\\n{ \\\n   auto bak = op.field; \\\n   op.field = value; \\\n   trx.operations.back() = op; \\\n   op.field = bak; \\\n   GRAPHENE_REQUIRE_THROW(db.push_transaction(trx, ~0), exc_type); \\\n}\n\n#define REQUIRE_THROW_WITH_VALUE( op, field, value ) \\\n   REQUIRE_THROW_WITH_VALUE_2( op, field, value, fc::exception )\n\n///This simply resets v back to its default-constructed value. Requires v to have a working assingment operator and\n/// default constructor.\n#define RESET(v) v = decltype(v)()\n///This allows me to build consecutive test cases. It's pretty ugly, but it works well enough for unit tests.\n/// i.e. This allows a test on update_account to begin with the database at the end state of create_account.\n#define INVOKE(test) ((struct test*)this)->test_method(); trx.clear()\n\n#define PREP_ACTOR(name) \\\n   fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name));   \\\n   graphene::chain::public_key_type name ## _public_key = name ## _private_key.get_public_key(); \\\n   BOOST_CHECK( name ## _public_key != public_key_type() );\n\n#define ACTOR(name) \\\n   PREP_ACTOR(name) \\\n   const auto name = create_account(BOOST_PP_STRINGIZE(name), name ## _public_key); \\\n   graphene::chain::account_id_type name ## _id = name.id; (void)name ## _id;\n\n#define GET_ACTOR(name) \\\n   fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \\\n   const account_object& name = get_account(BOOST_PP_STRINGIZE(name)); \\\n   graphene::chain::account_id_type name ## _id = name.id; \\\n   (void)name ##_id\n\n#define ACTORS_IMPL(r, data, elem) ACTOR(elem)\n#define ACTORS(names) BOOST_PP_SEQ_FOR_EACH(ACTORS_IMPL, ~, names)\n\n#define INITIAL_WITNESS_COUNT (9u)\n#define INITIAL_COMMITTEE_MEMBER_COUNT INITIAL_WITNESS_COUNT\n\nnamespace graphene { namespace chain {\n\nclass clearable_block : public signed_block {\npublic:\n   /** @brief Clears internal cached values like ID, signing key, Merkle root etc. */\n   void clear();\n};\n\nnamespace test {\n/// set a reasonable expiration time for the transaction\nvoid set_expiration( const database& db, transaction& tx );\n\nbool _push_block( database& db, const signed_block& b, uint32_t skip_flags = 0 );\nprocessed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags = 0 );\n} // namespace test\n\nstruct database_fixture {\n   // the reason we use an app is to exercise the indexes of built-in\n   //   plugins\n   graphene::app::application app;\n   genesis_state_type genesis_state;\n   chain::database &db;\n   signed_transaction trx;\n   public_key_type committee_key;\n   account_id_type committee_account;\n   fc::ecc::private_key private_key = fc::ecc::private_key::generate();\n   fc::ecc::private_key init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n   public_key_type init_account_pub_key;\n\n   optional<fc::temp_directory> data_dir;\n   bool skip_key_index_test = false;\n   uint32_t anon_acct_count;\n   bool hf1270 = false;\n   bool bsip77 = false;\n\n   database_fixture(const fc::time_point_sec &initial_timestamp =\n                        fc::time_point_sec(GRAPHENE_TESTING_GENESIS_TIMESTAMP));\n   ~database_fixture();\n\n   static fc::ecc::private_key generate_private_key(string seed);\n   string generate_anon_acct_name();\n   static void verify_asset_supplies( const database& db );\n   void open_database();\n   void vote_for_committee_and_witnesses(uint16_t num_committee, uint16_t num_witness);\n   signed_block generate_block(uint32_t skip = ~0,\n                               const fc::ecc::private_key& key = generate_private_key(\"null_key\"),\n                               int miss_blocks = 0);\n\n   /**\n    * @brief Generates block_count blocks\n    * @param block_count number of blocks to generate\n    */\n   void generate_blocks(uint32_t block_count);\n\n   /**\n    * @brief Generates blocks until the head block time matches or exceeds timestamp\n    * @param timestamp target time to generate blocks until\n    * @return number of blocks generated\n    */\n   uint32_t generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0);\n\n   account_create_operation make_account(\n      const std::string& name = \"nathan\",\n      public_key_type = public_key_type()\n      );\n\n   account_create_operation make_account(\n      const std::string& name,\n      const account_object& registrar,\n      const account_object& referrer,\n      uint16_t referrer_percent = 100,\n      public_key_type key = public_key_type()\n      );\n\n   void force_global_settle(const asset_object& what, const price& p);\n   operation_result force_settle(account_id_type who, asset what)\n   { return force_settle(who(db), what); }\n   operation_result force_settle(const account_object& who, asset what);\n   void update_feed_producers(asset_id_type mia, flat_set<account_id_type> producers)\n   { update_feed_producers(mia(db), producers); }\n   void update_feed_producers(const asset_object& mia, flat_set<account_id_type> producers);\n   void publish_feed(asset_id_type mia, account_id_type by, const price_feed& f, const optional<uint16_t> icr = {})\n   { publish_feed(mia(db), by(db), f, icr); }\n\n   /***\n    * @brief helper method to add a price feed\n    *\n    * Adds a price feed for asset2, pushes the transaction, and generates a block\n    *\n    * @param publisher who is publishing the feed\n    * @param asset1 the base asset\n    * @param amount1 the amount of the base asset\n    * @param asset2 the quote asset\n    * @param amount2 the amount of the quote asset\n    * @param core_id id of core (helps with core_exchange_rate)\n    * @param icr initial collateral ratio\n    */\n   void publish_feed(const account_id_type& publisher,\n         const asset_id_type& asset1, int64_t amount1,\n         const asset_id_type& asset2, int64_t amount2,\n         const asset_id_type& core_id, const optional<uint16_t> icr = {});\n\n   void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f,\n                      const optional<uint16_t> icr = {} );\n\n   const call_order_object* borrow( account_id_type who, asset what, asset collateral,\n                                    optional<uint16_t> target_cr = {} )\n   { return borrow(who(db), what, collateral, target_cr); }\n   const call_order_object* borrow( const account_object& who, asset what, asset collateral,\n                                    optional<uint16_t> target_cr = {} );\n   void cover(account_id_type who, asset what, asset collateral_freed,\n                                    optional<uint16_t> target_cr = {} )\n   { cover(who(db), what, collateral_freed, target_cr); }\n   void cover(const account_object& who, asset what, asset collateral_freed,\n                                    optional<uint16_t> target_cr = {} );\n   void bid_collateral(const account_object& who, const asset& to_bid, const asset& to_cover);\n\n   const asset_object& get_asset( const string& symbol )const;\n   const account_object& get_account( const string& name )const;\n   asset_create_operation make_bitasset( const string& name,\n                                       account_id_type issuer = GRAPHENE_WITNESS_ACCOUNT,\n                                       uint16_t market_fee_percent = 100 /*1%*/,\n                                       uint16_t flags = charge_market_fee,\n                                       uint16_t precision = 2,\n                                       asset_id_type backing_asset = {},\n                                       share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY,\n                                       optional<uint16_t> initial_cr = {},\n                                       optional<uint16_t> margin_call_fee_ratio = {} );\n   const asset_object& create_bitasset(const string& name,\n                                       account_id_type issuer = GRAPHENE_WITNESS_ACCOUNT,\n                                       uint16_t market_fee_percent = 100 /*1%*/,\n                                       uint16_t flags = charge_market_fee,\n                                       uint16_t precision = 2,\n                                       asset_id_type backing_asset = {},\n                                       share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY,\n                                       optional<uint16_t> initial_cr = {},\n                                       optional<uint16_t> margin_call_fee_ratio = {} );\n   const asset_object& create_prediction_market(const string& name,\n                                       account_id_type issuer = GRAPHENE_WITNESS_ACCOUNT,\n                                       uint16_t market_fee_percent = 100 /*1%*/,\n                                       uint16_t flags = charge_market_fee,\n                                       uint16_t precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS,\n                                       asset_id_type backing_asset = {});\n   const asset_object& create_user_issued_asset( const string& name );\n   const asset_object& create_user_issued_asset( const string& name,\n                                                 const account_object& issuer,\n                                                 uint16_t flags,\n                                                 const price& core_exchange_rate = price(asset(1, asset_id_type(1)), asset(1)),\n                                                 uint8_t precision = 2 /* traditional precision for tests */,\n                                                 uint16_t market_fee_percent = 0,\n                                                 additional_asset_options_t options = additional_asset_options_t());\n   void issue_uia( const account_object& recipient, asset amount );\n   void issue_uia( account_id_type recipient_id, asset amount );\n   void reserve_asset( account_id_type account, asset amount );\n\n   const account_object& create_account(\n      const string& name,\n      const public_key_type& key = public_key_type()\n      );\n\n   const account_object& create_account(\n      const string& name,\n      const account_object& registrar,\n      const account_object& referrer,\n      uint16_t referrer_percent = 100,\n      const public_key_type& key = public_key_type()\n      );\n\n   const account_object& create_account(\n      const string& name,\n      const private_key_type& key,\n      const account_id_type& registrar_id = account_id_type(),\n      const account_id_type& referrer_id = account_id_type(),\n      uint16_t referrer_percent = 100\n      );\n\n   const committee_member_object& create_committee_member( const account_object& owner );\n   const witness_object& create_witness(account_id_type owner,\n                                        const fc::ecc::private_key& signing_private_key = generate_private_key(\"null_key\"),\n                                        uint32_t skip_flags = ~0);\n   const witness_object& create_witness(const account_object& owner,\n                                        const fc::ecc::private_key& signing_private_key = generate_private_key(\"null_key\"),\n                                        uint32_t skip_flags = ~0);\n   const worker_object& create_worker(account_id_type owner, const share_type daily_pay = 1000, const fc::microseconds& duration = fc::days(2));\n   template<typename T>\n   proposal_create_operation make_proposal_create_op( const T& op, account_id_type proposer = GRAPHENE_TEMP_ACCOUNT,\n                                                      uint32_t timeout = 300, uint32_t review_period = 0 ) const\n   {\n      proposal_create_operation cop;\n      cop.fee_paying_account = proposer;\n      cop.expiration_time = db.head_block_time() + timeout;\n      cop.review_period_seconds = review_period;\n      cop.proposed_ops.emplace_back( op );\n      for( auto& o : cop.proposed_ops ) db.current_fee_schedule().set_fee(o.op);\n      return cop;\n   }\n   template<typename T>\n   const proposal_object& propose( const T& op, account_id_type proposer = GRAPHENE_TEMP_ACCOUNT,\n                                   uint32_t timeout = 300, uint32_t review_period = 0 )\n   {\n      proposal_create_operation cop = make_proposal_create_op( op, proposer, timeout, review_period );\n      trx.operations.clear();\n      trx.operations.push_back( cop );\n      for( auto& o : trx.operations ) db.current_fee_schedule().set_fee(o);\n      trx.validate();\n      test::set_expiration( db, trx );\n      processed_transaction ptx = PUSH_TX(db, trx, ~0);\n      const operation_result& op_result = ptx.operation_results.front();\n      trx.operations.clear();\n      verify_asset_supplies(db);\n      return db.get<proposal_object>( op_result.get<object_id_type>() );\n   }\n   uint64_t fund( const account_object& account, const asset& amount = asset(500000) );\n   digest_type digest( const transaction& tx );\n   void sign( signed_transaction& trx, const fc::ecc::private_key& key );\n   const limit_order_object* create_sell_order( account_id_type user, const asset& amount, const asset& recv,\n                                                const time_point_sec order_expiration = time_point_sec::maximum(),\n                                                const price& fee_core_exchange_rate = price::unit_price() );\n   const limit_order_object* create_sell_order( const account_object& user, const asset& amount, const asset& recv,\n                                                const time_point_sec order_expiration = time_point_sec::maximum(),\n                                                const price& fee_core_exchange_rate = price::unit_price() );\n   asset cancel_limit_order( const limit_order_object& order );\n   void transfer( account_id_type from, account_id_type to, const asset& amount, const asset& fee = asset() );\n   void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() );\n   void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount );\n   ticket_create_operation make_ticket_create_op( account_id_type account, ticket_type type,\n                                                  const asset& amount )const;\n   const ticket_object& create_ticket( account_id_type account, ticket_type type, const asset& amount );\n   ticket_update_operation make_ticket_update_op( const ticket_object& ticket, ticket_type type,\n                                                  const optional<asset>& amount )const;\n   generic_operation_result update_ticket( const ticket_object& ticket, ticket_type type,\n                                           const optional<asset>& amount );\n   /**\n    * NOTE: This modifies the database directly. You will probably have to call this each time you\n    * finish creating a block\n    */\n   void enable_fees();\n   void change_fees( const fee_parameters::flat_set_type& new_params, uint32_t new_scale = 0 );\n   void upgrade_to_lifetime_member( account_id_type account );\n   void upgrade_to_lifetime_member( const account_object& account );\n   void upgrade_to_annual_member( account_id_type account );\n   void upgrade_to_annual_member( const account_object& account );\n   void print_market( const string& syma, const string& symb )const;\n   string pretty( const asset& a )const;\n   void print_limit_order( const limit_order_object& cur )const;\n   void print_call_orders( )const;\n   void print_joint_market( const string& syma, const string& symb )const;\n   int64_t get_balance( account_id_type account, asset_id_type a )const;\n   int64_t get_balance( const account_object& account, const asset_object& a )const;\n\n   int64_t get_market_fee_reward( account_id_type account, asset_id_type asset )const;\n   int64_t get_market_fee_reward( const account_object& account, const asset_object& asset )const;\n\n   vector< operation_history_object > get_operation_history( account_id_type account_id )const;\n   vector< graphene::market_history::order_history_object > get_market_order_history( asset_id_type a, asset_id_type b )const;\n   bool validation_current_test_name_for_setting_api_limit( const string& current_test_name )const;\n\n   /****\n    * @brief return htlc fee parameters\n    */\n   flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters();\n   /****\n    * @brief push through a proposal that sets htlc parameters and fees\n    */\n   void set_htlc_committee_parameters();\n   /****\n    * Hash the preimage and put it in a vector\n    * @param preimage the preimage\n    * @returns a vector that cointains the sha256 hash of the preimage\n    */\n   template<typename H>\n   H hash_it(std::vector<char> preimage)\n   {\n      return H::hash( (char*)preimage.data(), preimage.size() );\n   }\n\n};\n\n} }\n"
  },
  {
    "path": "tests/common/genesis_file_util.hpp",
    "content": "#pragma once\n\n/////////\n/// @brief forward declaration, using as a hack to generate a genesis.json file\n/// for testing\n/////////\nnamespace graphene { namespace app { namespace detail {\n   graphene::chain::genesis_state_type create_example_genesis();\n} } } // graphene::app::detail\n\n/////////\n/// @brief create a genesis_json file\n/// @param directory the directory to place the file \"genesis.json\"\n/// @returns the full path to the file\n////////\nboost::filesystem::path create_genesis_file(fc::temp_directory& directory) {\n   boost::filesystem::path genesis_path = boost::filesystem::path{directory.path().generic_string()} / \"genesis.json\";\n   fc::path genesis_out = genesis_path;\n   graphene::chain::genesis_state_type genesis_state = graphene::app::detail::create_example_genesis();\n\n   /* Work In Progress: Place some accounts in the Genesis file so as to pre-make some accounts to play with\n   std::string test_prefix = \"test\";\n   // helper lambda\n   auto get_test_key = [&]( std::string prefix, uint32_t i ) -> public_key_type\n   {\n      return fc::ecc::private_key::regenerate( fc::sha256::hash( test_prefix + prefix + std::to_string(i) ) ).get_public_key();\n   };\n\n   // create 2 accounts to use\n   for (int i = 1; i <= 2; ++i )\n   {\n      genesis_state_type::initial_account_type dev_account(\n            test_prefix + std::to_string(i),\n            get_test_key(\"owner-\", i),\n            get_test_key(\"active-\", i),\n            false);\n\n      genesis_state.initial_accounts.push_back(dev_account);\n      // give her some coin\n\n   }\n   */\n\n   fc::json::save_to_file(genesis_state, genesis_out);\n   return genesis_path;\n}\n"
  },
  {
    "path": "tests/elasticsearch/main.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/app/api.hpp>\n#include <graphene/utilities/tempdir.hpp>\n#include <fc/crypto/digest.hpp>\n\n#include <graphene/utilities/elasticsearch.hpp>\n#include <graphene/elasticsearch/elasticsearch_plugin.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n#define BOOST_TEST_MODULE Elastic Search Database Tests\n#include <boost/test/included/unit_test.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::app;\n\nBOOST_FIXTURE_TEST_SUITE( elasticsearch_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE(elasticsearch_account_history) {\n   try {\n\n      CURL *curl; // curl handler\n      curl = curl_easy_init();\n\n      graphene::utilities::ES es;\n      es.curl = curl;\n      es.elasticsearch_url = \"http://localhost:9200/\";\n      es.index_prefix = \"bitshares-\";\n      //es.auth = \"elastic:changeme\";\n\n      // delete all first\n      auto delete_account_history = graphene::utilities::deleteAll(es);\n      fc::usleep(fc::milliseconds(1000)); // this is because index.refresh_interval, nothing to worry\n\n      if(delete_account_history) { // all records deleted\n\n         //account_id_type() do 3 ops\n         create_bitasset(\"USD\", account_id_type());\n         auto dan = create_account(\"dan\");\n         auto bob = create_account(\"bob\");\n\n         generate_block();\n         fc::usleep(fc::milliseconds(1000));\n\n         // for later use\n         //int asset_create_op_id = operation::tag<asset_create_operation>::value;\n         //int account_create_op_id = operation::tag<account_create_operation>::value;\n\n         string query = \"{ \\\"query\\\" : { \\\"bool\\\" : { \\\"must\\\" : [{\\\"match_all\\\": {}}] } } }\";\n         es.endpoint = es.index_prefix + \"*/data/_count\";\n         es.query = query;\n\n         auto res = graphene::utilities::simpleQuery(es);\n         variant j = fc::json::from_string(res);\n         auto total = j[\"count\"].as_string();\n         BOOST_CHECK_EQUAL(total, \"5\");\n\n         es.endpoint = es.index_prefix + \"*/data/_search\";\n         res = graphene::utilities::simpleQuery(es);\n         j = fc::json::from_string(res);\n         auto first_id = j[\"hits\"][\"hits\"][size_t(0)][\"_id\"].as_string();\n         BOOST_CHECK_EQUAL(first_id, \"2.9.1\"); // this should be 0? are they inserted in the right order?\n\n         generate_block();\n         auto willie = create_account(\"willie\");\n         generate_block();\n\n         fc::usleep(fc::milliseconds(1000)); // index.refresh_interval\n\n         es.endpoint = es.index_prefix + \"*/data/_count\";\n         res = graphene::utilities::simpleQuery(es);\n         j = fc::json::from_string(res);\n\n         total = j[\"count\"].as_string();\n         BOOST_CHECK_EQUAL(total, \"7\");\n\n         // do some transfers in 1 block\n         transfer(account_id_type()(db), bob, asset(100));\n         transfer(account_id_type()(db), bob, asset(200));\n         transfer(account_id_type()(db), bob, asset(300));\n\n         generate_block();\n         fc::usleep(fc::milliseconds(1000)); // index.refresh_interval\n\n         res = graphene::utilities::simpleQuery(es);\n         j = fc::json::from_string(res);\n\n         total = j[\"count\"].as_string();\n         BOOST_CHECK_EQUAL(total, \"13\");\n\n         // check the visitor data\n         auto block_date = db.head_block_time();\n         std::string index_name = graphene::utilities::generateIndexName(block_date, \"bitshares-\");\n\n         es.endpoint = index_name + \"/data/2.9.12\"; // we know last op is a transfer of amount 300\n         res = graphene::utilities::getEndPoint(es);\n         j = fc::json::from_string(res);\n         auto last_transfer_amount = j[\"_source\"][\"operation_history\"][\"op_object\"][\"amount_\"][\"amount\"].as_string();\n         BOOST_CHECK_EQUAL(last_transfer_amount, \"300\");\n      }\n   }\n   catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(elasticsearch_objects) {\n   try {\n\n      CURL *curl; // curl handler\n      curl = curl_easy_init();\n\n      graphene::utilities::ES es;\n      es.curl = curl;\n      es.elasticsearch_url = \"http://localhost:9200/\";\n      es.index_prefix = \"objects-\";\n      //es.auth = \"elastic:changeme\";\n\n      // delete all first\n      auto delete_objects = graphene::utilities::deleteAll(es);\n\n      generate_block();\n      fc::usleep(fc::milliseconds(1000));\n\n      if(delete_objects) { // all records deleted\n\n         // asset and bitasset\n         create_bitasset(\"USD\", account_id_type());\n         generate_block();\n         fc::usleep(fc::milliseconds(1000));\n\n         string query = \"{ \\\"query\\\" : { \\\"bool\\\" : { \\\"must\\\" : [{\\\"match_all\\\": {}}] } } }\";\n         es.endpoint = es.index_prefix + \"*/data/_count\";\n         es.query = query;\n\n         auto res = graphene::utilities::simpleQuery(es);\n         variant j = fc::json::from_string(res);\n         auto total = j[\"count\"].as_string();\n         BOOST_CHECK_EQUAL(total, \"2\");\n\n         es.endpoint = es.index_prefix + \"asset/data/_search\";\n         res = graphene::utilities::simpleQuery(es);\n         j = fc::json::from_string(res);\n         auto first_id = j[\"hits\"][\"hits\"][size_t(0)][\"_source\"][\"symbol\"].as_string();\n         BOOST_CHECK_EQUAL(first_id, \"USD\");\n\n         auto bitasset_data_id = j[\"hits\"][\"hits\"][size_t(0)][\"_source\"][\"bitasset_data_id\"].as_string();\n         es.endpoint = es.index_prefix + \"bitasset/data/_search\";\n         es.query = \"{ \\\"query\\\" : { \\\"bool\\\": { \\\"must\\\" : [{ \\\"term\\\": { \\\"object_id\\\": \\\"\"+bitasset_data_id+\"\\\"}}] } } }\";\n         res = graphene::utilities::simpleQuery(es);\n         j = fc::json::from_string(res);\n         auto bitasset_object_id = j[\"hits\"][\"hits\"][size_t(0)][\"_source\"][\"object_id\"].as_string();\n         BOOST_CHECK_EQUAL(bitasset_object_id, bitasset_data_id);\n      }\n   }\n   catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(elasticsearch_suite) {\n   try {\n\n      CURL *curl; // curl handler\n      curl = curl_easy_init();\n\n      graphene::utilities::ES es;\n      es.curl = curl;\n      es.elasticsearch_url = \"http://localhost:9200/\";\n      es.index_prefix = \"bitshares-\";\n      auto delete_account_history = graphene::utilities::deleteAll(es);\n      fc::usleep(fc::milliseconds(1000));\n      es.index_prefix = \"objects-\";\n      auto delete_objects = graphene::utilities::deleteAll(es);\n      fc::usleep(fc::milliseconds(1000));\n\n      if(delete_account_history && delete_objects) { // all records deleted\n\n\n      }\n   }\n   catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(elasticsearch_history_api) {\n   try {\n      CURL *curl; // curl handler\n      curl = curl_easy_init();\n\n      graphene::utilities::ES es;\n      es.curl = curl;\n      es.elasticsearch_url = \"http://localhost:9200/\";\n      es.index_prefix = \"bitshares-\";\n\n      auto delete_account_history = graphene::utilities::deleteAll(es);\n\n      generate_block();\n      fc::usleep(fc::milliseconds(1000));\n\n      if(delete_account_history) {\n\n         create_bitasset(\"USD\", account_id_type()); // create op 0\n         const account_object& dan = create_account(\"dan\"); // create op 1\n         create_bitasset(\"CNY\", dan.id); // create op 2\n         create_bitasset(\"BTC\", account_id_type()); // create op 3\n         create_bitasset(\"XMR\", dan.id); // create op 4\n         create_bitasset(\"EUR\", account_id_type()); // create op 5\n         create_bitasset(\"OIL\", dan.id); // create op 6\n\n         generate_block();\n         fc::usleep(fc::milliseconds(1000));\n\n         graphene::app::history_api hist_api(app);\n         app.enable_plugin(\"elasticsearch\");\n\n         // f(A, 0, 4, 9) = { 5, 3, 1, 0 }\n         auto histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(9));\n\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n         // f(A, 0, 4, 6) = { 5, 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(6));\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n         // f(A, 0, 4, 5) = { 5, 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(5));\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n         // f(A, 0, 4, 4) = { 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(4));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n         // f(A, 0, 4, 3) = { 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(3));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n         // f(A, 0, 4, 2) = { 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(2));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n         // f(A, 0, 4, 1) = { 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(1));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n         // f(A, 0, 4, 0) = { 5, 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type());\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n         // f(A, 1, 5, 9) = { 5, 3 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(9));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n         // f(A, 1, 5, 6) = { 5, 3 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(6));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n         // f(A, 1, 5, 5) = { 5, 3 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(5));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n         // f(A, 1, 5, 4) = { 3 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(4));\n         BOOST_CHECK_EQUAL(histories.size(), 1u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n\n         // f(A, 1, 5, 3) = { 3 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(3));\n         BOOST_CHECK_EQUAL(histories.size(), 1u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n\n         // f(A, 1, 5, 2) = { }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(2));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // f(A, 1, 5, 1) = { }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(1));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // f(A, 1, 5, 0) = { 5, 3 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(0));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n         // f(A, 0, 3, 9) = { 5, 3, 1 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(9));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n         // f(A, 0, 3, 6) = { 5, 3, 1 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(6));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n         // f(A, 0, 3, 5) = { 5, 3, 1 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(5));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n         // f(A, 0, 3, 4) = { 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(4));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n         // f(A, 0, 3, 3) = { 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(3));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n         // f(A, 0, 3, 2) = { 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(2));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n         // f(A, 0, 3, 1) = { 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(1));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n         // f(A, 0, 3, 0) = { 5, 3, 1 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type());\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n         // f(B, 0, 4, 9) = { 6, 4, 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(9));\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n\n         // f(B, 0, 4, 6) = { 6, 4, 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(6));\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n\n         // f(B, 0, 4, 5) = { 4, 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(5));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n         // f(B, 0, 4, 4) = { 4, 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(4));\n         BOOST_CHECK_EQUAL(histories.size(), 3u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n         // f(B, 0, 4, 3) = { 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(3));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n\n         // f(B, 0, 4, 2) = { 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(2));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n\n         // f(B, 0, 4, 1) = { 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(1));\n         BOOST_CHECK_EQUAL(histories.size(), 1u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n\n         // f(B, 0, 4, 0) = { 6, 4, 2, 1 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type());\n         BOOST_CHECK_EQUAL(histories.size(), 4u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n\n         // f(B, 2, 4, 9) = { 6, 4 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(9));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n\n         // f(B, 2, 4, 6) = { 6, 4 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(6));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n\n         // f(B, 2, 4, 5) = { 4 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(5));\n         BOOST_CHECK_EQUAL(histories.size(), 1u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n\n         // f(B, 2, 4, 4) = { 4 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(4));\n         BOOST_CHECK_EQUAL(histories.size(), 1u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n\n         // f(B, 2, 4, 3) = { }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(3));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // f(B, 2, 4, 2) = { }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(2));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // f(B, 2, 4, 1) = { }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(1));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // f(B, 2, 4, 0) = { 6, 4 }\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(0));\n         BOOST_CHECK_EQUAL(histories.size(), 2u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n\n         // 0 limits\n         histories = hist_api.get_account_history(\"dan\", operation_history_id_type(0), 0, operation_history_id_type(0));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(3), 0, operation_history_id_type(9));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // non existent account\n         histories = hist_api.get_account_history(\"1.2.18\", operation_history_id_type(0), 4, operation_history_id_type(0));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // create a new account C = alice { 7 }\n         create_account(\"alice\");\n\n         generate_block();\n         fc::usleep(fc::milliseconds(1000));\n\n         // f(C, 0, 4, 10) = { 7 }\n         histories = hist_api.get_account_history(\"alice\", operation_history_id_type(0), 4, operation_history_id_type(10));\n         BOOST_CHECK_EQUAL(histories.size(), 1u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u);\n\n         // f(C, 8, 4, 10) = { }\n         histories = hist_api.get_account_history(\"alice\", operation_history_id_type(8), 4, operation_history_id_type(10));\n         BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n         // f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 }\n         histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(0), 10, operation_history_id_type(0));\n         BOOST_CHECK_EQUAL(histories.size(), 5u);\n         BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u);\n         BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u);\n         BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);\n         BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n         BOOST_CHECK_EQUAL(histories[4].id.instance(), 0u);\n      }\n   }\n   catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/generate_empty_blocks/CMakeLists.txt",
    "content": "add_executable( generate_empty_blocks main.cpp )\nif( UNIX AND NOT APPLE )\n  set(rt_library rt )\nendif()\n\ntarget_link_libraries( generate_empty_blocks\n                       PRIVATE graphene_app graphene_chain graphene_egenesis_none fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )\n\ninstall( TARGETS\n   generate_empty_blocks\n\n   RUNTIME DESTINATION bin\n   LIBRARY DESTINATION lib\n   ARCHIVE DESTINATION lib\n)\n"
  },
  {
    "path": "tests/generate_empty_blocks/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <algorithm>\n#include <iomanip>\n#include <iostream>\n#include <iterator>\n\n#include <fc/io/fstream.hpp>\n#include <fc/io/json.hpp>\n#include <fc/io/stdio.hpp>\n\n#include <graphene/app/api.hpp>\n#include <graphene/egenesis/egenesis.hpp>\n#include <graphene/utilities/key_conversion.hpp>\n\n#include <boost/filesystem.hpp>\n\n#ifndef WIN32\n#include <csignal>\n#endif\n\nusing namespace graphene::app;\nusing namespace graphene::chain;\nusing namespace graphene::utilities;\nusing namespace std;\nnamespace bpo = boost::program_options;\n\n// hack:  import create_example_genesis() even though it's a way, way\n// specific internal detail\nnamespace graphene { namespace app { namespace detail {\ngenesis_state_type create_example_genesis();\n} } } // graphene::app::detail\n\nint main( int argc, char** argv )\n{\n   try\n   {\n      bpo::options_description cli_options(\"BitShares empty blocks\");\n      cli_options.add_options()\n            (\"help,h\", \"Print this help message and exit.\")\n            (\"data-dir\", bpo::value<boost::filesystem::path>()->default_value(\"empty_blocks_data_dir\"), \"Directory containing generator database\")\n            (\"genesis-json,g\", bpo::value<boost::filesystem::path>(), \"File to read genesis state from\")\n            (\"genesis-time,t\", bpo::value<uint32_t>()->default_value(0), \"Timestamp for genesis state (0=use value from file/example)\")\n            (\"num-blocks,n\", bpo::value<uint32_t>()->default_value(1000000), \"Number of blocks to generate\")\n            (\"miss-rate,r\", bpo::value<uint32_t>()->default_value(3), \"Percentage of blocks to miss\")\n            (\"verbose,v\", \"Enter verbose mode\")\n            ;\n\n      bpo::variables_map options;\n      try\n      {\n         boost::program_options::store( boost::program_options::parse_command_line(argc, argv, cli_options), options );\n      }\n      catch (const boost::program_options::error& e)\n      {\n         std::cerr << \"empty_blocks:  error parsing command line: \" << e.what() << \"\\n\";\n         return 1;\n      }\n\n      if( options.count(\"help\") )\n      {\n         std::cout << cli_options << \"\\n\";\n         return 0;\n      }\n\n      fc::path data_dir;\n      if( options.count(\"data-dir\") )\n      {\n         data_dir = options[\"data-dir\"].as<boost::filesystem::path>();\n         if( data_dir.is_relative() )\n            data_dir = fc::current_path() / data_dir;\n      }\n\n      genesis_state_type genesis;\n      if( options.count(\"genesis-json\") )\n      {\n         fc::path genesis_json_filename = options[\"genesis-json\"].as<boost::filesystem::path>();\n         std::cerr << \"embed_genesis:  Reading genesis from file \" << genesis_json_filename.preferred_string() << \"\\n\";\n         std::string genesis_json;\n         read_file_contents( genesis_json_filename, genesis_json );\n         genesis = fc::json::from_string( genesis_json ).as< genesis_state_type >(20);\n      }\n      else\n         genesis = graphene::app::detail::create_example_genesis();\n      uint32_t timestamp = options[\"genesis-time\"].as<uint32_t>();\n      if( timestamp != 0 )\n      {\n         genesis.initial_timestamp = fc::time_point_sec( timestamp );\n         std::cerr << \"embed_genesis:  Genesis timestamp is \" << genesis.initial_timestamp.sec_since_epoch() << \" (from CLI)\\n\";\n      }\n      else\n         std::cerr << \"embed_genesis:  Genesis timestamp is \" << genesis.initial_timestamp.sec_since_epoch() << \" (from state)\\n\";\n      bool verbose = (options.count(\"verbose\") != 0);\n\n      uint32_t num_blocks = options[\"num-blocks\"].as<uint32_t>();\n      uint32_t miss_rate = options[\"miss-rate\"].as<uint32_t>();\n\n      fc::ecc::private_key nathan_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"nathan\")));\n\n      database db;\n      fc::path db_path = data_dir / \"db\";\n      db.open(db_path, [&]() { return genesis; }, \"TEST\" );\n\n      uint32_t slot = 1;\n      uint32_t missed = 0;\n\n      for( uint32_t i = 1; i < num_blocks; ++i )\n      {\n         signed_block b = db.generate_block(db.get_slot_time(slot), db.get_scheduled_witness(slot), nathan_priv_key, database::skip_nothing);\n         FC_ASSERT( db.head_block_id() == b.id() );\n         fc::sha256 h = b.digest();\n         uint64_t rand = h._hash[0].value();\n         slot = 1;\n         while(true)\n         {\n            if( (rand % 100) < miss_rate )\n            {\n               slot++;\n               rand = (rand/100) ^ h._hash[slot&3].value();\n               missed++;\n            }\n            else\n               break;\n         }\n         \n         witness_id_type prev_witness = b.witness;\n         witness_id_type cur_witness = db.get_scheduled_witness(1);\n         if( verbose )\n         {\n            wdump( (prev_witness)(cur_witness) );\n         }\n         else if( (i%10000) == 0 )\n         {\n            std::cerr << \"\\rblock #\" << i << \"   missed \" << missed;\n         }\n         if( slot == 1 )  // can possibly get consecutive production if block missed\n         {\n            FC_ASSERT( cur_witness != prev_witness );\n         }\n      }\n      std::cerr << \"\\n\";\n      db.close();\n   }\n   catch ( const fc::exception& e )\n   {\n      std::cout << e.to_detail_string() << \"\\n\";\n      return 1;\n   }\n   return 0;\n}\n"
  },
  {
    "path": "tests/intense/api_stress.py",
    "content": "#!/usr/bin/env python3\n\nimport json\nimport os\nimport random\nimport signal\nimport struct\nimport sys\nimport time\n\ntry:\n    import asyncio\nexcept ImportError:\n    print(\"asyncio module not found (try pip install asyncio, or upgrade to Python 3.4 or later)\")\n    sys.exit(1)\n\ntry:\n    import websockets\nexcept ImportError:\n    print(\"websockets module not found (try pip install websockets)\")\n    sys.exit(1)\n\n@asyncio.coroutine\ndef mainloop():\n    ws = yield from websockets.connect('ws://localhost:8090/')\n    entropy = struct.unpack(\"<Q\", os.urandom(8))[0]\n    rand = random.Random(entropy)\n    my_account_id = rand.randrange(0, 90000)\n\n    gpo_get = {\"id\":1, \"method\":\"call\", \"params\":[0, \"get_objects\", [\"2.0.0\"]]}\n    dgpo_sub = {\"id\":2, \"method\":\"call\", \"params\":[0, \"subscribe_to_objects\", [111, [\"2.1.0\"]]]}\n    acct_sub = {\"id\":3, \"method\":\"call\", \"params\":[0, \"get_full_accounts\", [222, [\"1.2.\"+str(my_account_id)], True]]}\n    yield from ws.send(json.dumps(gpo_get))\n    yield from ws.send(json.dumps(dgpo_sub))\n    yield from ws.send(json.dumps(acct_sub))\n\n    next_call_id = 4\n\n    def peek_random_account():\n        nonlocal next_call_id\n        asyncio.get_event_loop().call_later(rand.uniform(0, 3), peek_random_account)\n        peek_account_id = rand.randrange(0, 90000)\n        acct_peek = {\"id\" : next_call_id, \"method\" : \"call\", \"params\":\n           [0, \"get_objects\", [[\"1.2.\"+str(peek_account_id)], True]]}\n        next_call_id += 1\n        yield from ws.send(json.dumps(acct_peek))\n        return\n\n    yield from peek_random_account()\n\n    while True:\n        result = yield from ws.recv()\n        #print(result)\n        if result is None:\n           break\n    yield from ws.close()\n\nchild_procs = []\n\n# stress test with 200 instances\nwhile len(child_procs) < 200:\n    pid = os.fork()\n    if pid == 0:\n        asyncio.get_event_loop().run_until_complete(mainloop())\n        break\n    else:\n        child_procs.append(pid)\n\ntime.sleep(60)\nfor pid in child_procs:\n    os.kill(pid, signal.SIGTERM)\n\ntime.sleep(2)\nfor pid in child_procs:\nos.kill(pid, signal.SIGKILL)\n"
  },
  {
    "path": "tests/performance/README.md",
    "content": "HOW TO\n======\n\nThis small test suite serves to demonstrate two key points about the performance\nof our current implementation. The subject was talked about in detail at\nBitFest Amsterdam, Sep 22, 2018.\n\nThe original description of the 100,000 transactions per second test can be\nfound at https://bitshares.org/blog/2015/06/08/measuring-performance/ .\n\nPrepare\n-------\n\n1. Follow the build instructions in the top-level README file.\n2. Instead of running ``make`` you can run ``make performance_test`` to build\n   only the test suite.\n3. Run ``tests/performance_test -t performance_tests/<testcase>``\n\n\n100k TX/s\n---------\n\n``tests/performance_test -t performance_tests/one_hundred_k_benchmark``\n\nThis test will create 200,000 accounts, make two transfers from each account,\nthen create an asset and issue tokens to each account, for a total of one\nmillion operations.\n\nDifferent operation types have different execution times, but on fairly modern\noff-the-shelf hardware an average of 100,000 transactions per second should be\nachieved.\n\nSignature verification\n----------------------\n\n``tests/performance_test -t performance_tests/sigcheck_benchmark``\n\nThis suite pre-creates 100,000 signatures and then measures how long it takes\nto verify them. Results vary depending on CPU type and clockspeed, but should be\nsomewhere between 5,000 and 20,000 per second.\n"
  },
  {
    "path": "tests/performance/market_fee_sharing_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Bitshares Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <boost/test/unit_test.hpp>\n\n#include <chrono>\n#include <graphene/chain/hardfork.hpp>\n#include <fc/time.hpp>\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\n\nBOOST_FIXTURE_TEST_CASE(mfs_performance_test, database_fixture)\n{\n   try\n   {\n      ACTORS((issuer));\n\n      const unsigned int accounts = 3000;\n      const unsigned int iterations = 20;\n\n      std::vector<account_object> registrators;\n      for (unsigned int i = 0; i < accounts; ++i)\n      {\n         auto account = create_account(\"registrar\" + std::to_string(i));\n         transfer(committee_account, account.get_id(), asset(1000000));\n         upgrade_to_lifetime_member(account);\n\n         registrators.push_back(std::move(account));\n      }\n\n      generate_block();\n\n      additional_asset_options_t options;\n      options.value.reward_percent = 2 * GRAPHENE_1_PERCENT;\n\n      const auto usd = create_user_issued_asset(\n                  \"USD\",\n                  issuer,\n                  charge_market_fee,\n                  price(asset(1, asset_id_type(1)), asset(1)),\n                  1,\n                  20 * GRAPHENE_1_PERCENT,\n                  options);\n\n      issue_uia(issuer, usd.amount(iterations * accounts * 2000));\n\n      std::vector<account_object> traders;\n      for (unsigned int i = 0; i < accounts; ++i)\n      {\n         std::string name = \"account\" + std::to_string(i);\n         auto account = create_account(name, registrators[i], registrators[i], GRAPHENE_1_PERCENT);\n         transfer(committee_account, account.get_id(), asset(1000000));\n         transfer(issuer, account, usd.amount(iterations * 2000));\n\n         traders.push_back(std::move(account));\n      }\n\n      using namespace std::chrono;\n\n      const auto start = high_resolution_clock::now();\n\n      for (unsigned int i = 0; i < iterations; ++i)\n      {\n         for (unsigned int j = 0; j < accounts; ++j)\n         {\n            create_sell_order(traders[j], usd.amount(2000), asset(1));\n            create_sell_order(traders[accounts - j - 1], asset(1), usd.amount(1000));\n         }\n      }\n\n      const auto end = high_resolution_clock::now();\n\n      const auto elapsed = duration_cast<milliseconds>(end - start);\n      wlog(\"Elapsed: ${c} ms\", (\"c\", elapsed.count()));\n\n      for (unsigned int i = 0; i < accounts; ++i)\n      {\n         const auto reward = get_market_fee_reward(registrators[i], usd);\n         BOOST_CHECK_GT(reward, 0);\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n"
  },
  {
    "path": "tests/performance/performance_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/included/unit_test.hpp>\n\nboost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {\n   std::srand(time(NULL));\n   std::cout << \"Random number generator seeded to \" << time(NULL) << std::endl;\n   return nullptr;\n}\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <graphene/db/simple_index.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n#include <cstdlib>\n#include <iostream>\n\nusing namespace graphene::chain;\n\nBOOST_FIXTURE_TEST_SUITE( performance_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( sigcheck_benchmark )\n{\n   fc::ecc::private_key nathan_key = fc::ecc::private_key::generate();\n   auto digest = fc::sha256::hash(\"hello\");\n   auto sig = nathan_key.sign_compact( digest );\n   auto start = fc::time_point::now();\n   const uint64_t cycles = 100000;\n   for( uint32_t i = 0; i < cycles; ++i )\n      fc::ecc::public_key( sig, digest );\n   auto end = fc::time_point::now();\n   auto elapsed = end-start;\n   wlog( \"Benchmark: verify ${sps} signatures/s\", (\"sps\",(cycles*1000000)/elapsed.count()) );\n}\n\n// See https://bitshares.org/blog/2015/06/08/measuring-performance/\n// (note this is not the original test mentioned in the above post, but was\n//  recreated later according to the description)\nBOOST_AUTO_TEST_CASE( one_hundred_k_benchmark )\n{ try {\n   ACTORS( (alice) );\n   fund( alice, asset(10000000) );\n   db._undo_db.disable(); // Blog post mentions replay, this implies no undo\n\n   const fc::ecc::private_key nathan_key = fc::ecc::private_key::generate();\n   const fc::ecc::public_key  nathan_pub = nathan_key.get_public_key();;\n   const auto& committee_account = account_id_type()(db);\n\n   const uint64_t cycles = 200000;\n   uint64_t total_time = 0;\n   uint64_t total_count = 0;\n   std::vector<account_id_type> accounts;\n   accounts.reserve( cycles+1 );\n   std::vector<asset_id_type> assets;\n   assets.reserve( cycles );\n\n   std::vector<signed_transaction> transactions;\n   transactions.reserve( cycles );\n\n   {\n      account_create_operation aco;\n      aco.name = \"a1\";\n      aco.registrar = committee_account.id;\n      aco.owner = authority( 1, public_key_type(nathan_pub), 1 );\n      aco.active = authority( 1, public_key_type(nathan_pub), 1 );\n      aco.options.memo_key = nathan_pub;\n      aco.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n      aco.options.num_committee = 0;\n      aco.options.num_witness = 0;\n      aco.fee = db.current_fee_schedule().calculate_fee( aco );\n      trx.clear();\n      test::set_expiration( db, trx );\n      for( uint32_t i = 0; i < cycles; ++i )\n      {\n         aco.name = \"a\" + fc::to_string(i);\n         trx.operations.push_back( aco );\n         transactions.push_back( trx );\n         trx.operations.clear();\n         ++total_count;\n      }\n\n      auto start = fc::time_point::now();\n      for( uint32_t i = 0; i < cycles; ++i )\n      {\n         auto result = db.apply_transaction( transactions[i], ~0 );\n         accounts[i] = result.operation_results[0].get<object_id_type>();\n      }\n      auto end = fc::time_point::now();\n      auto elapsed = end - start;\n      total_time += elapsed.count();\n      wlog( \"Create ${aps} accounts/s over ${total}ms\",\n            (\"aps\",(cycles*1000000)/elapsed.count())(\"total\",elapsed.count()/1000) );\n   }\n\n   {\n      accounts[cycles] = accounts[0];\n      transfer_operation to1;\n      to1.from = committee_account.id;\n      to1.amount = asset( 1000000 );\n      to1.fee = asset( 10 );\n      transfer_operation to2;\n      to2.amount = asset( 100 );\n      to2.fee = asset( 10 );\n      for( uint32_t i = 0; i < cycles; ++i )\n      {\n         to1.to = accounts[i];\n         to2.from = accounts[i];\n         to2.to = accounts[i+1];\n         trx.operations.push_back( to1 );\n         ++total_count;\n         trx.operations.push_back( to2 );\n         ++total_count;\n         transactions[i] = trx;\n         trx.operations.clear();\n      }\n\n      auto start = fc::time_point::now();\n      for( uint32_t i = 0; i < cycles; ++i )\n         db.apply_transaction( transactions[i], ~0 );\n      auto end = fc::time_point::now();\n      auto elapsed = end - start;\n      total_time += elapsed.count();\n      wlog( \"${aps} transfers/s over ${total}ms\",\n            (\"aps\",(2*cycles*1000000)/elapsed.count())(\"total\",elapsed.count()/1000) );\n      trx.clear();\n   }\n\n   {\n      asset_create_operation aco;\n      aco.fee = asset( 100000 );\n      aco.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type(1) );\n      for( uint32_t i = 0; i < cycles; ++i )\n      {\n         aco.issuer = accounts[i];\n         aco.symbol = \"ASSET\" + fc::to_string( i );\n         trx.operations.push_back( aco );\n         ++total_count;\n         transactions[i] = trx;\n         trx.operations.clear();\n      }\n\n      auto start = fc::time_point::now();\n      for( uint32_t i = 0; i < cycles; ++i )\n      {\n         auto result = db.apply_transaction( transactions[i], ~0 );\n         assets[i] = result.operation_results[0].get<object_id_type>();\n      }\n      auto end = fc::time_point::now();\n      auto elapsed = end - start;\n      total_time += elapsed.count();\n      wlog( \"${aps} asset create/s over ${total}ms\",\n            (\"aps\",(cycles*1000000)/elapsed.count())(\"total\",elapsed.count()/1000) );\n      trx.clear();\n   }\n\n   {\n      asset_issue_operation aio;\n      aio.fee = asset( 10 );\n      for( uint32_t i = 0; i < cycles; ++i )\n      {\n         aio.issuer = accounts[i];\n         aio.issue_to_account = accounts[i+1];\n         aio.asset_to_issue = asset( 10, assets[i] );\n         trx.operations.push_back( aio );\n         ++total_count;\n         transactions[i] = trx;\n         trx.operations.clear();\n      }\n\n      auto start = fc::time_point::now();\n      for( uint32_t i = 0; i < cycles; ++i )\n         db.apply_transaction( transactions[i], ~0 );\n      auto end = fc::time_point::now();\n      auto elapsed = end - start;\n      total_time += elapsed.count();\n      wlog( \"${aps} issuances/s over ${total}ms\",\n            (\"aps\",(cycles*1000000)/elapsed.count())(\"total\",elapsed.count()/1000) );\n      trx.clear();\n   }\n\n   wlog( \"${total} operations in ${total_time}ms => ${avg} ops/s on average\",\n         (\"total\",total_count)(\"total_time\",total_time/1000)\n         (\"avg\",(total_count*1000000)/total_time) );\n\n   db._undo_db.enable();\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/api_limit_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/database_api.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE(api_limit_tests, database_fixture)\n\nBOOST_AUTO_TEST_CASE( api_limit_get_key_references ){\n   try{\n   const int num_keys = 210;\n   const int num_keys1 = 2;\n   vector< private_key_type > numbered_private_keys;\n   vector< public_key_type >  numbered_key_id;\n   numbered_private_keys.reserve( num_keys );\n\n   graphene::app::database_api db_api1( db, &( app.get_options() ));\n   BOOST_CHECK_THROW( db_api1.get_key_references(numbered_key_id), fc::exception );\n\n   graphene::app::application_options opt = app.get_options();\n   opt.has_api_helper_indexes_plugin = true;\n   graphene::app::database_api db_api( db, &opt );\n\n   for( int i=0; i<num_keys1; i++ )\n   {\n      private_key_type privkey = generate_private_key(std::string(\"key_\") + std::to_string(i));\n      public_key_type pubkey = privkey.get_public_key();\n      numbered_private_keys.push_back( privkey );\n      numbered_key_id.push_back( pubkey );\n   }\n   vector< flat_set<account_id_type> > final_result=db_api.get_key_references(numbered_key_id);\n   BOOST_REQUIRE_EQUAL( final_result.size(), 2u );\n   numbered_private_keys.reserve( num_keys );\n   for( int i=num_keys1; i<num_keys; i++ )\n   {\n       private_key_type privkey = generate_private_key(std::string(\"key_\") + std::to_string(i));\n       public_key_type pubkey = privkey.get_public_key();\n       numbered_private_keys.push_back( privkey );\n       numbered_key_id.push_back( pubkey );\n   }\n   GRAPHENE_CHECK_THROW(db_api.get_key_references(numbered_key_id), fc::exception);\n   }catch (fc::exception& e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_get_full_accounts ) {\n\n   try {\n      ACTOR(alice);\n\n      graphene::app::application_options opt = app.get_options();\n      opt.has_api_helper_indexes_plugin = true;\n      graphene::app::database_api db_api( db, &opt );\n\n      vector<string> accounts;\n\n      for( size_t i = 0; i < 50; ++i )\n      {\n         string account_name = \"testaccount\" + fc::to_string(i);\n         create_account( account_name );\n         accounts.push_back( account_name );\n      }\n      accounts.push_back( \"alice\" );\n\n      transfer_operation op;\n      op.from = alice_id;\n      op.amount = asset(1);\n      for( size_t i = 0; i < 501; ++i )\n      {\n         propose( op, alice_id );\n      }\n\n      // Too many accounts\n      GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception);\n\n      accounts.erase(accounts.begin());\n      auto full_accounts = db_api.get_full_accounts(accounts, false);\n      BOOST_CHECK(full_accounts.size() == 50);\n\n      // The default max list size is 500\n      BOOST_REQUIRE( full_accounts.find(\"alice\") != full_accounts.end() );\n      BOOST_CHECK_EQUAL( full_accounts[\"alice\"].proposals.size(), 500u );\n      BOOST_CHECK( full_accounts[\"alice\"].more_data_available.proposals );\n      BOOST_REQUIRE( full_accounts.find(\"testaccount9\") != full_accounts.end() );\n      BOOST_CHECK_EQUAL( full_accounts[\"testaccount9\"].proposals.size(), 0 );\n      BOOST_CHECK( !full_accounts[\"testaccount9\"].more_data_available.proposals );\n\n      // not an account\n      accounts.erase(accounts.begin());\n      accounts.push_back(\"nosuchaccount\");\n\n      // non existing accounts will be ignored in the results\n      full_accounts = db_api.get_full_accounts(accounts, false);\n      BOOST_CHECK(full_accounts.size() == 49);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_get_limit_orders ){\n   try{\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   //account_id_type() do 3 ops\n   create_bitasset(\"USD\", account_id_type());\n   create_account(\"dan\");\n   create_account(\"bob\");\n   asset_id_type bit_jmj_id = create_bitasset(\"JMJBIT\").id;\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n   GRAPHENE_CHECK_THROW(db_api.get_limit_orders(std::string(static_cast<object_id_type>(asset_id_type())),\n      std::string(static_cast<object_id_type>(bit_jmj_id)), 370), fc::exception);\n   vector<limit_order_object>  limit_orders =db_api.get_limit_orders(std::string(\n      static_cast<object_id_type>(asset_id_type())),\n      std::string(static_cast<object_id_type>(bit_jmj_id)), 340);\n   BOOST_REQUIRE_EQUAL( limit_orders.size(), 0u);\n\n   }catch (fc::exception& e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_get_limit_orders_by_account ){\n   try{\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   const auto& test = create_user_issued_asset(\"TESTASSET\");\n   create_sell_order( account_id_type(), asset(1,asset_id_type()), test.amount(1) );\n   GRAPHENE_CHECK_THROW(db_api.get_limit_orders_by_account(\n      std::string(static_cast<object_id_type>(account_id_type())), 160), fc::exception);\n   vector<limit_order_object>  limit_orders =db_api.get_limit_orders_by_account(\n      std::string(static_cast<object_id_type>(account_id_type())), 145);\n   BOOST_REQUIRE_EQUAL( limit_orders.size(), 1u);\n\n   }catch (fc::exception& e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_get_call_orders ){\n   try{\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   //account_id_type() do 3 ops\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   account_id_type nathan_id = create_account(\"nathan\", nathan_private_key.get_public_key()).id;\n   transfer(account_id_type(), nathan_id, asset(100));\n   asset_id_type bitusd_id = create_bitasset(\n         \"USDBIT\", nathan_id, 100, disable_force_settle).id;\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n   BOOST_CHECK( bitusd_id(db).is_market_issued() );\n   GRAPHENE_CHECK_THROW(db_api.get_call_orders(std::string(static_cast<object_id_type>(bitusd_id)),\n         370), fc::exception);\n   vector< call_order_object>  call_order =db_api.get_call_orders(std::string(\n         static_cast<object_id_type>(bitusd_id)), 340);\n   BOOST_REQUIRE_EQUAL( call_order.size(), 0u);\n   }catch (fc::exception& e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\nBOOST_AUTO_TEST_CASE( api_limit_get_settle_orders ){\n   try{\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   //account_id_type() do 3 ops\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   account_id_type nathan_id = create_account(\"nathan\", nathan_private_key.get_public_key()).id;\n   transfer(account_id_type(), nathan_id, asset(100));\n   asset_id_type bitusd_id = create_bitasset(\n         \"USDBIT\", nathan_id, 100, disable_force_settle).id;\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n   GRAPHENE_CHECK_THROW(db_api.get_settle_orders(\n         std::string(static_cast<object_id_type>(bitusd_id)), 370), fc::exception);\n   vector<force_settlement_object> result =db_api.get_settle_orders(\n         std::string(static_cast<object_id_type>(bitusd_id)), 340);\n   BOOST_REQUIRE_EQUAL( result.size(), 0u);\n   }catch (fc::exception& e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\nBOOST_AUTO_TEST_CASE( api_limit_get_order_book ){\n   try{\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   auto dan_private_key = generate_private_key(\"dan\");\n   account_id_type nathan_id = create_account(\"nathan\", nathan_private_key.get_public_key()).id;\n   account_id_type dan_id = create_account(\"dan\", dan_private_key.get_public_key()).id;\n   transfer(account_id_type(), nathan_id, asset(100));\n   transfer(account_id_type(), dan_id, asset(100));\n   asset_id_type bitusd_id = create_bitasset(\n         \"USDBIT\", nathan_id, 100, disable_force_settle).id;\n   asset_id_type bitdan_id = create_bitasset(\n         \"DANBIT\", dan_id, 100, disable_force_settle).id;\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n   GRAPHENE_CHECK_THROW(db_api.get_order_book(std::string(static_cast<object_id_type>(bitusd_id)),\n         std::string(static_cast<object_id_type>(bitdan_id)),89), fc::exception);\n   graphene::app::order_book result =db_api.get_order_book(std::string(\n         static_cast<object_id_type>(bitusd_id)), std::string(static_cast<object_id_type>(bitdan_id)),78);\n   BOOST_REQUIRE_EQUAL( result.bids.size(), 0u);\n   }catch (fc::exception& e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_lookup_accounts ) {\n   try{\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      ACTOR(bob);\n      GRAPHENE_CHECK_THROW(db_api.lookup_accounts(\"bob\",220), fc::exception);\n      map<string,account_id_type> result =db_api.lookup_accounts(\"bob\",190);\n      BOOST_REQUIRE_EQUAL( result.size(), 17u);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_lookup_witness_accounts ) {\n   try{\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      ACTORS((bob)) ;\n      GRAPHENE_CHECK_THROW(db_api.lookup_witness_accounts(\"bob\",220), fc::exception);\n      map<string, witness_id_type> result =db_api.lookup_witness_accounts(\"bob\",190);\n      BOOST_REQUIRE_EQUAL( result.size(), 10u);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE( api_limit_get_full_accounts2 ) {\n\n   try {\n      ACTOR(alice);\n\n      graphene::app::application_options opt = app.get_options();\n      opt.has_api_helper_indexes_plugin = true;\n      graphene::app::database_api db_api( db, &opt );\n\n      vector<string> accounts;\n      for (int i=0; i<200; i++) {\n         std::string acct_name = \"mytempacct\" + std::to_string(i);\n         const account_object& account_name=create_account(acct_name);\n         accounts.push_back(account_name.name);\n      }\n      accounts.push_back( \"alice\" );\n\n      transfer_operation op;\n      op.from = alice_id;\n      op.amount = asset(1);\n      for( size_t i = 0; i < 501; ++i )\n      {\n         propose( op, alice_id );\n      }\n\n      GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception);\n      accounts.erase(accounts.begin());\n      auto full_accounts = db_api.get_full_accounts(accounts, false);\n      BOOST_REQUIRE_EQUAL(full_accounts.size(), 200u);\n\n      // The updated max list size is 120\n      BOOST_REQUIRE( full_accounts.find(\"alice\") != full_accounts.end() );\n      BOOST_CHECK_EQUAL( full_accounts[\"alice\"].proposals.size(), 120u );\n      BOOST_CHECK( full_accounts[\"alice\"].more_data_available.proposals );\n      BOOST_REQUIRE( full_accounts.find(\"mytempacct9\") != full_accounts.end() );\n      BOOST_CHECK_EQUAL( full_accounts[\"mytempacct9\"].proposals.size(), 0 );\n      BOOST_CHECK( !full_accounts[\"mytempacct9\"].more_data_available.proposals );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_recipient){\n   try{\n      graphene::app::database_api db_api( db, &app.get_options());\n      ACTORS((bob)) ;\n      withdraw_permission_id_type withdraw_permission;\n      GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_recipient(\n         \"bob\",withdraw_permission, 251), fc::exception);\n      vector<withdraw_permission_object> result =db_api.get_withdraw_permissions_by_recipient(\n         \"bob\",withdraw_permission,250);\n      BOOST_REQUIRE_EQUAL( result.size(), 0u);\n   }catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_giver){\n   try{\n      graphene::app::database_api db_api( db, &app.get_options());\n      ACTORS((bob)) ;\n      withdraw_permission_id_type withdraw_permission;\n      GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_giver(\n         \"bob\",withdraw_permission, 251), fc::exception);\n      vector<withdraw_permission_object> result =db_api.get_withdraw_permissions_by_giver(\n         \"bob\",withdraw_permission,250);\n      BOOST_REQUIRE_EQUAL( result.size(), 0u);\n   }catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_trade_history_by_sequence){\n   try{\n      app.enable_plugin(\"market_history\");\n      graphene::app::application_options opt=app.get_options();\n      opt.has_market_history_plugin = true;\n      graphene::app::database_api db_api( db, &opt);\n      const auto& bitusd = create_bitasset(\"USDBIT\");\n      asset_id_type asset_1, asset_2;\n      asset_1 = bitusd.id;\n      asset_2 = asset_id_type();\n      GRAPHENE_CHECK_THROW(db_api.get_trade_history_by_sequence(\n         std::string( static_cast<object_id_type>(asset_1)),\n         std::string( static_cast<object_id_type>(asset_2)),\n         0,fc::time_point_sec(), 251), fc::exception);\n      vector<graphene::app::market_trade> result =db_api.get_trade_history_by_sequence(\n         std::string( static_cast<object_id_type>(asset_1)),\n         std::string( static_cast<object_id_type>(asset_2)),\n         0,fc::time_point_sec(),250);\n      BOOST_REQUIRE_EQUAL( result.size(), 0u);\n   }catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(api_limit_get_trade_history){\n   try{\n      app.enable_plugin(\"market_history\");\n      graphene::app::application_options opt=app.get_options();\n      opt.has_market_history_plugin = true;\n      graphene::app::database_api db_api( db, &opt);\n      const auto& bitusd = create_bitasset(\"USDBIT\");\n      asset_id_type asset_1, asset_2;\n      asset_1 = bitusd.id;\n      asset_2 = asset_id_type();\n      GRAPHENE_CHECK_THROW(db_api.get_trade_history(\n                              std::string( static_cast<object_id_type>(asset_1)),\n                              std::string( static_cast<object_id_type>(asset_2)),\n                              fc::time_point_sec(),fc::time_point_sec(),\n                              251), fc::exception);\n      vector<graphene::app::market_trade> result =db_api.get_trade_history(\n         std::string( static_cast<object_id_type>(asset_1)),\n         std::string( static_cast<object_id_type>(asset_2)),\n         fc::time_point_sec(),fc::time_point_sec(),250);\n      BOOST_REQUIRE_EQUAL( result.size(), 0u);\n   }catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_top_markets){\n   try{\n      app.enable_plugin(\"market_history\");\n      graphene::app::application_options opt=app.get_options();\n      opt.has_market_history_plugin = true;\n      graphene::app::database_api db_api( db, &opt);\n      const auto& bitusd = create_bitasset(\"USDBIT\");\n      asset_id_type asset_1, asset_2;\n      asset_1 = bitusd.id;\n      asset_2 = asset_id_type();\n      GRAPHENE_CHECK_THROW(db_api.get_top_markets(251), fc::exception);\n      vector<graphene::app::market_ticker> result =db_api.get_top_markets(250);\n      BOOST_REQUIRE_EQUAL( result.size(), 0u);\n   }catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_collateral_bids) {\n   try {\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n\n      int64_t init_balance = 10000;\n      ///account_id_type borrower, borrower2, feedproducer;\n      asset_id_type swan, back;\n      ACTORS((borrower) (borrower2) (feedproducer)) ;\n      const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n      swan = bitusd.id;\n      back = asset_id_type();\n      update_feed_producers(swan(db), {feedproducer_id});\n      transfer(committee_account, borrower_id, asset(init_balance));\n      transfer(committee_account, borrower2_id, asset(init_balance));\n\n      generate_blocks( HARDFORK_CORE_216_TIME );\n      generate_block();\n\n      price_feed feed;\n      feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      feed.settlement_price = swan(db).amount(1) / back(db).amount(1);\n      publish_feed(swan(db), feedproducer_id(db), feed);\n      // start out with 2:1 collateral\n      borrow(borrower_id(db), swan(db).amount(10), back(db).amount(2*10));\n      borrow(borrower2_id(db), swan(db).amount(10), back(db).amount(4*10));\n      //feed 1: 2\n      feed.settlement_price = swan(db).amount(1) / back(db).amount(2);\n      publish_feed(swan(db), feedproducer_id(db), feed);\n\n      // this sell order is designed to trigger a black swan\n\n      create_sell_order( borrower2_id(db), swan(db).amount(1), back(db).amount(3) );\n      BOOST_CHECK( swan(db).bitasset_data(db).has_settlement() );\n      //making 3 collateral bids\n      for (int i=0; i<3; i++) {\n\n         std::string acct_name = \"mytempacct\" + std::to_string(i);\n         account_id_type account_id=create_account(acct_name).id;\n         transfer(committee_account, account_id, asset(init_balance));\n         bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1));\n      }\n      auto swan_symbol = swan(db).symbol;\n\n\n      //validating normal case; total_bids =3 ; result_bids=3\n      vector<collateral_bid_object> result_bids = db_api.get_collateral_bids(swan_symbol, 250, 0);\n      BOOST_CHECK_EQUAL( 3u, result_bids.size() );\n\n      //verify skip /// inefficient code test\n      //skip < total_bids; skip=1; total_bids =3 ; result_bids=2\n      result_bids = db_api.get_collateral_bids(swan_symbol, 250, 1);\n      BOOST_CHECK_EQUAL( 2u, result_bids.size() );\n      //skip= total_bids; skip=3; total_bids =3 ; result_bids=0\n      result_bids = db_api.get_collateral_bids(swan_symbol, 250, 3);\n      BOOST_CHECK_EQUAL( 0u, result_bids.size() );\n      //skip> total_bids; skip=4; total_bids =3 ; result_bids=0\n      result_bids = db_api.get_collateral_bids(swan_symbol, 250, 4);\n      BOOST_CHECK_EQUAL( 0u, result_bids.size() );\n\n      //verify limit // inefficient code test\n      //limit= api_limit\n      for (int i=3; i<255; i++) {\n         std::string acct_name = \"mytempacct\" + std::to_string(i);\n         account_id_type account_id=create_account(acct_name).id;\n         transfer(committee_account, account_id, asset(init_balance));\n         bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1));\n      }\n      result_bids=db_api.get_collateral_bids(swan_symbol, 250, 0);\n      BOOST_CHECK_EQUAL( 250u, result_bids.size() );\n      //limit> api_limit throw error\n      GRAPHENE_CHECK_THROW(db_api.get_collateral_bids(swan_symbol, 253, 3), fc::exception);\n\n   }\n   catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_account_limit_orders) {\n   try {\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      ACTORS((seller));\n      const auto &bitcny = create_bitasset(\"CNY\");\n      const auto &core = asset_id_type()(db);\n\n      int64_t init_balance(10000000);\n      transfer(committee_account, seller_id, asset(init_balance));\n\n      /// Create  versatile orders\n      for (size_t i = 0; i < 250; ++i) {\n         BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250+i)));\n      }\n\n\n      std::vector<limit_order_object> results=db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\",250);\n      BOOST_REQUIRE_EQUAL( results.size(), 250u);\n      GRAPHENE_CHECK_THROW( db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\",251), fc::exception);\n\n   }\n   catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE( api_limit_lookup_vote_ids ) {\n   try{\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      ACTORS( (connie)(whitney)(wolverine) );\n      fund(connie);\n      upgrade_to_lifetime_member(connie);\n      fund(whitney);\n      upgrade_to_lifetime_member(whitney);\n      fund(wolverine);\n      upgrade_to_lifetime_member(wolverine);\n      const auto& committee = create_committee_member( connie );\n      const auto& witness = create_witness( whitney );\n      const auto& worker = create_worker( wolverine_id );\n      std::vector<vote_id_type> votes;\n      votes.push_back( committee.vote_id );\n      votes.push_back( witness.vote_id );\n      const auto results = db_api.lookup_vote_ids( votes );\n      BOOST_REQUIRE_EQUAL( results.size(), 2u);\n      votes.push_back( worker.vote_for );\n      GRAPHENE_CHECK_THROW(db_api.lookup_vote_ids(votes), fc::exception);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( api_limit_lookup_committee_member_accounts ) {\n   try{\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      ACTORS((bob));\n      GRAPHENE_CHECK_THROW(db_api.lookup_committee_member_accounts(\"bob\",220), fc::exception);\n      std::map<std::string, committee_member_id_type>  result =db_api.lookup_committee_member_accounts(\"bob\",190);\n      BOOST_REQUIRE_EQUAL( result.size(), 10u);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/app_util_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n#include <graphene/app/util.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::app;\n\nusing fc::uint128_t;\n\nBOOST_AUTO_TEST_SUITE(app_util_tests)\n\nBOOST_AUTO_TEST_CASE(uint128_amount_to_string_test) {\n\n   fc::uint128_t max_u64( std::numeric_limits<uint64_t>::max() );\n   fc::uint128_t min_gt_u64 = max_u64 + 1;\n   fc::uint128_t one_u128 = max_u64 * 10;\n   fc::uint128_t max_u128 = std::numeric_limits<fc::uint128_t>::max();\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          0), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          0), \"1\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        0), \"100\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    0), \"1024000\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 0), \"1234567890\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    0), \"18446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 0), \"18446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   0), \"184467440737095516150\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   0), \"340282366920938463463374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          1), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          1), \"0.1\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        1), \"10\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    1), \"102400\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 1), \"123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    1), \"1844674407370955161.5\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 1), \"1844674407370955161.6\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   1), \"18446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   1), \"34028236692093846346337460743176821145.5\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          2), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          2), \"0.01\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        2), \"1\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    2), \"10240\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 2), \"12345678.9\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    2), \"184467440737095516.15\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 2), \"184467440737095516.16\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   2), \"1844674407370955161.5\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   2), \"3402823669209384634633746074317682114.55\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          3), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          3), \"0.001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        3), \"0.1\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    3), \"1024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 3), \"1234567.89\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    3), \"18446744073709551.615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 3), \"18446744073709551.616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   3), \"184467440737095516.15\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   3), \"340282366920938463463374607431768211.455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          4), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          4), \"0.0001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        4), \"0.01\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    4), \"102.4\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 4), \"123456.789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    4), \"1844674407370955.1615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 4), \"1844674407370955.1616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   4), \"18446744073709551.615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   4), \"34028236692093846346337460743176821.1455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          9), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          9), \"0.000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        9), \"0.0000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    9), \"0.001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 9), \"1.23456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    9), \"18446744073.709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 9), \"18446744073.709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   9), \"184467440737.09551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   9), \"340282366920938463463374607431.768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          10), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          10), \"0.0000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        10), \"0.00000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    10), \"0.0001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 10), \"0.123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    10), \"1844674407.3709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 10), \"1844674407.3709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   10), \"18446744073.709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   10), \"34028236692093846346337460743.1768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          19), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          19), \"0.0000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        19), \"0.00000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    19), \"0.0000000000001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 19), \"0.000000000123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    19), \"1.8446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 19), \"1.8446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   19), \"18.446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   19), \"34028236692093846346.3374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          20), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          20), \"0.00000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        20), \"0.000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    20), \"0.00000000000001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 20), \"0.0000000000123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    20), \"0.18446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 20), \"0.18446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   20), \"1.8446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   20), \"3402823669209384634.63374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          21), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          21), \"0.000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        21), \"0.0000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    21), \"0.000000000000001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 21), \"0.00000000000123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    21), \"0.018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 21), \"0.018446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   21), \"0.18446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   21), \"340282366920938463.463374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          38), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          38), \"0.00000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        38), \"0.000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    38), \"0.00000000000000000000000000000001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 38), \"0.0000000000000000000000000000123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    38), \"0.00000000000000000018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 38), \"0.00000000000000000018446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   38), \"0.0000000000000000018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   38), \"3.40282366920938463463374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          39), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          39), \"0.000000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        39), \"0.0000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    39), \"0.000000000000000000000000000000001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 39), \"0.00000000000000000000000000000123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    39), \"0.000000000000000000018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 39), \"0.000000000000000000018446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   39), \"0.00000000000000000018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   39), \"0.340282366920938463463374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          40), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          40), \"0.0000000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        40), \"0.00000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000,    40), \"0.0000000000000000000000000000000001024\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 40), \"0.000000000000000000000000000000123456789\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64,    40), \"0.0000000000000000000018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 40), \"0.0000000000000000000018446744073709551616\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128,   40), \"0.000000000000000000018446744073709551615\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   40), \"0.0340282366920938463463374607431768211455\" );\n\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 0,          127), \"0\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 1,          127), \"0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( 100,        127), \"0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\" );\n   BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128,   127), \"0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000340282366920938463463374607431768211455\" );\n\n}\n\nBOOST_AUTO_TEST_CASE(price_to_string_throws) {\n\n   int64_t m = std::numeric_limits<int64_t>::max();\n   int64_t n = -1;\n   int64_t x = m / 10000;\n   int64_t y = m / 2;\n   int64_t z = m - 1;\n\n   int64_t a[11] = {n,0,1,2,3,10,200,x,y,z,m};\n   price p[11][11];\n   for( int i = 0; i < 11; ++i )\n      for( int j = 0; j < 11; ++j )\n         p[i][j] = price( asset( a[i] ), asset( a[j] ) );\n\n   for( int i = 0; i < 11; ++i )\n   {\n      for( int j = 0; j < 11; ++j )\n      {\n         price pr = p[i][j];\n         if( i == 0 )\n         {\n            GRAPHENE_REQUIRE_THROW( price_to_string( p[i][j], 0, 0 ), fc::exception );\n         }\n         else if( i == 1 )\n         {\n            BOOST_CHECK_EQUAL( price_to_string( p[i][j],  0,  0 ), \"0\" );\n            BOOST_CHECK_EQUAL( price_to_string( p[i][j],  0, 19 ), \"0\" );\n            BOOST_CHECK_EQUAL( price_to_string( p[i][j], 19,  0 ), \"0\" );\n            BOOST_CHECK_EQUAL( price_to_string( p[i][j], 19, 19 ), \"0\" );\n            BOOST_CHECK_EQUAL( price_to_string( p[i][j], 20, 20 ), \"0\" );\n         }\n         else\n         {\n            GRAPHENE_REQUIRE_THROW( price_to_string( p[i][j], 20, 0 ), fc::exception );\n            GRAPHENE_REQUIRE_THROW( price_to_string( p[i][j], 0, 20 ), fc::exception );\n         }\n         try {\n            if ( pr.base.amount == 0 || (pr.base.amount > 0 && pr.quote.amount >= 0 ) )\n            {\n               // idump( (i) (j) (pr) ); // for debugging\n               // These should not throw\n               // TODO: Verify results\n               BOOST_CHECK( !price_to_string( pr ,0,0).empty() );\n               BOOST_CHECK( !price_to_string( pr ,0,1).empty() );\n               BOOST_CHECK( !price_to_string( pr ,0,2).empty() );\n               BOOST_CHECK( !price_to_string( pr ,0,8).empty() );\n               BOOST_CHECK( !price_to_string( pr ,0,19).empty() );\n               BOOST_CHECK( !price_to_string( pr ,1,0).empty() );\n               BOOST_CHECK( !price_to_string( pr ,1,15).empty() );\n               BOOST_CHECK( !price_to_string( pr ,2,6).empty() );\n               BOOST_CHECK( !price_to_string( pr ,2,10).empty() );\n               BOOST_CHECK( !price_to_string( pr ,5,0).empty() );\n               BOOST_CHECK( !price_to_string( pr ,9,1).empty() );\n               BOOST_CHECK( !price_to_string( pr ,9,9).empty() );\n               BOOST_CHECK( !price_to_string( pr ,9,19).empty() );\n               BOOST_CHECK( !price_to_string( pr ,18,10).empty() );\n               BOOST_CHECK( !price_to_string( pr ,18,13).empty() );\n               BOOST_CHECK( !price_to_string( pr ,18,19).empty() );\n               BOOST_CHECK( !price_to_string( pr ,19,0).empty() );\n               BOOST_CHECK( !price_to_string( pr ,19,7).empty() );\n               BOOST_CHECK( !price_to_string( pr ,19,19).empty() );\n               price new_price = p[j][i];\n               if (pr.quote.amount >= 0)\n                  BOOST_CHECK( !price_diff_percent_string( pr, new_price ).empty() );\n               else\n                  GRAPHENE_REQUIRE_THROW( price_diff_percent_string( pr, new_price ), fc::exception );\n            } else {\n               GRAPHENE_REQUIRE_THROW( price_to_string( pr, 0, 0 ), fc::exception );\n            }\n         } catch(fc::exception& fcx) {\n            BOOST_FAIL( \"FC Exception logging price_to_string: \" + fcx.to_detail_string() );\n         } catch(...) {\n            BOOST_FAIL( \"Uncaught exception in price_to_string. i=\" + std::to_string(i) + \" j=\" + std::to_string(j));\n         }\n      }\n   }\n}\n\n/**\n * Verify that price_to_string comes back with the correct results. Put edge cases here.\n */\nBOOST_AUTO_TEST_CASE(price_to_string_verify)\n{\n   try\n   {\n      BOOST_CHECK_EQUAL( price_to_string( price{ asset(1), asset(1) }, 0, 0 ), \"1\" );\n      BOOST_CHECK_EQUAL( price_to_string( price{ asset(10), asset(10) }, 0, 0), \"1\" );\n      int64_t mx = std::numeric_limits<int64_t>::max();\n      BOOST_CHECK_EQUAL( price_to_string( price{ asset(mx), asset(mx) }, 0, 0), \"1\" );\n      BOOST_CHECK_EQUAL( price_to_string( price{ asset(1), asset(mx) }, 0, 0), \"0.0000000000000000001\" );\n      BOOST_CHECK_EQUAL( price_to_string( price{ asset(mx), asset(1) }, 0, 0), \"9223372036854775807\" );\n   } \n   catch (fc::exception& fx)\n   {\n      BOOST_FAIL( \"FC Exception: \" + fx.to_detail_string() );\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/asset_api_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/app/api.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::app;\n\nBOOST_FIXTURE_TEST_SUITE(asset_api_tests, database_fixture)\n\nBOOST_AUTO_TEST_CASE( asset_holders )\n{\n   graphene::app::asset_api asset_api(app);\n\n   // create an asset and some accounts\n   create_bitasset(\"USD\", account_id_type());\n   auto dan = create_account(\"dan\");\n   auto bob = create_account(\"bob\");\n   auto alice = create_account(\"alice\");\n\n   // send them some bts\n   transfer(account_id_type()(db), dan, asset(100));\n   transfer(account_id_type()(db), alice, asset(200));\n   transfer(account_id_type()(db), bob, asset(300));\n\n   // make call\n   vector<account_asset_balance> holders = asset_api.get_asset_holders( std::string( static_cast<object_id_type>(asset_id_type())), 0, 100);\n   BOOST_CHECK_EQUAL(holders.size(), 4u);\n\n   // by now we can guarantee the order\n   BOOST_CHECK(holders[0].name == \"committee-account\");\n   BOOST_CHECK(holders[1].name == \"bob\");\n   BOOST_CHECK(holders[2].name == \"alice\");\n   BOOST_CHECK(holders[3].name == \"dan\");\n}\nBOOST_AUTO_TEST_CASE( api_limit_get_asset_holders )\n{\n   graphene::app::asset_api asset_api(app);\n\n   // create an asset and some accounts\n   create_bitasset(\"USD\", account_id_type());\n   auto dan = create_account(\"dan\");\n   auto bob = create_account(\"bob\");\n   auto alice = create_account(\"alice\");\n\n   // send them some bts\n   transfer(account_id_type()(db), dan, asset(100));\n   transfer(account_id_type()(db), alice, asset(200));\n   transfer(account_id_type()(db), bob, asset(300));\n\n   // make call\n   GRAPHENE_CHECK_THROW(asset_api.get_asset_holders(std::string( static_cast<object_id_type>(asset_id_type())), 0, 260), fc::exception);\n   vector<account_asset_balance> holders = asset_api.get_asset_holders(std::string( static_cast<object_id_type>(asset_id_type())), 0, 210);\n   BOOST_REQUIRE_EQUAL( holders.size(), 4u );\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/asset_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Bitshares Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n#include <string>\n#include <cmath>\n#include <graphene/chain/asset_object.hpp>\n\nBOOST_AUTO_TEST_SUITE(asset_tests)\n\nBOOST_AUTO_TEST_CASE( asset_to_from_string )\n{\n   std::string positive_results[19];\n   positive_results[0]  = \"12345\";\n   positive_results[1]  = \"1234.5\";\n   positive_results[2]  = \"123.45\";\n   positive_results[3]  = \"12.345\";\n   positive_results[4]  = \"1.2345\";\n   positive_results[5]  = \"0.12345\";\n   positive_results[6]  = \"0.012345\";\n   positive_results[7]  = \"0.0012345\";\n   positive_results[8]  = \"0.00012345\";\n   positive_results[9]  = \"0.000012345\";\n   positive_results[10] = \"0.0000012345\";\n   positive_results[11] = \"0.00000012345\";\n   positive_results[12] = \"0.000000012345\";\n   positive_results[13] = \"0.0000000012345\";\n   positive_results[14] = \"0.00000000012345\";\n   positive_results[15] = \"0.000000000012345\";\n   positive_results[16] = \"0.0000000000012345\";\n   positive_results[17] = \"0.00000000000012345\";\n   positive_results[18] = \"0.000000000000012345\";\n   std::string negative_results[19];\n   for(int i = 0; i < 19; ++i)\n   {\n      negative_results[i] = \"-\" + positive_results[i];\n   }\n   graphene::chain::asset_object test_obj;\n   graphene::chain::share_type amt12345 = 12345;\n   BOOST_TEST_MESSAGE( \"Testing positive numbers\" );\n   for (int i = 0; i < 19; i++)\n   {\n      test_obj.precision = i;\n      BOOST_CHECK_EQUAL(positive_results[i], test_obj.amount_to_string(amt12345));\n   }\n   BOOST_TEST_MESSAGE( \"Testing negative numbers\" );\n   for (int i = 0; i < 19; i++)\n   {\n      test_obj.precision = i;\n      BOOST_CHECK_EQUAL(negative_results[i], test_obj.amount_to_string(amt12345 * -1));\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/authority_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/restriction_predicate.hpp>\n\n#include <graphene/db/simple_index.hpp>\n\n#include <fc/crypto/digest.hpp>\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( authority_tests, database_fixture )\n\nauto make_get_custom(const database& db) {\n   return [&db](account_id_type id, const operation& op, rejected_predicate_map* rejects) {\n      return db.get_viable_custom_authorities(id, op, rejects); };\n}\n\nBOOST_AUTO_TEST_CASE( simple_single_signature )\n{ try {\n   try {\n      fc::ecc::private_key nathan_key = fc::ecc::private_key::generate();\n      const account_object& nathan = create_account(\"nathan\", nathan_key.get_public_key());\n      const asset_object& core = asset_id_type()(db);\n      auto old_balance = fund(nathan);\n\n      transfer_operation op;\n      op.from = nathan.id;\n      op.to = account_id_type();\n      op.amount = core.amount(500);\n      trx.operations.push_back(op);\n      sign(trx, nathan_key);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 500));\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( any_two_of_three )\n{\n   try {\n      fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest(\"key3\"));\n      const account_object& nathan = create_account(\"nathan\", nathan_key1.get_public_key() );\n      const asset_object& core = asset_id_type()(db);\n      auto old_balance = fund(nathan);\n\n      try {\n         account_update_operation op;\n         op.account = nathan.id;\n         op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         sign(trx, nathan_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n      } FC_CAPTURE_AND_RETHROW ((nathan.active))\n\n      transfer_operation op;\n      op.from = nathan.id;\n      op.to = account_id_type();\n      op.amount = core.amount(500);\n      trx.operations.push_back(op);\n      sign(trx, nathan_key1);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      sign(trx, nathan_key2);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 500));\n\n      trx.clear_signatures();\n      sign(trx, nathan_key2);\n      sign(trx, nathan_key3);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 1000));\n\n      trx.clear_signatures();\n      sign(trx, nathan_key1);\n      sign(trx, nathan_key3);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 1500));\n\n      trx.clear_signatures();\n      //sign(trx, fc::ecc::private_key::generate());\n      sign(trx,nathan_key3);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 1500));\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( recursive_accounts )\n{\n   try {\n      fc::ecc::private_key parent1_key = fc::ecc::private_key::generate();\n      fc::ecc::private_key parent2_key = fc::ecc::private_key::generate();\n      const auto& core = asset_id_type()(db);\n\n      BOOST_TEST_MESSAGE( \"Creating parent1 and parent2 accounts\" );\n      const account_object& parent1 = create_account(\"parent1\", parent1_key.get_public_key());\n      const account_object& parent2 = create_account(\"parent2\", parent2_key.get_public_key());\n\n      BOOST_TEST_MESSAGE( \"Creating child account that requires both parent1 and parent2 to approve\" );\n      {\n         auto make_child_op = make_account(\"child\");\n         make_child_op.owner = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1);\n         make_child_op.active = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1);\n         trx.operations.push_back(make_child_op);\n         PUSH_TX( db, trx, ~0 );\n         trx.operations.clear();\n      }\n\n      const account_object& child = get_account(\"child\");\n      auto old_balance = fund(child);\n\n      BOOST_TEST_MESSAGE( \"Attempting to transfer with no signatures, should fail\" );\n      transfer_operation op; \n      op.from = child.id;\n      op.amount = core.amount(500);\n      trx.operations.push_back(op);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n\n      BOOST_TEST_MESSAGE( \"Attempting to transfer with parent1 signature, should fail\" );\n      sign(trx,parent1_key);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      trx.clear_signatures();\n\n      BOOST_TEST_MESSAGE( \"Attempting to transfer with parent2 signature, should fail\" );\n      sign(trx,parent2_key);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n\n      BOOST_TEST_MESSAGE( \"Attempting to transfer with parent1 and parent2 signature, should succeed\" );\n      sign(trx,parent1_key);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 500));\n      trx.clear();\n\n      BOOST_TEST_MESSAGE( \"Adding a key for the child that can override parents\" );\n      fc::ecc::private_key child_key = fc::ecc::private_key::generate();\n      {\n         account_update_operation op;\n         op.account = child.id;\n         op.active = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1,\n                               public_key_type(child_key.get_public_key()), 2);\n         trx.operations.push_back(op);\n         sign(trx,parent1_key);\n         sign(trx,parent2_key);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         BOOST_REQUIRE_EQUAL(child.active.num_auths(), 3u);\n         trx.clear();\n      }\n\n      op.from = child.id;\n      op.to = account_id_type();\n      op.amount = core.amount(500);\n      trx.operations.push_back(op);\n\n      BOOST_TEST_MESSAGE( \"Attempting transfer with no signatures, should fail\" );\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      BOOST_TEST_MESSAGE( \"Attempting transfer just parent1, should fail\" );\n      sign(trx, parent1_key);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      trx.clear_signatures();\n      BOOST_TEST_MESSAGE( \"Attempting transfer just parent2, should fail\" );\n      sign(trx, parent2_key);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n\n      BOOST_TEST_MESSAGE( \"Attempting transfer both parents, should succeed\" );\n      sign(trx,  parent1_key);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 1000));\n      trx.clear_signatures();\n\n      BOOST_TEST_MESSAGE( \"Attempting transfer with just child key, should succeed\" );\n      sign(trx, child_key);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 1500));\n      trx.clear();\n\n      BOOST_TEST_MESSAGE( \"Creating grandparent account, parent1 now requires authority of grandparent\" );\n      auto grandparent = create_account(\"grandparent\");\n      fc::ecc::private_key grandparent_key = fc::ecc::private_key::generate();\n      {\n         account_update_operation op;\n         op.account = parent1.id;\n         op.active = authority(1, account_id_type(grandparent.id), 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         op.account = grandparent.id;\n         op.active = authority(1, public_key_type(grandparent_key.get_public_key()), 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         PUSH_TX( db, trx, ~0 );\n         trx.clear();\n      }\n\n      BOOST_TEST_MESSAGE( \"Attempt to transfer using old parent keys, should fail\" );\n      trx.operations.push_back(op);\n      sign(trx, parent1_key);\n      sign(trx, parent2_key);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      trx.clear_signatures();\n      sign( trx,  parent2_key  );\n      sign( trx,  grandparent_key  );\n\n      BOOST_TEST_MESSAGE( \"Attempt to transfer using parent2_key and grandparent_key\" );\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 2000));\n      trx.clear();\n\n      BOOST_TEST_MESSAGE( \"Update grandparent account authority to be committee account\" );\n      {\n         account_update_operation op;\n         op.account = grandparent.id;\n         op.active = authority(1, account_id_type(), 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         PUSH_TX( db, trx, ~0 );\n         trx.clear();\n      }\n\n      BOOST_TEST_MESSAGE( \"Create recursion depth failure\" );\n      trx.operations.push_back(op);\n      sign(trx, parent2_key);\n      sign(trx, grandparent_key);\n      sign(trx, init_account_priv_key);\n      //Fails due to recursion depth.\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n      BOOST_TEST_MESSAGE( \"verify child key can override recursion checks\" );\n      trx.clear_signatures();\n      sign(trx,  child_key);\n      PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n      BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 2500));\n      trx.clear();\n\n      BOOST_TEST_MESSAGE( \"Verify a cycle fails\" );\n      {\n         account_update_operation op;\n         op.account = parent1.id;\n         op.active = authority(1, account_id_type(child.id), 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         PUSH_TX( db, trx, ~0 );\n         trx.clear();\n      }\n\n      trx.operations.push_back(op);\n      sign(trx, parent2_key);\n      //Fails due to recursion depth.\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( proposed_single_account )\n{\n   using namespace graphene::chain;\n   try {\n      INVOKE(any_two_of_three);\n\n      fc::ecc::private_key committee_key = init_account_priv_key;\n      fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest(\"key3\"));\n\n      const account_object& moneyman = create_account(\"moneyman\", init_account_pub_key);\n      const account_object& nathan = get_account(\"nathan\");\n      const asset_object& core = asset_id_type()(db);\n\n      transfer(account_id_type()(db), moneyman, core.amount(1000000));\n\n      //Following any_two_of_three, nathan's active authority is satisfied by any two of {key1,key2,key3}\n      BOOST_TEST_MESSAGE( \"moneyman is creating proposal for nathan to transfer 100 CORE to moneyman\" );\n\n      transfer_operation transfer_op;\n      transfer_op.from = nathan.id;\n      transfer_op.to  = moneyman.get_id();\n      transfer_op.amount = core.amount(100); \n\n      proposal_create_operation op;\n      op.fee_paying_account = moneyman.id;\n      op.proposed_ops.emplace_back( transfer_op );\n      op.expiration_time =  db.head_block_time() + fc::days(1);\n                                     \n      asset nathan_start_balance = db.get_balance(nathan.get_id(), core.get_id());\n      {\n         vector<authority> other;\n         flat_set<account_id_type> active_set, owner_set;\n         operation_get_required_authorities(op, active_set, owner_set, other, false);\n         BOOST_CHECK_EQUAL(active_set.size(), 1lu);\n         BOOST_CHECK_EQUAL(owner_set.size(), 0lu);\n         BOOST_CHECK_EQUAL(other.size(), 0lu);\n         BOOST_CHECK(*active_set.begin() == moneyman.get_id());\n\n         active_set.clear();\n         other.clear();\n         operation_get_required_authorities(op.proposed_ops.front().op, active_set, owner_set, other, false);\n         BOOST_CHECK_EQUAL(active_set.size(), 1lu);\n         BOOST_CHECK_EQUAL(owner_set.size(), 0lu);\n         BOOST_CHECK_EQUAL(other.size(), 0lu);\n         BOOST_CHECK(*active_set.begin() == nathan.id);\n      }\n\n      trx.operations.push_back(op);\n      set_expiration( db, trx );\n\n      sign( trx,  init_account_priv_key  );\n      const proposal_object& proposal = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());\n\n      BOOST_CHECK_EQUAL(proposal.required_active_approvals.size(), 1lu);\n      BOOST_CHECK_EQUAL(proposal.available_active_approvals.size(), 0lu);\n      BOOST_CHECK_EQUAL(proposal.required_owner_approvals.size(), 0lu);\n      BOOST_CHECK_EQUAL(proposal.available_owner_approvals.size(), 0lu);\n      BOOST_CHECK(*proposal.required_active_approvals.begin() == nathan.id);\n\n      proposal_update_operation pup;\n      pup.proposal = proposal.id;\n      pup.fee_paying_account = nathan.id;\n      BOOST_TEST_MESSAGE( \"Updating the proposal to have nathan's authority\" );\n      pup.active_approvals_to_add.insert(nathan.id);\n\n      trx.operations = {pup};\n      sign( trx,   committee_key  );\n      //committee may not add nathan's approval.\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);\n      pup.active_approvals_to_add.clear();\n      pup.active_approvals_to_add.insert(account_id_type());\n      trx.operations = {pup};\n      sign( trx,   committee_key  );\n      //committee has no stake in the transaction.\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);\n\n      trx.clear_signatures();\n      pup.active_approvals_to_add.clear();\n      pup.active_approvals_to_add.insert(nathan.id);\n      \n      trx.operations = {pup};\n      sign( trx,   nathan_key3  );\n      sign( trx,   nathan_key2  );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), nathan_start_balance.amount.value);\n      PUSH_TX( db, trx );\n      BOOST_CHECK_EQUAL(get_balance(nathan, core), nathan_start_balance.amount.value - 100);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( proposal_failure )\n{\n   try\n   {\n      ACTORS( (bob) (alice) );\n\n      fund( bob,   asset(1000000) );\n      fund( alice, asset(1000000) );\n\n      // create proposal that will eventually fail due to lack of funds\n      transfer_operation top;\n      top.to = alice_id;\n      top.from = bob_id;\n      top.amount = asset(2000000);\n      proposal_create_operation pop;\n      pop.proposed_ops.push_back( { top } );\n      pop.expiration_time = db.head_block_time() + fc::days(1);\n      pop.fee_paying_account = bob_id;\n      trx.operations.push_back( pop );\n      trx.clear_signatures();\n      sign( trx, bob_private_key );\n      processed_transaction processed = PUSH_TX( db, trx );\n      proposal_object prop = db.get<proposal_object>(processed.operation_results.front().get<object_id_type>());\n      trx.clear();\n      generate_block();\n      // add signature\n      proposal_update_operation up_op;\n      up_op.proposal = prop.id;\n      up_op.fee_paying_account = bob_id;\n      up_op.active_approvals_to_add.emplace( bob_id );\n      trx.operations.push_back( up_op );\n      sign( trx, bob_private_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n\n      // check fail reason\n      const proposal_object& result = db.get<proposal_object>(prop.id);\n      BOOST_CHECK(!result.fail_reason.empty());\n      BOOST_CHECK_EQUAL( result.fail_reason.substr(0, 16), \"Assert Exception\");\n   }\n   FC_LOG_AND_RETHROW()\n}\n\n/// Verify that committee authority cannot be invoked in a normal transaction\nBOOST_AUTO_TEST_CASE( committee_authority )\n{ try {\n   fc::ecc::private_key nathan_key = fc::ecc::private_key::generate();\n   fc::ecc::private_key committee_key = init_account_priv_key;\n   const account_object nathan = create_account(\"nathan\", nathan_key.get_public_key());\n   const auto& global_params = db.get_global_properties().parameters;\n\n   generate_block();\n\n   // Signatures are for suckers.\n   db.modify(db.get_global_properties(), [](global_property_object& p) {\n      // Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.\n      p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();\n   });\n\n   BOOST_TEST_MESSAGE( \"transfering 100000 CORE to nathan, signing with committee key should fail because this requires it to be part of a proposal\" );\n   transfer_operation top;\n   top.to = nathan.id;\n   top.amount = asset(100000);\n   trx.operations.push_back(top);\n   sign(trx, committee_key);\n   GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), graphene::chain::invalid_committee_approval );\n\n   auto _sign = [&] { trx.clear_signatures(); sign( trx, nathan_key ); };\n\n   proposal_create_operation pop;\n   pop.proposed_ops.push_back({trx.operations.front()});\n   pop.expiration_time = db.head_block_time() + global_params.committee_proposal_review_period*2;\n   pop.fee_paying_account = nathan.id;\n   trx.operations = {pop};\n   _sign();\n\n   // The review period isn't set yet. Make sure it throws.\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), proposal_create_review_period_required );\n   pop.review_period_seconds = global_params.committee_proposal_review_period / 2;\n   trx.operations.back() = pop;\n   _sign();\n   // The review period is too short. Make sure it throws.\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), proposal_create_review_period_insufficient );\n   pop.review_period_seconds = global_params.committee_proposal_review_period;\n   trx.operations.back() = pop;\n   _sign();\n   proposal_object prop = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());\n   BOOST_REQUIRE(db.find_object(prop.id));\n\n   BOOST_CHECK(prop.expiration_time == pop.expiration_time);\n   BOOST_CHECK(prop.review_period_time && *prop.review_period_time == pop.expiration_time - *pop.review_period_seconds);\n   BOOST_CHECK(prop.proposed_transaction.operations.size() == 1);\n   BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);\n   BOOST_CHECK(!db.get<proposal_object>(prop.id).is_authorized_to_execute(db));\n\n   generate_block();\n   BOOST_REQUIRE(db.find_object(prop.id));\n   BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);\n\n   BOOST_TEST_MESSAGE( \"Checking that the proposal is not authorized to execute\" );\n   BOOST_REQUIRE(!db.get<proposal_object>(prop.id).is_authorized_to_execute(db));\n   trx.clear();\n   proposal_update_operation uop;\n   uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n   uop.proposal = prop.id;\n\n   uop.key_approvals_to_add.emplace(committee_key.get_public_key());\n   /*\n   uop.key_approvals_to_add.emplace(1);\n   uop.key_approvals_to_add.emplace(2);\n   uop.key_approvals_to_add.emplace(3);\n   uop.key_approvals_to_add.emplace(4);\n   uop.key_approvals_to_add.emplace(5);\n   uop.key_approvals_to_add.emplace(6);\n   */\n   trx.operations.push_back(uop);\n   sign( trx, committee_key );\n   PUSH_TX(db, trx);\n   BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);\n   BOOST_CHECK(db.get<proposal_object>(prop.id).is_authorized_to_execute(db));\n\n   trx.clear_signatures();\n   generate_blocks(*prop.review_period_time);\n   uop.key_approvals_to_add.clear();\n   uop.key_approvals_to_add.insert(committee_key.get_public_key()); // was 7\n   trx.operations.back() = uop;\n   sign( trx,  committee_key );\n   // Should throw because the transaction is now in review.\n   GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);\n\n   generate_blocks(prop.expiration_time);\n   BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);\n   // proposal deleted\n   BOOST_CHECK_THROW( db.get<proposal_object>(prop.id), fc::exception );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( fired_committee_members, database_fixture )\n{ try {\n   generate_block();\n   fc::ecc::private_key committee_key = init_account_priv_key;\n   fc::ecc::private_key committee_member_key = fc::ecc::private_key::generate();\n\n   //Meet nathan. He has a little money.\n   const account_object* nathan = &create_account(\"nathan\");\n   transfer(account_id_type()(db), *nathan, asset(5000));\n   generate_block();\n   nathan = &get_account(\"nathan\");\n   flat_set<vote_id_type> committee_members;\n\n   /*\n   db.modify(db.get_global_properties(), [](global_property_object& p) {\n      // Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.\n      p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();\n   });\n   */\n\n   for( int i = 0; i < 15; ++i )\n   {\n      const auto& account = create_account(\"committee-member\" + fc::to_string(i+1), committee_member_key.get_public_key());\n      upgrade_to_lifetime_member(account);\n      committee_members.insert(create_committee_member(account).vote_id);\n   }\n   BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n\n   //A proposal is created to give nathan lots more money.\n   proposal_create_operation pop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time());\n   pop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n   pop.expiration_time = db.head_block_time() + *pop.review_period_seconds + fc::days(1).to_seconds();\n   ilog( \"Creating proposal to give nathan money that expires: ${e}\", (\"e\", pop.expiration_time ) );\n   ilog( \"The proposal has a review period of: ${r} sec\", (\"r\",*pop.review_period_seconds) );\n\n   transfer_operation top;\n   top.to = nathan->id;\n   top.amount = asset(100000);\n   pop.proposed_ops.emplace_back(top);\n   trx.operations.push_back(pop);\n   const proposal_object& prop = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());\n   proposal_id_type pid = prop.id;\n   BOOST_CHECK(!pid(db).is_authorized_to_execute(db));\n\n   ilog( \"commitee member approves proposal\" );\n   //committee key approves of the proposal.\n   proposal_update_operation uop;\n   uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n   uop.proposal = pid;\n   uop.key_approvals_to_add.emplace(init_account_pub_key);\n   trx.operations.back() = uop;\n   sign( trx, committee_key );\n   PUSH_TX( db, trx );\n   BOOST_CHECK(pid(db).is_authorized_to_execute(db));\n\n   ilog( \"Generating blocks for 2 days\" );\n   generate_block();\n   BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n   generate_block();\n   BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n   //Time passes... the proposal is now in its review period.\n   //generate_blocks(*pid(db).review_period_time);\n   generate_blocks(db.head_block_time() + fc::days(2) );\n   ilog( \"head block time: ${t}\", (\"t\",db.head_block_time()));\n\n   fc::time_point_sec maintenance_time = db.get_dynamic_global_properties().next_maintenance_time;\n   BOOST_CHECK_LT(maintenance_time.sec_since_epoch(), pid(db).expiration_time.sec_since_epoch());\n   //Yay! The proposal to give nathan more money is authorized.\n   BOOST_REQUIRE(pid(db).is_authorized_to_execute(db));\n\n   nathan = &get_account(\"nathan\");\n   // no money yet\n   BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n\n   {\n      //Oh noes! Nathan votes for a whole new slate of committee_members!\n      account_update_operation op;\n      op.account = nathan->id;\n      op.new_options = nathan->options;\n      op.new_options->votes = committee_members;\n      trx.operations.push_back(op);\n      set_expiration( db, trx );\n      PUSH_TX( db, trx, ~0 );\n      trx.operations.clear();\n   }\n   idump((get_balance(*nathan, asset_id_type()(db))));\n   // still no money\n   BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n\n   //Time passes... the set of active committee_members gets updated.\n   generate_blocks(maintenance_time);\n   //The proposal is no longer authorized, because the active committee_members got changed.\n   BOOST_CHECK(!pid(db).is_authorized_to_execute(db));\n   // still no money\n   BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n\n   //Time passes... the proposal has now expired.\n   generate_blocks(pid(db).expiration_time);\n   BOOST_CHECK(db.find(pid) == nullptr);\n\n   //Nathan never got any more money because the proposal was rejected.\n   BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( proposal_two_accounts, database_fixture )\n{ try {\n   generate_block();\n\n   auto nathan_key = generate_private_key(\"nathan\");\n   auto dan_key = generate_private_key(\"dan\");\n   const account_object& nathan = create_account(\"nathan\", nathan_key.get_public_key() );\n   const account_object& dan = create_account(\"dan\", dan_key.get_public_key() );\n\n   transfer(account_id_type()(db), nathan, asset(100000));\n   transfer(account_id_type()(db), dan, asset(100000));\n\n   {\n      transfer_operation top;\n      top.from = dan.get_id();\n      top.to = nathan.get_id();\n      top.amount = asset(500);\n\n      proposal_create_operation pop;\n      pop.proposed_ops.emplace_back(top);\n      std::swap(top.from, top.to);\n      pop.proposed_ops.emplace_back(top);\n\n      pop.fee_paying_account = nathan.get_id();\n      pop.expiration_time = db.head_block_time() + fc::days(1);\n      trx.operations.push_back(pop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();\n   BOOST_CHECK(prop.required_active_approvals.size() == 2);\n   BOOST_CHECK(prop.required_owner_approvals.size() == 0);\n   BOOST_CHECK(!prop.is_authorized_to_execute(db));\n\n   {\n      proposal_id_type pid = prop.id;\n      proposal_update_operation uop;\n      uop.proposal = prop.id;\n      uop.active_approvals_to_add.insert(nathan.get_id());\n      uop.fee_paying_account = nathan.get_id();\n      trx.operations.push_back(uop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n\n      BOOST_CHECK(db.find_object(pid) != nullptr);\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n\n      uop.active_approvals_to_add = {dan.get_id()};\n      trx.operations.push_back(uop);\n      sign( trx, nathan_key );\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);\n      sign( trx, dan_key );\n      PUSH_TX( db, trx );\n\n      BOOST_CHECK(db.find_object(pid) == nullptr);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture )\n{ try {\n   generate_block();\n\n   auto nathan_key = generate_private_key(\"nathan\");\n   auto dan_key = generate_private_key(\"dan\");\n   const account_object& nathan = create_account(\"nathan\", nathan_key.get_public_key() );\n   const account_object& dan = create_account(\"dan\", dan_key.get_public_key() );\n\n   transfer(account_id_type()(db), nathan, asset(100000));\n   transfer(account_id_type()(db), dan, asset(100000));\n\n   {\n      transfer_operation top;\n      top.from = dan.get_id();\n      top.to = nathan.get_id();\n      top.amount = asset(500);\n\n      proposal_create_operation pop;\n      pop.proposed_ops.emplace_back(top);\n      std::swap(top.from, top.to);\n      top.amount = asset(6000);\n      pop.proposed_ops.emplace_back(top);\n\n      pop.fee_paying_account = nathan.get_id();\n      pop.expiration_time = db.head_block_time() + fc::days(1);\n      trx.operations.push_back(pop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();\n   BOOST_CHECK(prop.required_active_approvals.size() == 2);\n   BOOST_CHECK(prop.required_owner_approvals.size() == 0);\n   BOOST_CHECK(!prop.is_authorized_to_execute(db));\n\n   {\n      proposal_update_operation uop;\n      uop.fee_paying_account = nathan.get_id();\n      uop.proposal = prop.id;\n      uop.active_approvals_to_add.insert(nathan.get_id());\n      trx.operations.push_back(uop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 1lu);\n\n      std::swap(uop.active_approvals_to_add, uop.active_approvals_to_remove);\n      trx.operations.push_back(uop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 0lu);\n   }\n\n   {\n      proposal_id_type pid = prop.id;\n      proposal_delete_operation dop;\n      dop.fee_paying_account = nathan.get_id();\n      dop.proposal = pid;\n      trx.operations.push_back(dop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      BOOST_CHECK(db.find_object(pid) == nullptr);\n      BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture )\n{ try {\n   generate_block();\n\n   auto nathan_key = generate_private_key(\"nathan\");\n   auto dan_key = generate_private_key(\"dan\");\n   const account_object& nathan = create_account(\"nathan\", nathan_key.get_public_key() );\n   const account_object& dan = create_account(\"dan\", dan_key.get_public_key() );\n\n   transfer(account_id_type()(db), nathan, asset(100000));\n   transfer(account_id_type()(db), dan, asset(100000));\n\n   {\n      transfer_operation top;\n      top.from = dan.get_id();\n      top.to = nathan.get_id();\n      top.amount = asset(500);\n\n      account_update_operation uop;\n      uop.account = nathan.get_id();\n      uop.owner = authority(1, public_key_type(generate_private_key(\"nathan2\").get_public_key()), 1);\n\n      proposal_create_operation pop;\n      pop.proposed_ops.emplace_back(top);\n      pop.proposed_ops.emplace_back(uop);\n      std::swap(top.from, top.to);\n      top.amount = asset(6000);\n      pop.proposed_ops.emplace_back(top);\n\n      pop.fee_paying_account = nathan.get_id();\n      pop.expiration_time = db.head_block_time() + fc::days(1);\n      trx.operations.push_back(pop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();\n   BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1lu);\n   BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1lu);\n   BOOST_CHECK(!prop.is_authorized_to_execute(db));\n\n   {\n      proposal_update_operation uop;\n      uop.fee_paying_account = nathan.get_id();\n      uop.proposal = prop.id;\n      uop.owner_approvals_to_add.insert(nathan.get_id());\n      trx.operations.push_back(uop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 1lu);\n\n      std::swap(uop.owner_approvals_to_add, uop.owner_approvals_to_remove);\n      trx.operations.push_back(uop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 0lu);\n   }\n\n   {\n      proposal_id_type pid = prop.id;\n      proposal_delete_operation dop;\n      dop.fee_paying_account = nathan.get_id();\n      dop.proposal = pid;\n      dop.using_owner_authority = true;\n      trx.operations.push_back(dop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      BOOST_CHECK(db.find_object(pid) == nullptr);\n      BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture )\n{ try {\n   generate_block();\n\n   auto nathan_key = generate_private_key(\"nathan\");\n   auto dan_key = generate_private_key(\"dan\");\n   const account_object& nathan = create_account(\"nathan\", nathan_key.get_public_key() );\n   const account_object& dan = create_account(\"dan\", dan_key.get_public_key() );\n\n   transfer(account_id_type()(db), nathan, asset(100000));\n   transfer(account_id_type()(db), dan, asset(100000));\n\n   {\n      transfer_operation top;\n      top.from = dan.get_id();\n      top.to = nathan.get_id();\n      top.amount = asset(500);\n\n      account_update_operation uop;\n      uop.account = nathan.get_id();\n      uop.owner = authority(1, public_key_type(generate_private_key(\"nathan2\").get_public_key()), 1);\n\n      proposal_create_operation pop;\n      pop.proposed_ops.emplace_back(top);\n      pop.proposed_ops.emplace_back(uop);\n      std::swap(top.from, top.to);\n      top.amount = asset(6000);\n      pop.proposed_ops.emplace_back(top);\n\n      pop.fee_paying_account = nathan.get_id();\n      pop.expiration_time = db.head_block_time() + fc::days(1);\n      trx.operations.push_back(pop);\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();\n   BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1lu);\n   BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1lu);\n   BOOST_CHECK(!prop.is_authorized_to_execute(db));\n\n   {\n      proposal_id_type pid = prop.id;\n      proposal_update_operation uop;\n      uop.fee_paying_account = nathan.get_id();\n      uop.proposal = prop.id;\n      uop.key_approvals_to_add.insert(dan.active.key_auths.begin()->first);\n      trx.operations.push_back(uop);\n      set_expiration( db, trx );\n      sign( trx, nathan_key );\n      sign( trx, dan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1lu);\n\n      std::swap(uop.key_approvals_to_add, uop.key_approvals_to_remove);\n      trx.operations.push_back(uop);\n      trx.expiration += fc::seconds(1);  // Survive trx dupe check\n      sign( trx, nathan_key );\n      sign( trx, dan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 0lu);\n\n      std::swap(uop.key_approvals_to_add, uop.key_approvals_to_remove);\n      trx.operations.push_back(uop);\n      trx.expiration += fc::seconds(1);  // Survive trx dupe check\n      sign( trx, nathan_key );\n      sign( trx, dan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(!prop.is_authorized_to_execute(db));\n      BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1lu);\n\n      uop.key_approvals_to_add.clear();\n      uop.owner_approvals_to_add.insert(nathan.get_id());\n      trx.operations.push_back(uop);\n      trx.expiration += fc::seconds(1);  // Survive trx dupe check\n      sign( trx, nathan_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n      BOOST_CHECK(db.find_object(pid) == nullptr);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )\n{\n   try\n   {\n      //Get a sane head block time\n      generate_block();\n\n      db.modify(db.get_global_properties(), [](global_property_object& p) {\n         p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();\n      });\n\n      transaction tx;\n\n      private_key_type committee_key = init_account_priv_key;\n      // Sam is the creator of accounts\n      private_key_type sam_key = generate_private_key(\"sam\");\n\n      account_object sam_account_object = create_account( \"sam\", sam_key );\n      upgrade_to_lifetime_member(sam_account_object);\n      account_object committee_account_object = committee_account(db);\n\n      const asset_object& core = asset_id_type()(db);\n\n      transfer(committee_account_object, sam_account_object, core.amount(100000));\n\n      // have Sam create some keys\n\n      int keys_to_create = 2*GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;\n      vector<private_key_type> private_keys;\n\n      private_keys.reserve( keys_to_create );\n      for( int i=0; i<keys_to_create; i++ )\n      {\n         string seed = \"this_is_a_key_\" + std::to_string(i);\n         private_key_type privkey = generate_private_key( seed );\n         private_keys.push_back( privkey );\n      }\n      set_expiration( db, tx );\n\n      vector<public_key_type> key_ids;\n\n      key_ids.reserve( keys_to_create );\n      for( int i=0; i<keys_to_create; i++ )\n          key_ids.push_back( private_keys[i].get_public_key() );\n\n      // now try registering accounts with n keys, 0 < n < 20\n\n      // TODO:  Make sure it throws / accepts properly when\n      //   max_account_authority is changed in global parameteres\n\n      for( int num_keys=1; num_keys<=keys_to_create; num_keys++ )\n      {\n         // try registering account with n keys\n\n         authority test_authority;\n         test_authority.weight_threshold = num_keys;\n\n         for( int i=0; i<num_keys; i++ )\n            test_authority.key_auths[ key_ids[i] ] = 1;\n\n         auto check_tx = [&]( const authority& owner_auth,\n                              const authority& active_auth )\n         {\n             const uint16_t max_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;\n             account_create_operation anon_create_op;\n             transaction tx;\n\n             anon_create_op.owner = owner_auth;\n             anon_create_op.active = active_auth;\n             anon_create_op.registrar = sam_account_object.id;\n             anon_create_op.options.memo_key = sam_account_object.options.memo_key;\n             anon_create_op.name = generate_anon_acct_name();\n\n             tx.operations.push_back( anon_create_op );\n             set_expiration( db, tx );\n\n             if( num_keys > max_authority_membership )\n             {\n                GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, ~0 ), account_create_max_auth_exceeded );\n             }\n             else\n             {\n                PUSH_TX( db, tx, ~0 );\n             }\n             return;\n         };\n\n         check_tx( sam_account_object.owner, test_authority  );\n         check_tx( test_authority, sam_account_object.active );\n         check_tx( test_authority, test_authority );\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )\n{\n   try\n   {\n      private_key_type committee_key = init_account_priv_key;\n      // Sam is the creator of accounts\n      private_key_type alice_key = generate_private_key(\"alice\");\n      private_key_type bob_key = generate_private_key(\"bob\");\n      private_key_type charlie_key = generate_private_key(\"charlie\");\n\n      account_object committee_account_object = committee_account(db);\n      account_object alice_account_object = create_account( \"alice\", alice_key );\n      account_object bob_account_object = create_account( \"bob\", bob_key );\n      account_object charlie_account_object = create_account( \"charlie\", charlie_key );\n\n      // unneeded, comment it out to silence compiler warning\n      //key_id_type bob_key_id = bob_account_object.memo_key;\n\n      uint32_t skip = database::skip_transaction_dupe_check;\n\n      // send from Sam -> Alice, signed by Sam\n\n      const asset_object& core = asset_id_type()(db);\n      transfer(committee_account_object, alice_account_object, core.amount(100000));\n\n      transfer_operation xfer_op;\n      xfer_op.from = alice_account_object.id;\n      xfer_op.to = bob_account_object.id;\n      xfer_op.amount = core.amount(5000);\n      xfer_op.fee = db.current_fee_schedule().calculate_fee( xfer_op );\n\n      trx.clear();\n      trx.operations.push_back( xfer_op );\n\n      BOOST_TEST_MESSAGE( \"Transfer signed by alice\" );\n      sign( trx, alice_key  );\n\n      flat_set<account_id_type> active_set, owner_set;\n      vector<authority> others;\n      trx.get_required_authorities(active_set, owner_set, others, false);\n\n      PUSH_TX( db,  trx, skip  );\n\n      trx.operations.push_back( xfer_op );\n      BOOST_TEST_MESSAGE( \"Invalidating Alices Signature\" );\n      // Alice's signature is now invalid\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db,  trx, skip  ), fc::exception );\n      // Re-sign, now OK (sig is replaced)\n      BOOST_TEST_MESSAGE( \"Resign with Alice's Signature\" );\n      trx.clear_signatures();\n      sign( trx,  alice_key  );\n      PUSH_TX( db,  trx, skip  );\n\n      trx.clear_signatures();\n      trx.operations.pop_back();\n      sign( trx,  alice_key  );\n      sign( trx,  charlie_key  );\n      // Signed by third-party Charlie (irrelevant key, not in authority)\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db,  trx, skip  ), tx_irrelevant_sig );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_FIXTURE_TEST_CASE( voting_account, database_fixture )\n{ try {\n   ACTORS((nathan)(vikram));\n   upgrade_to_lifetime_member(nathan_id);\n   upgrade_to_lifetime_member(vikram_id);\n   committee_member_id_type nathan_committee_member = create_committee_member(nathan_id(db)).id;\n   committee_member_id_type vikram_committee_member = create_committee_member(vikram_id(db)).id;\n\n   //wdump((db.get_balance(account_id_type(), asset_id_type())));\n   generate_block();\n\n   //wdump((db.get_balance(account_id_type(), asset_id_type())));\n   transfer(account_id_type(), nathan_id, asset(1000000));\n   transfer(account_id_type(), vikram_id, asset(100));\n\n   {\n      account_update_operation op;\n      op.account = nathan_id;\n      op.new_options = nathan_id(db).options;\n      op.new_options->voting_account = vikram_id;\n      op.new_options->votes = flat_set<vote_id_type>{nathan_committee_member(db).vote_id};\n      op.new_options->num_committee = 1;\n      trx.operations.push_back(op);\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n   {\n      account_update_operation op;\n      op.account = vikram_id;\n      op.new_options = vikram_id(db).options;\n      op.new_options->votes.insert(vikram_committee_member(db).vote_id);\n      op.new_options->num_committee = 11;\n      trx.operations.push_back(op);\n      sign( trx, vikram_private_key );\n      // Fails because num_committee is larger than the cardinality of committee members being voted for\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);\n      op.new_options->num_committee = 3;\n      trx.operations = {op};\n      trx.clear_signatures();\n      sign( trx, vikram_private_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time + GRAPHENE_DEFAULT_BLOCK_INTERVAL);\n   BOOST_CHECK(std::find(db.get_global_properties().active_committee_members.begin(),\n                         db.get_global_properties().active_committee_members.end(),\n                         nathan_committee_member) == db.get_global_properties().active_committee_members.end());\n   BOOST_CHECK(std::find(db.get_global_properties().active_committee_members.begin(),\n                         db.get_global_properties().active_committee_members.end(),\n                         vikram_committee_member) != db.get_global_properties().active_committee_members.end());\n} FC_LOG_AND_RETHROW() }\n\n/*\n * Simple corporate accounts:\n *\n * Well Corp.       Alice 50, Bob 50             T=60\n * Xylo Company     Alice 30, Cindy 50           T=40\n * Yaya Inc.        Bob 10, Dan 10, Edy 10       T=20\n * Zyzz Co.         Dan 50                       T=40\n *\n * Complex corporate accounts:\n *\n * Mega Corp.       Well 30, Yes 30              T=40\n * Nova Ltd.        Alice 10, Well 10            T=20\n * Odle Intl.       Dan 10, Yes 10, Zyzz 10      T=20\n * Poxx LLC         Well 10, Xylo 10, Yes 20, Zyzz 20   T=40\n */\n\nBOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )\n{\n   try\n   {\n      ACTORS(\n              (alice)(bob)(cindy)(dan)(edy)\n              (mega)(nova)(odle)(poxx)\n              (well)(xylo)(yaya)(zyzz)\n            );\n\n      auto set_auth = [&](\n         account_id_type aid,\n         const authority& auth\n         )\n      {\n         signed_transaction tx;\n         account_update_operation op;\n         op.account = aid;\n         op.active = auth;\n         op.owner = auth;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_transaction_signatures );\n      } ;\n\n      auto get_active = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).active);\n      } ;\n\n      auto get_owner = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).owner);\n      } ;\n\n      auto chk = [&](\n         const signed_transaction& tx,\n         flat_set<public_key_type> available_keys,\n         set<public_key_type> ref_set\n         ) -> bool\n      {\n         //wdump( (tx)(available_keys) );\n         set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,\n                                                                      get_active, get_owner, false, false);\n         set<public_key_type> result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys,\n                                                                       get_active, get_owner, true, false);\n         //wdump( (result_set)(result_set2)(ref_set) );\n         return result_set == ref_set && result_set2 == ref_set;\n      } ;\n\n      set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) );\n      set_auth( xylo_id, authority( 40, alice_id, 30, cindy_id, 50 ) );\n      set_auth( yaya_id, authority( 20, bob_id, 10, dan_id, 10, edy_id, 10 ) );\n      set_auth( zyzz_id, authority( 40, dan_id, 50 ) );\n\n      set_auth( mega_id, authority( 40, well_id, 30, yaya_id, 30 ) );\n      set_auth( nova_id, authority( 20, alice_id, 10, well_id, 10 ) );\n      set_auth( odle_id, authority( 20, dan_id, 10, yaya_id, 10, zyzz_id, 10 ) );\n      set_auth( poxx_id, authority( 40, well_id, 10, xylo_id, 10, yaya_id, 20, zyzz_id, 20 ) );\n\n      signed_transaction tx;\n      flat_set< public_key_type > all_keys\n         { alice_public_key, bob_public_key, cindy_public_key, dan_public_key, edy_public_key };\n\n      tx.operations.push_back( transfer_operation() );\n      transfer_operation& op = tx.operations.back().get<transfer_operation>();\n      op.to = edy_id;\n      op.amount = asset(1);\n\n      op.from = alice_id;\n      BOOST_CHECK( chk( tx, all_keys, { alice_public_key } ) );\n      op.from = bob_id;\n      BOOST_CHECK( chk( tx, all_keys, { bob_public_key } ) );\n      op.from = well_id;\n      BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key } ) );\n      op.from = xylo_id;\n      BOOST_CHECK( chk( tx, all_keys, { alice_public_key, cindy_public_key } ) );\n      op.from = yaya_id;\n      BOOST_CHECK( chk( tx, all_keys, { bob_public_key, dan_public_key } ) );\n      op.from = zyzz_id;\n      BOOST_CHECK( chk( tx, all_keys, { dan_public_key } ) );\n\n      op.from = mega_id;\n      BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key, dan_public_key } ) );\n      op.from = nova_id;\n      BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key } ) );\n      op.from = odle_id;\n      BOOST_CHECK( chk( tx, all_keys, { bob_public_key, dan_public_key } ) );\n      op.from = poxx_id;\n      BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key, cindy_public_key, dan_public_key } ) );\n\n      // TODO:  Add sigs to tx, then check\n      // TODO:  Check removing sigs      \n      // TODO:  Accounts with mix of keys and accounts in their authority\n      // TODO:  Tx with multiple ops requiring different sigs\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/*\n * Pathological case\n *\n *      Roco(T=2)\n *    1/         \\2\n *   Styx(T=2)   Thud(T=1)\n *  1/     \\1       |1\n * Alice  Bob     Alice\n */\n\nBOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )\n{\n   try\n   {\n      ACTORS(\n         (alice)(bob)\n         (roco)\n         (styx)(thud)\n         );\n\n      auto set_auth = [&](\n         account_id_type aid,\n         const authority& auth\n         )\n      {\n         signed_transaction tx;\n         account_update_operation op;\n         op.account = aid;\n         op.active = auth;\n         op.owner = auth;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_transaction_signatures );\n      } ;\n\n      auto get_active = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).active);\n      } ;\n\n      auto get_owner = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).owner);\n      } ;\n\n      auto chk = [&](\n         const signed_transaction& tx,\n         flat_set<public_key_type> available_keys,\n         set<public_key_type> ref_set\n         ) -> bool\n      {\n         //wdump( (tx)(available_keys) );\n         set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,\n                                                                      get_active, get_owner, false, false);\n         set<public_key_type> result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys,\n                                                                       get_active, get_owner, true, false);\n         //wdump( (result_set)(result_set2)(ref_set) );\n         return result_set == ref_set && result_set2 == ref_set;\n      } ;\n\n      auto chk_min = [&](\n         const signed_transaction& tx,\n         flat_set<public_key_type> available_keys,\n         set<public_key_type> ref_set\n         ) -> bool\n      {\n         //wdump( (tx)(available_keys) );\n          auto get_custom = make_get_custom(db);\n         set<public_key_type> result_set = tx.minimize_required_signatures(db.get_chain_id(), available_keys,\n                                                                           get_active, get_owner, get_custom,\n                                                                           false, false);\n         set<public_key_type> result_set2 = tx.minimize_required_signatures(db.get_chain_id(), available_keys,\n                                                                            get_active, get_owner, get_custom,\n                                                                            true, false);\n         //wdump( (result_set)(result_set2)(ref_set) );\n         return result_set == ref_set && result_set2 == ref_set;\n      } ;\n\n      set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) );\n      set_auth( styx_id, authority( 2, alice_id, 1, bob_id, 1 ) );\n      set_auth( thud_id, authority( 1, alice_id, 1 ) );\n\n      signed_transaction tx;\n      transfer_operation op;\n      op.from = roco_id;\n      op.to = bob_id;\n      op.amount = asset(1);\n      tx.operations.push_back( op );\n\n      BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) );\n      BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) );\n\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   true, false ), fc::exception );\n      sign( tx, alice_private_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/*\n * Active vs Owner https://github.com/bitshares/bitshares-core/issues/584\n *\n * All weights and all thresholds are 1, so every single key should be able to sign if within max_depth\n *\n * Bob --+--(a)--+-- Alice --+--(a)--+-- Daisy --(a/o)-- Daisy_active_key / Daisy_owner_key\n *       |       |           |       |\n *       |       |           |       +-- Alice_active_key\n *       |       |           |\n *       |       |           +--(o)--+-- Cindy --(a/o)-- Cindy_active_key / Cindy_owner_key\n *       |       |                   |\n *       |       |                   +-- Alice_owner_key\n *       |       |\n *       |       +-- Bob_active_key\n *       |\n *       +--(o)--+-- Edwin --+--(a)--+-- Gavin --(a/o)-- Gavin_active_key / Gavin_owner_key\n *               |           |       |\n *               |           |       +-- Edwin_active_key\n *               |           |\n *               |           +--(o)--+-- Frank --(a/o)-- Frank_active_key / Frank_owner_key\n *               |                   |\n *               |                   +-- Edwin_owner_key\n *               |\n *               +-- Bob_owner_key\n */\nBOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture )\n{\n   try\n   {\n      ACTORS(\n         (alice)(bob)(cindy)(daisy)(edwin)(frank)(gavin)\n         );\n\n      transfer( account_id_type(), bob_id, asset(100000) );\n\n      auto set_auth = [&](\n         account_id_type aid,\n         const authority& active,\n         const authority& owner\n         )\n      {\n         signed_transaction tx;\n         account_update_operation op;\n         op.account = aid;\n         op.active = active;\n         op.owner = owner;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_transaction_signatures );\n      } ;\n\n      auto get_active = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).active);\n      } ;\n\n      auto get_owner = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).owner);\n      } ;\n\n      auto chk = [&](\n         const signed_transaction& tx,\n         bool after_hf_584,\n         flat_set<public_key_type> available_keys,\n         set<public_key_type> ref_set\n         ) -> bool\n      {\n         //wdump( (tx)(available_keys) );\n         set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,\n                                                                      get_active, get_owner, after_hf_584, false);\n         //wdump( (result_set)(ref_set) );\n         return result_set == ref_set;\n      } ;\n\n      fc::ecc::private_key alice_active_key = fc::ecc::private_key::regenerate(fc::digest(\"alice_active\"));\n      fc::ecc::private_key alice_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"alice_owner\"));\n      fc::ecc::private_key bob_active_key = fc::ecc::private_key::regenerate(fc::digest(\"bob_active\"));\n      fc::ecc::private_key bob_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"bob_owner\"));\n      fc::ecc::private_key cindy_active_key = fc::ecc::private_key::regenerate(fc::digest(\"cindy_active\"));\n      fc::ecc::private_key cindy_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"cindy_owner\"));\n      fc::ecc::private_key daisy_active_key = fc::ecc::private_key::regenerate(fc::digest(\"daisy_active\"));\n      fc::ecc::private_key daisy_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"daisy_owner\"));\n      fc::ecc::private_key edwin_active_key = fc::ecc::private_key::regenerate(fc::digest(\"edwin_active\"));\n      fc::ecc::private_key edwin_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"edwin_owner\"));\n      fc::ecc::private_key frank_active_key = fc::ecc::private_key::regenerate(fc::digest(\"frank_active\"));\n      fc::ecc::private_key frank_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"frank_owner\"));\n      fc::ecc::private_key gavin_active_key = fc::ecc::private_key::regenerate(fc::digest(\"gavin_active\"));\n      fc::ecc::private_key gavin_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"gavin_owner\"));\n\n      public_key_type alice_active_pub( alice_active_key.get_public_key() );\n      public_key_type alice_owner_pub( alice_owner_key.get_public_key() );\n      public_key_type bob_active_pub( bob_active_key.get_public_key() );\n      public_key_type bob_owner_pub( bob_owner_key.get_public_key() );\n      public_key_type cindy_active_pub( cindy_active_key.get_public_key() );\n      public_key_type cindy_owner_pub( cindy_owner_key.get_public_key() );\n      public_key_type daisy_active_pub( daisy_active_key.get_public_key() );\n      public_key_type daisy_owner_pub( daisy_owner_key.get_public_key() );\n      public_key_type edwin_active_pub( edwin_active_key.get_public_key() );\n      public_key_type edwin_owner_pub( edwin_owner_key.get_public_key() );\n      public_key_type frank_active_pub( frank_active_key.get_public_key() );\n      public_key_type frank_owner_pub( frank_owner_key.get_public_key() );\n      public_key_type gavin_active_pub( gavin_active_key.get_public_key() );\n      public_key_type gavin_owner_pub( gavin_owner_key.get_public_key() );\n\n      set_auth( alice_id, authority( 1, alice_active_pub, 1, daisy_id, 1 ), authority( 1, alice_owner_pub, 1, cindy_id, 1 ) );\n      set_auth(   bob_id, authority( 1,   bob_active_pub, 1, alice_id, 1 ), authority( 1,   bob_owner_pub, 1, edwin_id, 1 ) );\n\n      set_auth( cindy_id, authority( 1, cindy_active_pub, 1 ), authority( 1, cindy_owner_pub, 1 ) );\n      set_auth( daisy_id, authority( 1, daisy_active_pub, 1 ), authority( 1, daisy_owner_pub, 1 ) );\n\n      set_auth( edwin_id, authority( 1, edwin_active_pub, 1, gavin_id, 1 ), authority( 1, edwin_owner_pub, 1, frank_id, 1 ) );\n\n      set_auth( frank_id, authority( 1, frank_active_pub, 1 ), authority( 1, frank_owner_pub, 1 ) );\n      set_auth( gavin_id, authority( 1, gavin_active_pub, 1 ), authority( 1, gavin_owner_pub, 1 ) );\n\n      generate_block();\n\n      signed_transaction tx;\n      transfer_operation op;\n      op.from = bob_id;\n      op.to = alice_id;\n      op.amount = asset(1);\n      tx.operations.push_back( op );\n      set_expiration( db, tx );\n\n      // https://github.com/bitshares/bitshares-core/issues/584\n      // If not allow non-immediate owner to authorize\n      BOOST_CHECK( chk( tx, false, { alice_owner_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { alice_active_pub }, { alice_active_pub } ) );\n      BOOST_CHECK( chk( tx, false, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, false, { bob_owner_pub }, { bob_owner_pub } ) );\n      BOOST_CHECK( chk( tx, false, { bob_active_pub }, { bob_active_pub } ) );\n      BOOST_CHECK( chk( tx, false, { bob_active_pub, bob_owner_pub }, { bob_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, false, { cindy_owner_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { cindy_active_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { cindy_active_pub, cindy_owner_pub }, { } ) );\n\n      BOOST_CHECK( chk( tx, false, { daisy_owner_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { daisy_active_pub }, { daisy_active_pub } ) );\n      BOOST_CHECK( chk( tx, false, { daisy_active_pub, daisy_owner_pub }, { daisy_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, false, { edwin_owner_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { edwin_active_pub }, { edwin_active_pub } ) );\n      BOOST_CHECK( chk( tx, false, { edwin_active_pub, edwin_owner_pub }, { edwin_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, false, { frank_owner_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { frank_active_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { frank_active_pub, frank_owner_pub }, { } ) );\n\n      BOOST_CHECK( chk( tx, false, { gavin_owner_pub }, { } ) );\n      BOOST_CHECK( chk( tx, false, { gavin_active_pub }, { gavin_active_pub } ) );\n      BOOST_CHECK( chk( tx, false, { gavin_active_pub, gavin_owner_pub }, { gavin_active_pub } ) );\n\n      // If allow non-immediate owner to authorize\n      BOOST_CHECK( chk( tx, true, { alice_owner_pub }, { alice_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { alice_active_pub }, { alice_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, true, { bob_owner_pub }, { bob_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { bob_active_pub }, { bob_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { bob_active_pub, bob_owner_pub }, { bob_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, true, { cindy_owner_pub }, { cindy_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { cindy_active_pub }, { cindy_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { cindy_active_pub, cindy_owner_pub }, { cindy_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, true, { daisy_owner_pub }, { daisy_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { daisy_active_pub }, { daisy_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { daisy_active_pub, daisy_owner_pub }, { daisy_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, true, { edwin_owner_pub }, { edwin_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { edwin_active_pub }, { edwin_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { edwin_active_pub, edwin_owner_pub }, { edwin_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, true, { frank_owner_pub }, { frank_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { frank_active_pub }, { frank_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { frank_active_pub, frank_owner_pub }, { frank_active_pub } ) );\n\n      BOOST_CHECK( chk( tx, true, { gavin_owner_pub }, { gavin_owner_pub } ) );\n      BOOST_CHECK( chk( tx, true, { gavin_active_pub }, { gavin_active_pub } ) );\n      BOOST_CHECK( chk( tx, true, { gavin_active_pub, gavin_owner_pub }, { gavin_active_pub } ) );\n\n      sign( tx, alice_owner_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                           true, false );\n      tx.clear_signatures();\n\n      sign( tx, alice_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, bob_owner_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, bob_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, cindy_owner_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, cindy_active_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, daisy_owner_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, daisy_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, edwin_owner_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, edwin_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, frank_owner_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, frank_active_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, gavin_owner_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      sign( tx, gavin_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n      tx.clear_signatures();\n\n      // proposal tests\n      auto new_proposal = [&]() -> proposal_id_type {\n         signed_transaction ptx;\n\n         proposal_create_operation pop;\n         pop.proposed_ops.emplace_back(op);\n         pop.fee_paying_account = bob_id;\n         pop.expiration_time = db.head_block_time() + fc::days(1);\n         ptx.operations.push_back(pop);\n         set_expiration( db, ptx );\n         sign( ptx, bob_active_key );\n\n         return PUSH_TX( db, ptx, database::skip_transaction_dupe_check ).operation_results[0].get<object_id_type>();\n      };\n\n      auto approve_proposal = [&](\n            proposal_id_type proposal,\n            account_id_type account,\n            bool approve_with_owner,\n            fc::ecc::private_key key\n            )\n      {\n         signed_transaction ptx;\n\n         proposal_update_operation pup;\n         pup.fee_paying_account = account;\n         pup.proposal = proposal;\n         if( approve_with_owner )\n            pup.owner_approvals_to_add.insert( account );\n         else\n            pup.active_approvals_to_add.insert( account );\n         ptx.operations.push_back(pup);\n         set_expiration( db, ptx );\n         sign( ptx, key );\n         PUSH_TX( db, ptx, database::skip_transaction_dupe_check );\n      };\n\n      proposal_id_type pid;\n\n      pid = new_proposal();\n      approve_proposal( pid, alice_id, true, alice_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, alice_id, false, alice_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, bob_id, true, bob_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, bob_id, false, bob_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      // Cindy's approval doesn't work\n      pid = new_proposal();\n      approve_proposal( pid, cindy_id, true, cindy_owner_key );\n      BOOST_CHECK( db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, cindy_id, false, cindy_active_key );\n      BOOST_CHECK( db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, daisy_id, true, daisy_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, daisy_id, false, daisy_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, edwin_id, true, edwin_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, edwin_id, false, edwin_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      // Frank's approval doesn't work\n      pid = new_proposal();\n      approve_proposal( pid, frank_id, true, frank_owner_key );\n      BOOST_CHECK( db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, frank_id, false, frank_active_key );\n      BOOST_CHECK( db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, gavin_id, true, gavin_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, gavin_id, false, gavin_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      generate_block( database::skip_transaction_dupe_check );\n\n      // pass the hard fork time\n      generate_blocks( HARDFORK_CORE_584_TIME, true, database::skip_transaction_dupe_check );\n      set_expiration( db, tx );\n\n      // signing tests\n      sign( tx, alice_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, alice_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, bob_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, bob_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, cindy_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, cindy_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, daisy_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, daisy_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, edwin_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, edwin_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, frank_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, frank_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, gavin_owner_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      sign( tx, gavin_active_key );\n      PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      tx.clear_signatures();\n\n      // proposal tests\n      pid = new_proposal();\n      approve_proposal( pid, alice_id, true, alice_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, alice_id, false, alice_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, bob_id, true, bob_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, bob_id, false, bob_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, cindy_id, true, cindy_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, cindy_id, false, cindy_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, daisy_id, true, daisy_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, daisy_id, false, daisy_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, edwin_id, true, edwin_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, edwin_id, false, edwin_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, frank_id, true, frank_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, frank_id, false, frank_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, gavin_id, true, gavin_owner_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      pid = new_proposal();\n      approve_proposal( pid, gavin_id, false, gavin_active_key );\n      BOOST_CHECK( !db.find( pid ) );\n\n      generate_block( database::skip_transaction_dupe_check );\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( custom_operation_required_auths_before_fork ) {\n   try {\n      ACTORS((alice)(bob));\n      fund(alice, asset(10000000));\n      enable_fees();\n\n      // Unable to test custom_operation required auths before fork if hardfork already passed\n      BOOST_REQUIRE(db.head_block_time() < HARDFORK_CORE_210_TIME);\n\n      signed_transaction trx;\n      custom_operation op;\n      op.payer = alice_id;\n      op.required_auths.insert(bob_id);\n      op.fee = op.calculate_fee(db.current_fee_schedule().get<custom_operation>());\n      trx.operations.emplace_back(op);\n      trx.set_expiration(db.head_block_time() + 30);\n      sign(trx, alice_private_key);\n      // Op requires bob's authorization, but only alice signed. We're before the fork, so this should work anyways.\n      db.push_transaction(trx);\n\n      // Now try the same thing, but with a proposal\n      proposal_create_operation pcop;\n      pcop.fee_paying_account = alice_id;\n      pcop.proposed_ops = {op_wrapper(op)};\n      pcop.expiration_time = db.head_block_time() + 10;\n      pcop.fee = pcop.calculate_fee(db.current_fee_schedule().get<proposal_create_operation>());\n      trx.operations = {pcop};\n      trx.signatures.clear();\n      sign(trx, alice_private_key);\n      proposal_id_type pid = db.push_transaction(trx).operation_results[0].get<object_id_type>();\n\n      // Check bob is not listed as a required approver\n      BOOST_REQUIRE_EQUAL(pid(db).required_active_approvals.count(bob_id), 0);\n\n      // Add alice's approval\n      proposal_update_operation puop;\n      puop.fee_paying_account = alice_id;\n      puop.proposal = pid;\n      puop.active_approvals_to_add = {alice_id};\n      puop.fee = puop.calculate_fee(db.current_fee_schedule().get<proposal_update_operation>());\n      trx.operations = {puop};\n      trx.signatures.clear();\n      sign(trx, alice_private_key);\n      db.push_transaction(trx);\n\n      // The proposal should have processed. Check it's not still in the database\n      BOOST_REQUIRE(db.find(pid) == nullptr);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( custom_operation_required_auths_after_fork ) {\n   try {\n      ACTORS((alice)(bob));\n      fund(alice, asset(10000000));\n\n      if (db.head_block_time() < HARDFORK_CORE_210_TIME)\n         generate_blocks(HARDFORK_CORE_210_TIME + 10);\n\n      enable_fees();\n\n      signed_transaction trx;\n      custom_operation op;\n      op.payer = alice_id;\n      op.required_auths.insert(bob_id);\n      op.fee = op.calculate_fee(db.current_fee_schedule().get<custom_operation>());\n      trx.operations.emplace_back(op);\n      trx.set_expiration(db.head_block_time() + 30);\n      sign(trx, alice_private_key);\n      // Op require's bob's authorization, but only alice signed. This should throw.\n      GRAPHENE_REQUIRE_THROW(db.push_transaction(trx), tx_missing_active_auth);\n      sign(trx, bob_private_key);\n      // Now that bob has signed, it should work.\n      PUSH_TX(db, trx);\n\n      // Now try the same thing, but with a proposal\n      proposal_create_operation pcop;\n      pcop.fee_paying_account = alice_id;\n      pcop.proposed_ops = {op_wrapper(op)};\n      pcop.expiration_time = db.head_block_time() + 10;\n      pcop.fee = pcop.calculate_fee(db.current_fee_schedule().get<proposal_create_operation>());\n      trx.operations = {pcop};\n      trx.signatures.clear();\n      sign(trx, alice_private_key);\n      proposal_id_type pid = db.push_transaction(trx).operation_results[0].get<object_id_type>();\n\n      // Check bob is listed as a required approver\n      BOOST_REQUIRE_EQUAL(pid(db).required_active_approvals.count(bob_id), 1);\n\n      // Add alice's approval\n      proposal_update_operation puop;\n      puop.fee_paying_account = alice_id;\n      puop.proposal = pid;\n      puop.active_approvals_to_add = {alice_id};\n      puop.fee = puop.calculate_fee(db.current_fee_schedule().get<proposal_update_operation>());\n      trx.operations = {puop};\n      trx.signatures.clear();\n      sign(trx, alice_private_key);\n      db.push_transaction(trx);\n\n      // The proposal should not have processed without bob's approval.\n      // Check it's still in the database\n      BOOST_REQUIRE_EQUAL(pid(db).required_active_approvals.count(bob_id), 1);\n\n      // Now add bob's approval\n      puop.active_approvals_to_add = {bob_id};\n      trx.operations = {puop};\n      trx.signatures.clear();\n      sign(trx, alice_private_key); // Alice still pays fee\n      sign(trx, bob_private_key);\n      db.push_transaction(trx);\n\n      // Now the proposal should have processed and been removed from the database\n      BOOST_REQUIRE(db.find(pid) == nullptr);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( owner_delegation_test, database_fixture )\n{ try {\n   ACTORS( (alice)(bob) );\n\n   fc::ecc::private_key bob_active_key = fc::ecc::private_key::regenerate(fc::digest(\"bob_active\"));\n   fc::ecc::private_key bob_owner_key  = fc::ecc::private_key::regenerate(fc::digest(\"bob_owner\"));\n\n   trx.clear();\n\n   // Make sure Bob has different keys\n   account_update_operation auo;\n   auo.account = bob_id;\n   auo.active = authority( 1, public_key_type(bob_active_key.get_public_key()), 1 );\n   auo.owner  = authority( 1, public_key_type(bob_owner_key.get_public_key()), 1 );\n   trx.operations.push_back( auo );\n   sign( trx, bob_private_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n\n   // Delegate Alice's owner auth to herself and active auth to Bob\n   auo.account = alice_id;\n   auo.active = authority( 1, bob_id, 1 );\n   auo.owner  = authority( 1, alice_id, 1 );\n   trx.operations.push_back( auo );\n   sign( trx, alice_private_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n\n   // Now Bob has full control over Alice's account\n   auo.account = alice_id;\n   auo.active.reset();\n   auo.owner  = authority( 1, bob_id, 1 );\n   trx.operations.push_back( auo );\n   sign( trx, bob_active_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n} FC_LOG_AND_RETHROW() }\n\n/// This test case reproduces https://github.com/bitshares/bitshares-core/issues/944\n///                       and https://github.com/bitshares/bitshares-core/issues/580\nBOOST_FIXTURE_TEST_CASE( missing_owner_auth_test, database_fixture )\n{\n   try\n   {\n      ACTORS(\n         (alice)\n         );\n\n      auto set_auth = [&](\n         account_id_type aid,\n         const authority& active,\n         const authority& owner\n         )\n      {\n         signed_transaction tx;\n         account_update_operation op;\n         op.account = aid;\n         op.active = active;\n         op.owner = owner;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_transaction_signatures );\n      } ;\n\n      auto get_active = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).active);\n      } ;\n\n      auto get_owner = [&](\n         account_id_type aid\n         ) -> const authority*\n      {\n         return &(aid(db).owner);\n      } ;\n\n      fc::ecc::private_key alice_active_key = fc::ecc::private_key::regenerate(fc::digest(\"alice_active\"));\n      fc::ecc::private_key alice_owner_key = fc::ecc::private_key::regenerate(fc::digest(\"alice_owner\"));\n      public_key_type alice_active_pub( alice_active_key.get_public_key() );\n      public_key_type alice_owner_pub( alice_owner_key.get_public_key() );\n      set_auth( alice_id, authority( 1, alice_active_pub, 1 ), authority( 1, alice_owner_pub, 1 ) );\n\n      // creating a transaction that needs owner permission\n      signed_transaction tx;\n      account_update_operation op;\n      op.account = alice_id;\n      op.owner = authority( 1, alice_active_pub, 1 );\n      tx.operations.push_back( op );\n\n      // not signed, should throw tx_missing_owner_auth\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ),\n                              graphene::chain::tx_missing_owner_auth );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   true, false ),\n                              graphene::chain::tx_missing_owner_auth );\n\n      // signed with alice's active key, should throw tx_missing_owner_auth\n      sign( tx, alice_active_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ),\n                              graphene::chain::tx_missing_owner_auth );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   true, false ),\n                              graphene::chain::tx_missing_owner_auth );\n\n      // signed with alice's owner key, should not throw\n      tx.clear_signatures();\n      sign( tx, alice_owner_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n\n      // signed with both alice's owner key and active key,\n      // it does not throw due to https://github.com/bitshares/bitshares-core/issues/580\n      sign( tx, alice_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n\n      // creating a transaction that needs active permission\n      tx.clear();\n      op.owner.reset();\n      op.active = authority( 1, alice_owner_pub, 1 );\n      tx.operations.push_back( op );\n\n      // not signed, should throw tx_missing_active_auth\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ),\n                              graphene::chain::tx_missing_active_auth );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   true, false ),\n                              graphene::chain::tx_missing_active_auth );\n\n      // signed with alice's active key, should not throw\n      sign( tx, alice_active_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n\n      // signed with alice's owner key, should not throw\n      tx.clear_signatures();\n      sign( tx, alice_owner_key );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), false, false );\n      tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db), true, false );\n\n      // signed with both alice's owner key and active key, should throw tx_irrelevant_sig\n      sign( tx, alice_active_key );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   false, false ),\n                              graphene::chain::tx_irrelevant_sig );\n      GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, make_get_custom(db),\n                                                   true, false ),\n                              graphene::chain::tx_irrelevant_sig );\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( nested_execution )\n{ try {\n   ACTORS( (alice)(bob) );\n   fund( alice );\n\n   generate_blocks( HARDFORK_CORE_214_TIME + fc::hours(1) );\n   set_expiration( db, trx );\n\n   const auto& gpo = db.get_global_properties();\n\n   proposal_create_operation pco;\n   pco.expiration_time = db.head_block_time() + fc::minutes(1);\n   pco.fee_paying_account = alice_id;\n   proposal_id_type inner;\n   {\n      transfer_operation top;\n      top.from = alice_id;\n      top.to = bob_id;\n      top.amount = asset( 10 );\n      pco.proposed_ops.emplace_back( top );\n      trx.operations.push_back( pco );\n      inner = PUSH_TX( db, trx, ~0 ).operation_results.front().get<object_id_type>();\n      trx.clear();\n      pco.proposed_ops.clear();\n   }\n\n   std::vector<proposal_id_type> nested;\n   nested.push_back( inner );\n   for( size_t i = 0; i < gpo.active_witnesses.size() * 2; i++ )\n   {\n      proposal_update_operation pup;\n      pup.fee_paying_account = alice_id;\n      pup.proposal = nested.back();\n      pup.active_approvals_to_add.insert( alice_id );\n      pco.proposed_ops.emplace_back( pup );\n      trx.operations.push_back( pco );\n      nested.push_back( PUSH_TX( db, trx, ~0 ).operation_results.front().get<object_id_type>() );\n      trx.clear();\n      pco.proposed_ops.clear();\n   }\n\n   proposal_update_operation pup;\n   pup.fee_paying_account = alice_id;\n   pup.proposal = nested.back();\n   pup.active_approvals_to_add.insert( alice_id );\n   trx.operations.push_back( pup );\n   PUSH_TX( db, trx, ~0 );\n\n   for( size_t i = 1; i < nested.size(); i++ )\n      BOOST_CHECK_THROW( db.get<proposal_object>( nested[i] ), fc::assert_exception ); // executed successfully -> object removed\n   db.get<proposal_object>( inner ); // wasn't executed -> object exists, doesn't throw\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( issue_214 )\n{ try {\n   ACTORS( (alice)(bob) );\n   fund( alice );\n\n   generate_blocks( HARDFORK_CORE_214_TIME - fc::hours(1) );\n   set_expiration( db, trx );\n\n   // Bob proposes that Alice transfer 500 CORE to himself\n   transfer_operation top;\n   top.from = alice_id;\n   top.to = bob_id;\n   top.amount = asset( 500 );\n   proposal_create_operation pop;\n   pop.proposed_ops.emplace_back(top);\n   pop.fee_paying_account = bob_id;\n   pop.expiration_time = db.head_block_time() + fc::days(1);\n   trx.operations.push_back(pop);\n   sign( trx, bob_private_key );\n   const proposal_id_type pid1 = PUSH_TX( db, trx ).operation_results[0].get<object_id_type>();\n   trx.clear();\n\n   // Bob wants to propose that Alice confirm the first proposal\n   proposal_update_operation pup;\n   pup.fee_paying_account = alice_id;\n   pup.proposal = pid1;\n   pup.active_approvals_to_add.insert( alice_id );\n   pop.proposed_ops.clear();\n   pop.proposed_ops.emplace_back( pup );\n   trx.operations.push_back(pop);\n   sign( trx, bob_private_key );\n   // before HF_CORE_214, Bob can't do that\n   BOOST_REQUIRE_THROW( PUSH_TX( db, trx ), fc::assert_exception );\n   trx.clear_signatures();\n\n   { // Bob can create a proposal nesting the one containing the proposal_update\n      proposal_create_operation npop;\n      npop.proposed_ops.emplace_back(pop);\n      npop.fee_paying_account = bob_id;\n      npop.expiration_time = db.head_block_time() + fc::days(2);\n      signed_transaction ntx;\n      set_expiration( db, ntx );\n      ntx.operations.push_back(npop);\n      sign( ntx, bob_private_key );\n      const proposal_id_type pid1a = PUSH_TX( db, ntx ).operation_results[0].get<object_id_type>();\n      ntx.clear();\n\n      // But execution after confirming it fails\n      proposal_update_operation npup;\n      npup.fee_paying_account = bob_id;\n      npup.proposal = pid1a;\n      npup.active_approvals_to_add.insert( bob_id );\n      ntx.operations.push_back(npup);\n      sign( ntx, bob_private_key );\n      PUSH_TX( db, ntx );\n      ntx.clear();\n\n      db.get<proposal_object>( pid1a ); // still exists\n   }\n\n   generate_blocks( HARDFORK_CORE_214_TIME + fc::hours(1) );\n   set_expiration( db, trx );\n   sign( trx, bob_private_key );\n   // after the HF the previously failed tx works too\n   const proposal_id_type pid2 = PUSH_TX( db, trx ).operation_results[0].get<object_id_type>();\n   trx.clear();\n\n   // For completeness, Alice confirms Bob's second proposal\n   pup.proposal = pid2;\n   trx.operations.push_back(pup);\n   sign( trx, alice_private_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n\n   // Execution of the second proposal should have confirmed the first,\n   // which should have been executed by now.\n   BOOST_CHECK_THROW( db.get<proposal_object>(pid1), fc::assert_exception );\n   BOOST_CHECK_THROW( db.get<proposal_object>(pid2), fc::assert_exception );\n   BOOST_CHECK_EQUAL( top.amount.amount.value, get_balance( bob_id, top.amount.asset_id ) );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( irrelevant_signatures )\n{ try {\n   ACTORS( (alice)(bob) );\n   fund( alice );\n\n   // PK: BTS4vsFgTXJcGQMKCFayF2hrNRfYcKjNZ6Mzk8aw9M4zuWfscPhzE, A: BTSGfxPKKLj6tdTUB7i3mHsd2m7QvPLPy2YA\n   const fc::ecc::private_key test2 = fc::ecc::private_key::regenerate( fc::sha256::hash( std::string( \"test-2\" ) ) );\n   const public_key_type test2_pub( test2.get_public_key() );\n\n   // PK: BTS7FXC7S9UH7HEH8QiuJ8Xv1NRJJZd1GomALLm9ffjtH95Tb2ZQB, A: BTSBajRqmdrXqmDpZhJ8sgkGagdeXneHFVeM\n   const fc::ecc::private_key test3 = fc::ecc::private_key::regenerate( fc::sha256::hash( std::string( \"test-3\" ) ) );\n   const public_key_type test3_pub( test3.get_public_key() );\n\n   BOOST_REQUIRE( test2_pub.key_data < test3_pub.key_data );\n   BOOST_REQUIRE( address( test3_pub ) < address( test2_pub ) );\n\n   account_update_operation auo;\n   auo.account = alice_id;\n   auo.active = authority( 2, test2_pub, 2, test3_pub, 1 );\n\n   trx.clear();\n   set_expiration( db, trx );\n   trx.operations.push_back( auo );\n   sign( trx, alice_private_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n\n   transfer_operation to;\n   to.amount = asset( 1 );\n   to.from = alice_id;\n   to.to = bob_id;\n   trx.operations.push_back( to );\n   sign( trx, test2 );\n   sign( trx, test3 );\n   PUSH_TX( db, trx );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( self_approving_proposal )\n{ try {\n   ACTORS( (alice) );\n   fund( alice );\n\n   generate_blocks( HARDFORK_CORE_1479_TIME );\n   trx.clear();\n   set_expiration( db, trx );\n\n   proposal_update_operation pup;\n   pup.fee_paying_account = alice_id;\n   pup.proposal = proposal_id_type(0);\n   pup.active_approvals_to_add.insert( alice_id );\n\n   proposal_create_operation pop;\n   pop.proposed_ops.emplace_back(pup);\n   pop.fee_paying_account = alice_id;\n   pop.expiration_time = db.head_block_time() + fc::days(1);\n   trx.operations.push_back(pop);\n   const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get<object_id_type>();\n   trx.clear();\n   BOOST_REQUIRE_EQUAL( 0u, pid1.instance.value );\n   db.get<proposal_object>(pid1);\n\n   trx.operations.push_back(pup);\n   PUSH_TX( db, trx, ~0 );\n\n   // Proposal failed and still exists\n   db.get<proposal_object>(pid1);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( self_deleting_proposal )\n{ try {\n   ACTORS( (alice) );\n   fund( alice );\n\n   generate_blocks( HARDFORK_CORE_1479_TIME );\n   trx.clear();\n   set_expiration( db, trx );\n\n   proposal_delete_operation pdo;\n   pdo.fee_paying_account = alice_id;\n   pdo.proposal = proposal_id_type(0);\n   pdo.using_owner_authority = false;\n\n   proposal_create_operation pop;\n   pop.proposed_ops.emplace_back( pdo );\n   pop.fee_paying_account = alice_id;\n   pop.expiration_time = db.head_block_time() + fc::days(1);\n   trx.operations.push_back( pop );\n   const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get<object_id_type>();\n   trx.clear();\n   BOOST_REQUIRE_EQUAL( 0u, pid1.instance.value );\n   db.get<proposal_object>(pid1);\n\n   proposal_update_operation pup;\n   pup.fee_paying_account = alice_id;\n   pup.proposal = proposal_id_type(0);\n   pup.active_approvals_to_add.insert( alice_id );\n   trx.operations.push_back(pup);\n   PUSH_TX( db, trx, ~0 );\n\n   // Proposal failed and still exists\n   db.get<proposal_object>(pid1);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/basic_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/exceptions.hpp>\n\n#include <graphene/db/simple_index.hpp>\n\n#include <fc/crypto/digest.hpp>\n#include <fc/crypto/hex.hpp>\n#include \"../common/database_fixture.hpp\"\n\n#include <algorithm>\n#include <random>\n\nusing namespace graphene::chain;\nusing namespace graphene::db;\n\nBOOST_FIXTURE_TEST_SUITE( basic_tests, database_fixture )\n\n/**\n * Verify that names are RFC-1035 compliant https://tools.ietf.org/html/rfc1035\n * https://github.com/cryptonomex/graphene/issues/15\n */\nBOOST_AUTO_TEST_CASE( valid_name_test )\n{\n   BOOST_CHECK( is_valid_name( \"a\" ) );\n   BOOST_CHECK( !is_valid_name( \"A\" ) );\n   BOOST_CHECK( !is_valid_name( \"0\" ) );\n   BOOST_CHECK( !is_valid_name( \".\" ) );\n   BOOST_CHECK( !is_valid_name( \"-\" ) );\n\n   BOOST_CHECK( is_valid_name( \"aa\" ) );\n   BOOST_CHECK( !is_valid_name( \"aA\" ) );\n   BOOST_CHECK( is_valid_name( \"a0\" ) );\n   BOOST_CHECK( !is_valid_name( \"a.\" ) );\n   BOOST_CHECK( !is_valid_name( \"a-\" ) );\n\n   BOOST_CHECK( is_valid_name( \"aaa\" ) );\n   BOOST_CHECK( !is_valid_name( \"aAa\" ) );\n   BOOST_CHECK( is_valid_name( \"a0a\" ) );\n   BOOST_CHECK( is_valid_name( \"a.a\" ) );\n   BOOST_CHECK( is_valid_name( \"a-a\" ) );\n\n   BOOST_CHECK( is_valid_name( \"aa0\" ) );\n   BOOST_CHECK( !is_valid_name( \"aA0\" ) );\n   BOOST_CHECK( is_valid_name( \"a00\" ) );\n   BOOST_CHECK( !is_valid_name( \"a.0\" ) );\n   BOOST_CHECK( is_valid_name( \"a-0\" ) );\n\n   BOOST_CHECK(  is_valid_name( \"aaa-bbb-ccc\" ) );\n   BOOST_CHECK(  is_valid_name( \"aaa-bbb.ccc\" ) );\n\n   BOOST_CHECK( !is_valid_name( \"aaa,bbb-ccc\" ) );\n   BOOST_CHECK( !is_valid_name( \"aaa_bbb-ccc\" ) );\n   BOOST_CHECK( !is_valid_name( \"aaa-BBB-ccc\" ) );\n\n   BOOST_CHECK( !is_valid_name( \"1aaa-bbb\" ) );\n   BOOST_CHECK( !is_valid_name( \"-aaa-bbb-ccc\" ) );\n   BOOST_CHECK( !is_valid_name( \".aaa-bbb-ccc\" ) );\n   BOOST_CHECK( !is_valid_name( \"/aaa-bbb-ccc\" ) );\n\n   BOOST_CHECK( !is_valid_name( \"aaa-bbb-ccc-\" ) );\n   BOOST_CHECK( !is_valid_name( \"aaa-bbb-ccc.\" ) );\n   BOOST_CHECK( !is_valid_name( \"aaa-bbb-ccc..\" ) );\n   BOOST_CHECK( !is_valid_name( \"aaa-bbb-ccc/\" ) );\n\n   BOOST_CHECK( !is_valid_name( \"aaa..bbb-ccc\" ) );\n   BOOST_CHECK( is_valid_name( \"aaa.bbb-ccc\" ) );\n   BOOST_CHECK( is_valid_name( \"aaa.bbb.ccc\" ) );\n\n   BOOST_CHECK(  is_valid_name( \"aaa--bbb--ccc\" ) );\n   BOOST_CHECK(  is_valid_name( \"xn--sandmnnchen-p8a.de\" ) );\n   BOOST_CHECK(  is_valid_name( \"xn--sandmnnchen-p8a.dex\" ) );\n   BOOST_CHECK(  is_valid_name( \"xn-sandmnnchen-p8a.de\" ) );\n   BOOST_CHECK(  is_valid_name( \"xn-sandmnnchen-p8a.dex\" ) );\n\n   BOOST_CHECK(  is_valid_name( \"this-label-has-less-than-64-char.acters-63-to-be-really-precise\" ) );\n   BOOST_CHECK( !is_valid_name( \"this-label-has-more-than-63-char.act.ers-64-to-be-really-precise\" ) );\n   BOOST_CHECK( !is_valid_name( \"none.of.these.labels.has.more.than-63.chars--but.still.not.valid\" ) );\n}\n\nBOOST_AUTO_TEST_CASE( valid_symbol_test )\n{\n   BOOST_CHECK( !is_valid_symbol( \"A\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"a\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"0\" ) );\n   BOOST_CHECK( !is_valid_symbol( \".\" ) );\n\n   BOOST_CHECK( !is_valid_symbol( \"AA\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"Aa\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"A0\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"A.\" ) );\n\n   BOOST_CHECK( is_valid_symbol( \"AAA\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"AaA\" ) );\n   BOOST_CHECK( is_valid_symbol( \"A0A\" ) );\n   BOOST_CHECK( is_valid_symbol( \"A.A\" ) );\n\n   BOOST_CHECK( !is_valid_symbol( \"A..A\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"A.A.\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"A.A.A\" ) );\n\n   BOOST_CHECK( is_valid_symbol( \"AAAAAAAAAAAAAAAA\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"AAAAAAAAAAAAAAAAA\" ) );\n   BOOST_CHECK( is_valid_symbol( \"A.AAAAAAAAAAAAAA\" ) );\n   BOOST_CHECK( !is_valid_symbol( \"A.AAAAAAAAAAAA.A\" ) );\n\n   BOOST_CHECK( is_valid_symbol( \"AAA000AAA\" ) );\n}\n\nBOOST_AUTO_TEST_CASE( price_test )\n{\n    auto price_max = []( uint32_t a, uint32_t b )\n    {   return price::max( asset_id_type(a), asset_id_type(b) );   };\n    auto price_min = []( uint32_t a, uint32_t b )\n    {   return price::min( asset_id_type(a), asset_id_type(b) );   };\n\n    BOOST_CHECK( price_max(0,1) > price_min(0,1) );\n    BOOST_CHECK( price_max(1,0) > price_min(1,0) );\n    BOOST_CHECK( price_max(0,1) >= price_min(0,1) );\n    BOOST_CHECK( price_max(1,0) >= price_min(1,0) );\n    BOOST_CHECK( price_max(0,1) >= price_max(0,1) );\n    BOOST_CHECK( price_max(1,0) >= price_max(1,0) );\n    BOOST_CHECK( price_min(0,1) < price_max(0,1) );\n    BOOST_CHECK( price_min(1,0) < price_max(1,0) );\n    BOOST_CHECK( price_min(0,1) <= price_max(0,1) );\n    BOOST_CHECK( price_min(1,0) <= price_max(1,0) );\n    BOOST_CHECK( price_min(0,1) <= price_min(0,1) );\n    BOOST_CHECK( price_min(1,0) <= price_min(1,0) );\n    BOOST_CHECK( price_min(1,0) != price_max(1,0) );\n    BOOST_CHECK( ~price_max(0,1) != price_min(0,1) );\n    BOOST_CHECK( ~price_min(0,1) != price_max(0,1) );\n    BOOST_CHECK( ~price_max(0,1) == price_min(1,0) );\n    BOOST_CHECK( ~price_min(0,1) == price_max(1,0) );\n    BOOST_CHECK( ~price_max(0,1) < ~price_min(0,1) );\n    BOOST_CHECK( ~price_max(0,1) <= ~price_min(0,1) );\n    price a(asset(1), asset(2,asset_id_type(1)));\n    price b(asset(2), asset(2,asset_id_type(1)));\n    price c(asset(1), asset(2,asset_id_type(1)));\n    BOOST_CHECK(a < b);\n    BOOST_CHECK(b > a);\n    BOOST_CHECK(a == c);\n    BOOST_CHECK(!(b == c));\n\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1)) * ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(0),  asset(1, asset_id_type(1))) * ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(-1), asset(1, asset_id_type(1))) * ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(0, asset_id_type(1))) * ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(-1, asset_id_type(1))) * ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) * ratio_type(0,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) * ratio_type(-1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) * ratio_type(1,0), std::domain_error ); // zero denominator\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) * ratio_type(1,-1), fc::exception );\n\n    GRAPHENE_REQUIRE_THROW( price(asset(0),  asset(1, asset_id_type(1))) / ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(-1), asset(1, asset_id_type(1))) / ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(0, asset_id_type(1))) / ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(-1, asset_id_type(1))) / ratio_type(1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) / ratio_type(0,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) / ratio_type(-1,1), fc::exception );\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) / ratio_type(1,0), std::domain_error ); // zero denominator\n    GRAPHENE_REQUIRE_THROW( price(asset(1),  asset(1, asset_id_type(1))) / ratio_type(1,-1), fc::exception );\n\n    BOOST_CHECK( price(asset(1), asset(1, asset_id_type(1))) * ratio_type(1,1) == price(asset(1), asset(1, asset_id_type(1))) );\n    BOOST_CHECK( price(asset(3), asset(2, asset_id_type(1))) * ratio_type(80,100) == price(asset(12), asset(10, asset_id_type(1))) );\n    BOOST_CHECK( price(asset(3), asset(2, asset_id_type(1))) * ratio_type(120,100) == price(asset(9), asset(5, asset_id_type(1))) );\n\n    BOOST_CHECK( price(asset(1), asset(1, asset_id_type(1))) / ratio_type(1,1) == price(asset(1), asset(1, asset_id_type(1))) );\n    BOOST_CHECK( price(asset(3), asset(2, asset_id_type(1))) / ratio_type(80,100) == price(asset(15), asset(8, asset_id_type(1))) );\n    BOOST_CHECK( price(asset(3), asset(2, asset_id_type(1))) / ratio_type(120,100) == price(asset(30), asset(24, asset_id_type(1))) );\n\n    BOOST_CHECK( price_max(0,1) * ratio_type(2,1) == price_max(0,1) );\n    BOOST_CHECK( price_max(0,1) * ratio_type(125317293,125317292) == price_max(0,1) );\n    BOOST_CHECK( price_max(0,1) * ratio_type(125317293,105317292) == price_max(0,1) );\n    BOOST_CHECK( price_max(0,1) * ratio_type(125317293,25317292) == price_max(0,1) );\n    BOOST_CHECK( price_min(0,1) * ratio_type(1,2) == price_min(0,1) );\n    BOOST_CHECK( price_min(0,1) * ratio_type(98752395,98752396) == price_min(0,1) );\n    BOOST_CHECK( price_min(0,1) * ratio_type(70000000,99999999) == price_min(0,1) );\n    BOOST_CHECK( price_min(0,1) * ratio_type(30000000,99999999) == price_min(0,1) );\n\n    price more_than_max = price_max(0,1);\n    more_than_max.base.amount *= 5;\n    more_than_max.quote.amount *= 3;\n    BOOST_CHECK( more_than_max * ratio_type(125317293,125317292) == more_than_max );\n    BOOST_CHECK( more_than_max * ratio_type(125317293,125317293) == more_than_max );\n    BOOST_CHECK( more_than_max * ratio_type(125317293,125317294) == price_max(0,1) );\n\n    price less_than_min = price_min(0,1);\n    less_than_min.base.amount *= 19;\n    less_than_min.quote.amount *= 47;\n    BOOST_CHECK( less_than_min * ratio_type(125317293,125317292) == price_min(0,1) );\n    BOOST_CHECK( less_than_min * ratio_type(125317293,125317293) == less_than_min );\n    BOOST_CHECK( less_than_min * ratio_type(125317293,125317294) == less_than_min );\n\n    price less_than_max = price_max(0,1);\n    less_than_max.quote.amount = 11;\n    BOOST_CHECK( less_than_max * ratio_type(7,1) == price(asset(less_than_max.base.amount*7/11),asset(1,asset_id_type(1))) );\n    less_than_max.quote.amount = 92131419;\n    BOOST_CHECK( less_than_max * ratio_type(7,1) == price(asset(less_than_max.base.amount*7/92131419),asset(1,asset_id_type(1))) );\n    less_than_max.quote.amount = 192131419;\n    BOOST_CHECK( less_than_max * ratio_type(7,1) == price(asset(less_than_max.base.amount.value*7>>3),asset(192131419>>3,asset_id_type(1))) );\n\n    price more_than_min = price_min(0,1);\n    more_than_min.base.amount = 11;\n    BOOST_CHECK( more_than_min * ratio_type(1,7) == price(asset(1),asset(more_than_min.quote.amount*7/11,asset_id_type(1))) );\n    more_than_min.base.amount = 64823;\n    BOOST_CHECK( more_than_min * ratio_type(31672,102472047) == price(asset(1),asset(static_cast<uint64_t>(fc::uint128_t(more_than_min.quote.amount.value)*102472047/(64823*31672)),asset_id_type(1))) );\n    more_than_min.base.amount = 13;\n    BOOST_CHECK( more_than_min * ratio_type(202472059,3) == price(asset((int64_t(13)*202472059)>>1),asset((more_than_min.quote.amount.value*3)>>1,asset_id_type(1))) ); // after >>1, quote = max*1.5, but gcd = 3, so quote/=3 = max/2, less than max\n\n    price less_than_max2 = price_max(0,1);\n    less_than_max2.base.amount *= 2;\n    less_than_max2.quote.amount *= 7;\n    BOOST_CHECK( less_than_max2 * ratio_type(1,1) == less_than_max2 );\n    BOOST_CHECK( less_than_max2 * ratio_type(5,2) == price(asset(less_than_max2.base.amount*5/2/7),asset(1,asset_id_type(1))) );\n\n    BOOST_CHECK( ( asset(1) * price( asset(1), asset(1, asset_id_type(1)) ) ) == asset(1, asset_id_type(1)) );\n    BOOST_CHECK( ( asset(1) * price( asset(1, asset_id_type(1)), asset(1) ) ) == asset(1, asset_id_type(1)) );\n    BOOST_CHECK( ( asset(1, asset_id_type(1)) * price( asset(1), asset(1, asset_id_type(1)) ) ) == asset(1) );\n    BOOST_CHECK( ( asset(1, asset_id_type(1)) * price( asset(1, asset_id_type(1)), asset(1) ) ) == asset(1) );\n\n    BOOST_CHECK( ( asset(3) * price( asset(3), asset(5, asset_id_type(1)) ) ) == asset(5, asset_id_type(1)) ); // round_down(3*5/3)\n    BOOST_CHECK( ( asset(5) * price( asset(2, asset_id_type(1)), asset(7) ) ) == asset(1, asset_id_type(1)) ); // round_down(5*2/7)\n    BOOST_CHECK( ( asset(7, asset_id_type(1)) * price( asset(2), asset(3, asset_id_type(1)) ) ) == asset(4) ); // round_down(7*2/3)\n    BOOST_CHECK( ( asset(9, asset_id_type(1)) * price( asset(8, asset_id_type(1)), asset(7) ) ) == asset(7) ); // round_down(9*7/8)\n\n    // asset and price doesn't match\n    BOOST_CHECK_THROW( asset(1) * price( asset(1, asset_id_type(2)), asset(1, asset_id_type(1)) ), fc::assert_exception );\n    // divide by zero\n    BOOST_CHECK_THROW( asset(1) * price( asset(0), asset(1, asset_id_type(1)) ), fc::assert_exception );\n    BOOST_CHECK_THROW( asset(1) * price( asset(1, asset_id_type(1)), asset(0) ), fc::assert_exception );\n    // overflow\n    BOOST_CHECK_THROW( asset(GRAPHENE_MAX_SHARE_SUPPLY/2+1) * price( asset(1), asset(2, asset_id_type(1)) ), fc::assert_exception );\n    BOOST_CHECK_THROW( asset(2) * price( asset(GRAPHENE_MAX_SHARE_SUPPLY/2+1, asset_id_type(1)), asset(1) ), fc::assert_exception );\n\n    BOOST_CHECK( asset(1).multiply_and_round_up( price( asset(1), asset(1, asset_id_type(1)) ) ) == asset(1, asset_id_type(1)) );\n    BOOST_CHECK( asset(1).multiply_and_round_up( price( asset(1, asset_id_type(1)), asset(1) ) ) == asset(1, asset_id_type(1)) );\n    BOOST_CHECK( asset(1, asset_id_type(1)).multiply_and_round_up( price( asset(1), asset(1, asset_id_type(1)) ) ) == asset(1) );\n    BOOST_CHECK( asset(1, asset_id_type(1)).multiply_and_round_up( price( asset(1, asset_id_type(1)), asset(1) ) ) == asset(1) );\n\n    // round_up(3*5/3)\n    BOOST_CHECK( asset(3).multiply_and_round_up( price( asset(3), asset(5, asset_id_type(1)) ) ) == asset(5, asset_id_type(1)) );\n    // round_up(5*2/7)\n    BOOST_CHECK( asset(5).multiply_and_round_up( price( asset(2, asset_id_type(1)), asset(7) ) ) == asset(2, asset_id_type(1)) );\n    // round_up(7*2/3)\n    BOOST_CHECK( asset(7, asset_id_type(1)).multiply_and_round_up( price( asset(2), asset(3, asset_id_type(1)) ) ) == asset(5) );\n    // round_up(9*7/8)\n    BOOST_CHECK( asset(9, asset_id_type(1)).multiply_and_round_up( price( asset(8, asset_id_type(1)), asset(7) ) ) == asset(8) );\n\n    // asset and price doesn't match\n    BOOST_CHECK_THROW( asset(1, asset_id_type(3)).multiply_and_round_up( price( asset(1, asset_id_type(2)), asset(1) ) ),\n                       fc::assert_exception );\n    // divide by zero\n    BOOST_CHECK_THROW( asset(1).multiply_and_round_up( price( asset(0), asset(1, asset_id_type(1)) ) ), fc::assert_exception );\n    BOOST_CHECK_THROW( asset(1).multiply_and_round_up( price( asset(1, asset_id_type(1)), asset(0) ) ), fc::assert_exception );\n    // overflow\n    BOOST_CHECK_THROW( asset(GRAPHENE_MAX_SHARE_SUPPLY/2+1).multiply_and_round_up( price( asset(1), asset(2, asset_id_type(1)) ) ),\n                       fc::assert_exception );\n    BOOST_CHECK_THROW( asset(2).multiply_and_round_up( price( asset(GRAPHENE_MAX_SHARE_SUPPLY/2+1, asset_id_type(1)), asset(1) ) ),\n                       fc::assert_exception );\n\n    price_feed dummy;\n    dummy.maintenance_collateral_ratio = 1002;\n    dummy.maximum_short_squeeze_ratio = 1234;\n    dummy.settlement_price = price(asset(1000), asset(2000, asset_id_type(1)));\n    price_feed dummy2 = dummy;\n    price_feed dummy3 = dummy;\n    dummy3.core_exchange_rate = price( asset(11), asset(13, asset_id_type(1)) );\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy ) );\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy2 ) );\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy3 ) );\n    dummy.maximum_short_squeeze_ratio = 1235;\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy ) );\n    BOOST_CHECK( !dummy.margin_call_params_equal( dummy2 ) );\n    BOOST_CHECK( !dummy.margin_call_params_equal( dummy3 ) );\n    dummy2.maximum_short_squeeze_ratio = 1235;\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy ) );\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy2 ) );\n    BOOST_CHECK( !dummy.margin_call_params_equal( dummy3 ) );\n    dummy2.maintenance_collateral_ratio = 1003;\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy ) );\n    BOOST_CHECK( !dummy.margin_call_params_equal( dummy2 ) );\n    BOOST_CHECK( !dummy.margin_call_params_equal( dummy3 ) );\n    dummy3.maximum_short_squeeze_ratio = 1235;\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy3 ) );\n    dummy3.settlement_price = price( asset(1), asset(3, asset_id_type(1)) );\n    BOOST_CHECK( !dummy.margin_call_params_equal( dummy3 ) );\n    dummy3.settlement_price = price( asset(1), asset(2, asset_id_type(1)) );\n    BOOST_CHECK( dummy.margin_call_params_equal( dummy3 ) );\n}\n\nBOOST_AUTO_TEST_CASE( price_multiplication_test )\n{ try {\n   // random test\n   std::mt19937_64 gen( time(NULL) );\n   std::uniform_int_distribution<int64_t> amt_uid(1, GRAPHENE_MAX_SHARE_SUPPLY);\n   std::uniform_int_distribution<int64_t> amt_uid2(1, 1000*1000*1000);\n   std::uniform_int_distribution<int64_t> amt_uid3(1, 1000*1000);\n   std::uniform_int_distribution<int64_t> amt_uid4(1, 1000);\n   asset a;\n   price p;\n   for( int i = 1*1000*1000; i > 0; --i )\n   {\n      if( i <= 30 )\n         a = asset( 0 );\n      else if( i % 4 == 0 )\n         a = asset( amt_uid(gen) );\n      else if( i % 4 == 1 )\n         a = asset( amt_uid2(gen) );\n      else if( i % 4 == 2 )\n         a = asset( amt_uid3(gen) );\n      else // if( i % 4 == 3 )\n         a = asset( amt_uid4(gen) );\n\n      if( i % 7 == 0 )\n         p = price( asset(amt_uid(gen)), asset(amt_uid(gen), asset_id_type(1)) );\n      else if( i % 7 == 1 )\n         p = price( asset(amt_uid2(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n      else if( i % 7 == 2 )\n         p = price( asset(amt_uid3(gen)), asset(amt_uid3(gen), asset_id_type(1)) );\n      else if( i % 7 == 3 )\n         p = price( asset(amt_uid4(gen)), asset(amt_uid4(gen), asset_id_type(1)) );\n      else if( i % 7 == 4 )\n         p = price( asset(amt_uid(gen)), asset(amt_uid(gen), asset_id_type(1)) );\n      else if( i % 7 == 5 )\n         p = price( asset(amt_uid4(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n      else // if( i % 7 == 6 )\n         p = price( asset(amt_uid2(gen)), asset(amt_uid4(gen), asset_id_type(1)) );\n\n      try\n      {\n         asset b = a * p;\n         asset a1 = b.multiply_and_round_up( p );\n         BOOST_CHECK( a1 <= a );\n         BOOST_CHECK( (a1 * p) == b );\n\n         b = a.multiply_and_round_up( p );\n         a1 = b * p;\n         BOOST_CHECK( a1 >= a );\n         BOOST_CHECK( a1.multiply_and_round_up( p ) == b );\n      }\n      catch( fc::assert_exception& e )\n      {\n         BOOST_CHECK( e.to_detail_string().find( \"result <= GRAPHENE_MAX_SHARE_SUPPLY\" ) != string::npos );\n      }\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( memo_test )\n{ try {\n   memo_data m;\n   auto sender = generate_private_key(\"1\");\n   auto receiver = generate_private_key(\"2\");\n   m.from = sender.get_public_key();\n   m.to = receiver.get_public_key();\n   m.set_message(sender, receiver.get_public_key(), \"Hello, world!\", 12345);\n\n   decltype(fc::digest(m)) hash(\"8de72a07d093a589f574460deb19023b4aff354b561eb34590d9f4629f51dbf3\");\n   if( fc::digest(m) != hash )\n   {\n      // If this happens, notify the web guys that the memo serialization format changed.\n      edump((m)(fc::digest(m)));\n      BOOST_FAIL(\"Memo format has changed. Notify the web guys and update this test.\");\n   }\n   BOOST_CHECK_EQUAL(m.get_message(receiver, sender.get_public_key()), \"Hello, world!\");\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( exceptions )\n{\n   GRAPHENE_CHECK_THROW(FC_THROW_EXCEPTION(balance_claim_invalid_claim_amount, \"Etc\"), balance_claim_invalid_claim_amount);\n}\n\nBOOST_AUTO_TEST_CASE( scaled_precision )\n{\n   const int64_t _k = 1000;\n   const int64_t _m = _k*_k;\n   const int64_t _g = _m*_k;\n   const int64_t _t = _g*_k;\n   const int64_t _p = _t*_k;\n   const int64_t _e = _p*_k;\n\n   BOOST_CHECK( asset::scaled_precision( 0) == share_type(   1   ) );\n   BOOST_CHECK( asset::scaled_precision( 1) == share_type(  10   ) );\n   BOOST_CHECK( asset::scaled_precision( 2) == share_type( 100   ) );\n   BOOST_CHECK( asset::scaled_precision( 3) == share_type(   1*_k) );\n   BOOST_CHECK( asset::scaled_precision( 4) == share_type(  10*_k) );\n   BOOST_CHECK( asset::scaled_precision( 5) == share_type( 100*_k) );\n   BOOST_CHECK( asset::scaled_precision( 6) == share_type(   1*_m) );\n   BOOST_CHECK( asset::scaled_precision( 7) == share_type(  10*_m) );\n   BOOST_CHECK( asset::scaled_precision( 8) == share_type( 100*_m) );\n   BOOST_CHECK( asset::scaled_precision( 9) == share_type(   1*_g) );\n   BOOST_CHECK( asset::scaled_precision(10) == share_type(  10*_g) );\n   BOOST_CHECK( asset::scaled_precision(11) == share_type( 100*_g) );\n   BOOST_CHECK( asset::scaled_precision(12) == share_type(   1*_t) );\n   BOOST_CHECK( asset::scaled_precision(13) == share_type(  10*_t) );\n   BOOST_CHECK( asset::scaled_precision(14) == share_type( 100*_t) );\n   BOOST_CHECK( asset::scaled_precision(15) == share_type(   1*_p) );\n   BOOST_CHECK( asset::scaled_precision(16) == share_type(  10*_p) );\n   BOOST_CHECK( asset::scaled_precision(17) == share_type( 100*_p) );\n   BOOST_CHECK( asset::scaled_precision(18) == share_type(   1*_e) );\n   GRAPHENE_CHECK_THROW( asset::scaled_precision(19), fc::exception );\n}\n\nBOOST_AUTO_TEST_CASE( merkle_root )\n{\n   clearable_block block;\n   vector<processed_transaction> tx;\n   vector<digest_type> t;\n   const uint32_t num_tx = 10;\n\n   for( uint32_t i=0; i<num_tx; i++ )\n   {\n      tx.emplace_back();\n      tx.back().ref_block_prefix = i;\n      t.push_back( tx.back().merkle_digest() );\n   }\n\n   auto c = []( const digest_type& digest ) -> checksum_type\n   {   return checksum_type::hash( digest );   };\n   \n   auto d = []( const digest_type& left, const digest_type& right ) -> digest_type\n   {   return digest_type::hash( std::make_pair( left, right ) );   };\n\n   BOOST_CHECK( block.calculate_merkle_root() == checksum_type() );\n\n   block.transactions.push_back( tx[0] );\n   BOOST_CHECK( block.calculate_merkle_root() ==\n      c(t[0])\n      );\n\n   digest_type dA, dB, dC, dD, dE, dI, dJ, dK, dM, dN, dO;\n\n   /*\n      A=d(0,1)\n         / \\ \n        0   1\n   */\n\n   dA = d(t[0], t[1]);\n\n   block.transactions.push_back( tx[1] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dA) );\n\n   /*\n            I=d(A,B)\n           /        \\\n      A=d(0,1)      B=2\n         / \\        /\n        0   1      2\n   */\n\n   dB = t[2];\n   dI = d(dA, dB);\n\n   block.transactions.push_back( tx[2] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dI) );\n\n   /*\n          I=d(A,B)\n           /    \\\n      A=d(0,1)   B=d(2,3)\n         / \\    /   \\\n        0   1  2     3\n   */\n\n   dB = d(t[2], t[3]);\n   dI = d(dA, dB);\n\n   block.transactions.push_back( tx[3] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dI) );\n\n   /*\n                     __M=d(I,J)__\n                    /            \\\n            I=d(A,B)              J=C\n           /        \\            /\n      A=d(0,1)   B=d(2,3)      C=4\n         / \\        / \\        /\n        0   1      2   3      4\n   */\n\n   dC = t[4];\n   dJ = dC;\n   dM = d(dI, dJ);\n\n   block.transactions.push_back( tx[4] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dM) );\n\n   /*\n                     __M=d(I,J)__\n                    /            \\\n            I=d(A,B)              J=C\n           /        \\            /\n      A=d(0,1)   B=d(2,3)   C=d(4,5)\n         / \\        / \\        / \\\n        0   1      2   3      4   5\n   */\n\n   dC = d(t[4], t[5]);\n   dJ = dC;\n   dM = d(dI, dJ);\n\n   block.transactions.push_back( tx[5] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dM) );\n\n   /*\n                     __M=d(I,J)__\n                    /            \\\n            I=d(A,B)              J=d(C,D)\n           /        \\            /        \\\n      A=d(0,1)   B=d(2,3)   C=d(4,5)      D=6\n         / \\        / \\        / \\        /\n        0   1      2   3      4   5      6\n   */\n\n   dD = t[6];\n   dJ = d(dC, dD);\n   dM = d(dI, dJ);\n\n   block.transactions.push_back( tx[6] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dM) );\n\n   /*\n                     __M=d(I,J)__\n                    /            \\\n            I=d(A,B)              J=d(C,D)\n           /        \\            /        \\\n      A=d(0,1)   B=d(2,3)   C=d(4,5)   D=d(6,7)\n         / \\        / \\        / \\        / \\\n        0   1      2   3      4   5      6   7\n   */\n\n   dD = d(t[6], t[7]);\n   dJ = d(dC, dD);\n   dM = d(dI, dJ);\n\n   block.transactions.push_back( tx[7] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dM) );\n\n   /*\n                                _____________O=d(M,N)______________\n                               /                                   \\   \n                     __M=d(I,J)__                                  N=K\n                    /            \\                              /\n            I=d(A,B)              J=d(C,D)                 K=E\n           /        \\            /        \\            /\n      A=d(0,1)   B=d(2,3)   C=d(4,5)   D=d(6,7)      E=8\n         / \\        / \\        / \\        / \\        /\n        0   1      2   3      4   5      6   7      8\n   */\n\n   dE = t[8];\n   dK = dE;\n   dN = dK;\n   dO = d(dM, dN);\n\n   block.transactions.push_back( tx[8] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dO) );\n\n   /*\n                                _____________O=d(M,N)______________\n                               /                                   \\   \n                     __M=d(I,J)__                                  N=K\n                    /            \\                              /\n            I=d(A,B)              J=d(C,D)                 K=E\n           /        \\            /        \\            /\n      A=d(0,1)   B=d(2,3)   C=d(4,5)   D=d(6,7)   E=d(8,9)\n         / \\        / \\        / \\        / \\        / \\\n        0   1      2   3      4   5      6   7      8   9\n   */\n\n   dE = d(t[8], t[9]);\n   dK = dE;\n   dN = dK;\n   dO = d(dM, dN);\n\n   block.transactions.push_back( tx[9] );\n   block.clear();\n   BOOST_CHECK( block.calculate_merkle_root() == c(dO) );\n}\n\n/**\n * Reproduces https://github.com/bitshares/bitshares-core/issues/888 and tests fix for it.\n */\nBOOST_AUTO_TEST_CASE( bitasset_feed_expiration_test )\n{\n   time_point_sec now = fc::time_point::now();\n\n   asset_bitasset_data_object o;\n\n   o.current_feed_publication_time = now - fc::hours(1);\n   o.options.feed_lifetime_sec = std::numeric_limits<uint32_t>::max() - 1;\n\n   BOOST_CHECK( !o.feed_is_expired( now ) );\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/bitasset_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Bitshares Foundation, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <vector>\n#include <boost/test/unit_test.hpp>\n\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/budget_record_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n#include <graphene/chain/asset_evaluator.hpp>\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <fc/crypto/digest.hpp>\n#include <fc/log/log_message.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( bitasset_tests, database_fixture )\n\n/*****\n * @brief helper method to change a backing asset to a new one\n * @param fixture the database_fixture\n * @param signing_key the signer\n * @param asset_id_to_update asset to update\n * @param new_backing_asset_id the new backing asset\n */\nvoid change_backing_asset(database_fixture& fixture, const fc::ecc::private_key& signing_key,\n      asset_id_type asset_id_to_update, asset_id_type new_backing_asset_id)\n{\n   try\n   {\n      asset_update_bitasset_operation ba_op;\n      const asset_object& asset_to_update = asset_id_to_update(fixture.db);\n      ba_op.asset_to_update = asset_id_to_update;\n      ba_op.issuer = asset_to_update.issuer;\n      ba_op.new_options.short_backing_asset = new_backing_asset_id;\n      fixture.trx.operations.push_back(ba_op);\n      fixture.sign(fixture.trx, signing_key);\n      PUSH_TX(fixture.db, fixture.trx, ~0);\n      fixture.generate_block();\n      fixture.trx.clear();\n   }\n   catch (fc::exception& ex)\n   {\n      BOOST_FAIL( \"Exception thrown in change_backing_asset. Exception was: \" +\n            ex.to_string(fc::log_level(fc::log_level::all)) );\n   }\n}\n\n/******\n * @brief helper method to turn witness_fed_asset on and off\n * @param fixture the database_fixture\n * @param new_issuer optionally change the issuer\n * @param signing_key signer\n * @param asset_id asset we want to change\n * @param witness_fed true if you want this to be a witness fed asset\n */\nvoid change_asset_options(database_fixture& fixture, const optional<account_id_type>& new_issuer,\n      const fc::ecc::private_key& signing_key,\n      asset_id_type asset_id, bool witness_fed)\n{\n   asset_update_operation op;\n   const asset_object& obj = asset_id(fixture.db);\n   op.asset_to_update = asset_id;\n   op.issuer = obj.issuer;\n   if (new_issuer)\n      op.new_issuer = new_issuer;\n   op.new_options = obj.options;\n   if (witness_fed)\n   {\n      op.new_options.flags |= witness_fed_asset;\n      op.new_options.flags &= ~committee_fed_asset;\n   }\n   else\n   {\n      op.new_options.flags &= ~witness_fed_asset; // we don't care about the committee flag here\n   }\n   fixture.trx.operations.push_back(op);\n   fixture.sign( fixture.trx, signing_key );\n   PUSH_TX( fixture.db, fixture.trx, ~0 );\n   fixture.generate_block();\n   fixture.trx.clear();\n\n}\n\n/*********\n * @brief helper method to create a coin backed by a bitasset\n * @param fixture the database_fixture\n * @param index added to name of the coin\n * @param backing the backing asset\n * @param signing_key the signing key\n */\nconst graphene::chain::asset_object& create_bitasset_backed(graphene::chain::database_fixture& fixture,\n      int index, graphene::chain::asset_id_type backing, const fc::ecc::private_key& signing_key)\n{\n   // create the coin\n   std::string name = \"COIN\" + std::to_string(index + 1) + \"TEST\";\n   const graphene::chain::asset_object& obj = fixture.create_bitasset(name);\n   asset_id_type asset_id = obj.get_id();\n   // adjust the backing asset\n   change_backing_asset(fixture, signing_key, asset_id, backing);\n   fixture.trx.set_expiration(fixture.db.get_dynamic_global_properties().next_maintenance_time);\n   return obj;\n}\n\n\n/*********\n * @brief make sure feeds still work after changing backing asset on a witness-fed asset\n */\nBOOST_AUTO_TEST_CASE( reset_backing_asset_on_witness_asset )\n{\n   ACTORS((nathan));\n\n   /*\n       // do a maintenance block\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      // generate blocks until close to hard fork\n      generate_blocks( HARDFORK_CORE_868_890_TIME - fc::hours(1) );\n    */\n\n   BOOST_TEST_MESSAGE(\"Advance to near hard fork\");\n   auto maint_interval = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks( HARDFORK_CORE_868_890_TIME - maint_interval);\n   trx.set_expiration(HARDFORK_CORE_868_890_TIME - fc::seconds(1));\n\n   BOOST_TEST_MESSAGE(\"Create USDBIT\");\n   asset_id_type bit_usd_id = create_bitasset(\"USDBIT\").id;\n   asset_id_type core_id = bit_usd_id(db).bitasset_data(db).options.short_backing_asset;\n\n   {\n      BOOST_TEST_MESSAGE(\"Update the USDBIT asset options\");\n      change_asset_options(*this, nathan_id, nathan_private_key, bit_usd_id, false );\n   }\n\n   BOOST_TEST_MESSAGE(\"Create JMJBIT based on USDBIT.\");\n   asset_id_type bit_jmj_id = create_bitasset(\"JMJBIT\").id;\n   {\n      BOOST_TEST_MESSAGE(\"Update the JMJBIT asset options\");\n      change_asset_options(*this, nathan_id, nathan_private_key, bit_jmj_id, true );\n   }\n\n   {\n      BOOST_TEST_MESSAGE(\"Update the JMJBIT bitasset options\");\n      asset_update_bitasset_operation ba_op;\n      const asset_object& obj = bit_jmj_id(db);\n      ba_op.asset_to_update = obj.get_id();\n      ba_op.issuer = obj.issuer;\n      ba_op.new_options.short_backing_asset = bit_usd_id;\n      ba_op.new_options.minimum_feeds = 1;\n      trx.operations.push_back(ba_op);\n      sign(trx, nathan_private_key);\n      PUSH_TX(db, trx, ~0);\n      generate_block();\n      trx.clear();\n   }\n\n   BOOST_TEST_MESSAGE(\"Grab active witnesses\");\n   auto& global_props = db.get_global_properties();\n   std::vector<account_id_type> active_witnesses;\n   for(const witness_id_type& wit_id : global_props.active_witnesses)\n      active_witnesses.push_back(wit_id(db).witness_account);\n   BOOST_REQUIRE_EQUAL(active_witnesses.size(), INITIAL_WITNESS_COUNT);\n\n   {\n      BOOST_TEST_MESSAGE(\"Adding price feed 1\");\n      publish_feed(active_witnesses[0], bit_usd_id, 1, bit_jmj_id, 300, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 300.0);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Adding price feed 2\");\n      publish_feed(active_witnesses[1], bit_usd_id, 1, bit_jmj_id, 100, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 300.0);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Adding price feed 3\");\n      publish_feed(active_witnesses[2], bit_usd_id, 1, bit_jmj_id, 1, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 100.0);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Change underlying asset of bit_jmj from bit_usd to core\");\n      change_backing_asset(*this, nathan_private_key, bit_jmj_id, core_id);\n\n      BOOST_TEST_MESSAGE(\"Verify feed producers have not been reset\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 3ul);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"With underlying bitasset changed from one to another, price feeds should still be publish-able\");\n      BOOST_TEST_MESSAGE(\"Re-Adding Witness 1 price feed\");\n      publish_feed(active_witnesses[0], core_id, 1, bit_jmj_id, 30, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 1);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      BOOST_CHECK(bitasset.current_feed.core_exchange_rate.base.asset_id != bitasset.current_feed.core_exchange_rate.quote.asset_id);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Re-Adding Witness 2 price feed\");\n      publish_feed(active_witnesses[1], core_id, 1, bit_jmj_id, 100, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 100);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Advance to after hard fork\");\n      generate_blocks( HARDFORK_CORE_868_890_TIME + fc::seconds(1));\n      trx.set_expiration(HARDFORK_CORE_868_890_TIME + fc::hours(2));\n\n      BOOST_TEST_MESSAGE(\"After hardfork, 1 feed should have been erased\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 2ul);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"After hardfork, change underlying asset of bit_jmj from core to bit_usd\");\n      change_backing_asset(*this, nathan_private_key, bit_jmj_id, bit_usd_id);\n\n      BOOST_TEST_MESSAGE(\"Verify feed producers have been reset\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 0ul);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"With underlying bitasset changed from one to another, price feeds should still be publish-able\");\n      BOOST_TEST_MESSAGE(\"Re-Adding Witness 1 price feed\");\n      publish_feed(active_witnesses[0], bit_usd_id, 1, bit_jmj_id, 30, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      BOOST_CHECK(bitasset.current_feed.core_exchange_rate.base.asset_id != bitasset.current_feed.core_exchange_rate.quote.asset_id);\n   }\n}\n\n/****\n * @brief make sure feeds work correctly after changing the backing asset on a non-witness-fed asset\n */\nBOOST_AUTO_TEST_CASE( reset_backing_asset_on_non_witness_asset )\n{\n   ACTORS((nathan)(dan)(ben)(vikram));\n\n   BOOST_TEST_MESSAGE(\"Advance to near hard fork\");\n   auto maint_interval = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks( HARDFORK_CORE_868_890_TIME - maint_interval);\n   trx.set_expiration(HARDFORK_CORE_868_890_TIME - fc::seconds(1));\n\n\n   BOOST_TEST_MESSAGE(\"Create USDBIT\");\n   asset_id_type bit_usd_id = create_bitasset(\"USDBIT\").id;\n   asset_id_type core_id = bit_usd_id(db).bitasset_data(db).options.short_backing_asset;\n\n   {\n      BOOST_TEST_MESSAGE(\"Update the USDBIT asset options\");\n      change_asset_options(*this, nathan_id, nathan_private_key, bit_usd_id, false );\n   }\n\n   BOOST_TEST_MESSAGE(\"Create JMJBIT based on USDBIT.\");\n   asset_id_type bit_jmj_id = create_bitasset(\"JMJBIT\").id;\n   {\n      BOOST_TEST_MESSAGE(\"Update the JMJBIT asset options\");\n      change_asset_options(*this, nathan_id, nathan_private_key, bit_jmj_id, false );\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Update the JMJBIT bitasset options\");\n      asset_update_bitasset_operation ba_op;\n      const asset_object& obj = bit_jmj_id(db);\n      ba_op.asset_to_update = obj.get_id();\n      ba_op.issuer = obj.issuer;\n      ba_op.new_options.short_backing_asset = bit_usd_id;\n      ba_op.new_options.minimum_feeds = 1;\n      trx.operations.push_back(ba_op);\n      sign(trx, nathan_private_key);\n      PUSH_TX(db, trx, ~0);\n      generate_block();\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Set feed producers for JMJBIT\");\n      asset_update_feed_producers_operation op;\n      op.asset_to_update = bit_jmj_id;\n      op.issuer = nathan_id;\n      op.new_feed_producers = {dan_id, ben_id, vikram_id};\n      trx.operations.push_back(op);\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx, ~0 );\n      generate_block();\n      trx.clear();\n   }\n\n   {\n      BOOST_TEST_MESSAGE(\"Verify feed producers are registered for JMJBIT\");\n      const asset_bitasset_data_object& obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(obj.feeds.size(), 3ul);\n      BOOST_CHECK( obj.current_feed.margin_call_params_equal( price_feed() ) );\n\n      BOOST_CHECK( bit_usd_id == obj.options.short_backing_asset );\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Adding Vikram's price feed\");\n      publish_feed(vikram_id, bit_usd_id, 1, bit_jmj_id, 300, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 300.0);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Adding Ben's pricing to JMJBIT\");\n      publish_feed(ben_id, bit_usd_id, 1, bit_jmj_id, 100, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 300);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Adding Dan's pricing to JMJBIT\");\n      publish_feed(dan_id, bit_usd_id, 1, bit_jmj_id, 1, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 100);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n      generate_block();\n      trx.clear();\n\n      BOOST_CHECK(bitasset.current_feed.core_exchange_rate.base.asset_id != bitasset.current_feed.core_exchange_rate.quote.asset_id);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Change underlying asset of bit_jmj from bit_usd to core\");\n      change_backing_asset(*this, nathan_private_key, bit_jmj_id, core_id);\n\n      BOOST_TEST_MESSAGE(\"Verify feed producers have not been reset\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 3ul);\n      for(const auto& feed : jmj_obj.feeds) {\n         BOOST_CHECK(!feed.second.second.settlement_price.is_null());\n      }\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Add a new (and correct) feed price for 1 feed producer\");\n      publish_feed(vikram_id, core_id, 1, bit_jmj_id, 300, core_id);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Advance to past hard fork\");\n      generate_blocks( HARDFORK_CORE_868_890_TIME + maint_interval);\n      trx.set_expiration(HARDFORK_CORE_868_890_TIME + fc::hours(48));\n\n      BOOST_TEST_MESSAGE(\"Verify that the incorrect feeds have been corrected\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 3ul);\n      int nan_count = 0;\n      for(const auto& feed : jmj_obj.feeds)\n      {\n         if (feed.second.second.settlement_price.is_null())\n            nan_count++;\n      }\n      BOOST_CHECK_EQUAL(nan_count, 2);\n      // the settlement price will be NaN until 50% of price feeds are valid\n      //BOOST_CHECK_EQUAL(jmj_obj.current_feed.settlement_price.to_real(), 300);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"After hardfork, change underlying asset of bit_jmj from core to bit_usd\");\n      change_backing_asset(*this, nathan_private_key, bit_jmj_id, bit_usd_id);\n\n      BOOST_TEST_MESSAGE(\"Verify feed producers have been reset\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 3ul);\n      for(const auto& feed : jmj_obj.feeds)\n      {\n         BOOST_CHECK(feed.second.second.settlement_price.is_null());\n      }\n   }\n   {\n      BOOST_TEST_MESSAGE(\"With underlying bitasset changed from one to another, price feeds should still be publish-able\");\n      BOOST_TEST_MESSAGE(\"Adding Vikram's price feed\");\n      publish_feed(vikram_id, bit_usd_id, 1, bit_jmj_id, 30, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      BOOST_TEST_MESSAGE(\"Adding Ben's pricing to JMJBIT\");\n      publish_feed(ben_id, bit_usd_id, 1, bit_jmj_id, 25, core_id);\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      BOOST_TEST_MESSAGE(\"Adding Dan's pricing to JMJBIT\");\n      publish_feed(dan_id, bit_usd_id, 1, bit_jmj_id, 10, core_id);\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 25);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n      generate_block();\n      trx.clear();\n\n      BOOST_CHECK(bitasset.current_feed.core_exchange_rate.base.asset_id != bitasset.current_feed.core_exchange_rate.quote.asset_id);\n   }\n}\n\n/*********\n * @brief Update median feeds after feed_lifetime_sec changed\n */\nBOOST_AUTO_TEST_CASE( hf_890_test )\n{\n   uint32_t skip = database::skip_witness_signature\n                 | database::skip_transaction_signatures\n                 | database::skip_transaction_dupe_check\n                 | database::skip_block_size_check\n                 | database::skip_tapos_check\n                 | database::skip_merkle_check\n                 ;\n   generate_blocks(HARDFORK_615_TIME, true, skip); // get around Graphene issue #615 feed expiration bug\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n\n   auto hf_time = HARDFORK_CORE_868_890_TIME;\n   if(hf1270)\n      hf_time = HARDFORK_CORE_1270_TIME;\n\n   for( int i=0; i<2; ++i )\n   {\n      int blocks = 0;\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n      if( i == 1 ) // go beyond hard fork\n      {\n         blocks += generate_blocks(hf_time - mi, true, skip);\n         blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n      }\n      set_expiration( db, trx );\n\n      ACTORS((buyer)(seller)(borrower)(feedproducer));\n\n      int64_t init_balance(1000000);\n\n      transfer(committee_account, buyer_id, asset(init_balance));\n      transfer(committee_account, borrower_id, asset(init_balance));\n\n      const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n      asset_id_type usd_id = bitusd.id;\n\n      {\n         // change feed lifetime\n         const asset_object& asset_to_update = usd_id(db);\n         asset_update_bitasset_operation ba_op;\n         ba_op.asset_to_update = usd_id;\n         ba_op.issuer = asset_to_update.issuer;\n         ba_op.new_options = asset_to_update.bitasset_data(db).options;\n         ba_op.new_options.feed_lifetime_sec = 600;\n         trx.operations.push_back(ba_op);\n         PUSH_TX(db, trx, ~0);\n         trx.clear();\n      }\n\n      // prepare feed data\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n\n      // set price feed\n      update_feed_producers( usd_id(db), {feedproducer_id} );\n      current_feed.settlement_price = asset(100, usd_id) / asset(5);\n      publish_feed( usd_id, feedproducer_id, current_feed );\n\n      // Place some collateralized orders\n      // start out with 300% collateral, call price is 15/175 CORE/USD = 60/700\n      borrow( borrower_id, asset(100, usd_id), asset(15) );\n\n      transfer( borrower_id, seller_id, asset(100, usd_id) );\n\n      // Adjust price feed to get call order into margin call territory\n      current_feed.settlement_price = asset(100, usd_id) / asset(10);\n      publish_feed( usd_id, feedproducer_id, current_feed );\n      // settlement price = 100 USD / 10 CORE, mssp = 100/11 USD/CORE\n\n      // let the feed expire\n      blocks += generate_blocks( db.head_block_time() + 1200, true, skip );\n      set_expiration( db, trx );\n\n      // check: median feed should be null\n      BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price.is_null() );\n\n      // place a sell order, it won't be matched with the call order\n      limit_order_id_type sell_id = create_sell_order(seller_id, asset(10, usd_id), asset(1))->id;\n\n      {\n         // change feed lifetime to longer\n         const asset_object& asset_to_update = usd_id(db);\n         asset_update_bitasset_operation ba_op;\n         ba_op.asset_to_update = usd_id;\n         ba_op.issuer = asset_to_update.issuer;\n         ba_op.new_options = asset_to_update.bitasset_data(db).options;\n         ba_op.new_options.feed_lifetime_sec = hf_time.sec_since_epoch()\n                                             - db.head_block_time().sec_since_epoch()\n                                             + mi\n                                             + 1800;\n         trx.operations.push_back(ba_op);\n         PUSH_TX(db, trx, ~0);\n         trx.clear();\n      }\n\n      // check\n      if( i == 0 ) // before hard fork, median feed is still null, and limit order is still there\n      {\n         BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price.is_null() );\n         BOOST_CHECK( db.find<limit_order_object>( sell_id ) );\n\n         // go beyond hard fork\n         blocks += generate_blocks(hf_time - mi, true, skip);\n         blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n      }\n\n      // after hard fork, median feed should become valid, and the limit order should be filled\n      {\n         BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n         BOOST_CHECK( !db.find<limit_order_object>( sell_id ) );\n      }\n\n      // undo above tx's and reset\n      generate_block( skip );\n      ++blocks;\n      while( blocks > 0 )\n      {\n         db.pop_block();\n         --blocks;\n      }\n   }\n}\n\nclass bitasset_evaluator_wrapper : public asset_update_bitasset_evaluator\n{\npublic:\n   void set_db(database& db)\n   {\n      this->trx_state = new transaction_evaluation_state(&db);\n   }\n};\n\nstruct assets_922_931\n{\n   asset_id_type bit_usd;\n   asset_id_type bit_usdbacked;\n   asset_id_type bit_usdbacked2;\n   asset_id_type bit_child_bitasset;\n   asset_id_type bit_parent;\n   asset_id_type user_issued;\n   asset_id_type six_precision;\n   asset_id_type prediction;\n};\n\nassets_922_931 create_assets_922_931(database_fixture* fixture)\n{\n   assets_922_931 asset_objs;\n   BOOST_TEST_MESSAGE( \"Create USDBIT\" );\n   asset_objs.bit_usd = fixture->create_bitasset( \"USDBIT\", GRAPHENE_COMMITTEE_ACCOUNT ).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create USDBACKED\" );\n   asset_objs.bit_usdbacked = fixture->create_bitasset( \"USDBACKED\", GRAPHENE_COMMITTEE_ACCOUNT,\n         100, charge_market_fee, 2, asset_objs.bit_usd ).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create USDBACKEDII\" );\n   asset_objs.bit_usdbacked2 = fixture->create_bitasset( \"USDBACKEDII\", GRAPHENE_WITNESS_ACCOUNT,\n         100, charge_market_fee, 2, asset_objs.bit_usd ).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create PARENT\" );\n   asset_objs.bit_parent = fixture->create_bitasset( \"PARENT\", GRAPHENE_WITNESS_ACCOUNT).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create CHILDUSER\" );\n   asset_objs.bit_child_bitasset = fixture->create_bitasset( \"CHILDUSER\", GRAPHENE_WITNESS_ACCOUNT,\n         100, charge_market_fee, 2, asset_objs.bit_parent ).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create user issued USERISSUED\" );\n   asset_objs.user_issued = fixture->create_user_issued_asset( \"USERISSUED\",\n         GRAPHENE_WITNESS_ACCOUNT(fixture->db), charge_market_fee ).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create a user-issued asset with a precision of 6\" );\n   asset_objs.six_precision = fixture->create_user_issued_asset( \"SIXPRECISION\", GRAPHENE_WITNESS_ACCOUNT(fixture->db),\n         charge_market_fee, price(asset(1, asset_id_type(1)), asset(1)), 6 ).get_id();\n\n   BOOST_TEST_MESSAGE( \"Create Prediction market with precision of 6, backed by SIXPRECISION\" );\n   asset_objs.prediction = fixture->create_prediction_market( \"PREDICTION\", GRAPHENE_WITNESS_ACCOUNT,\n         100, charge_market_fee, 6, asset_objs.six_precision ).get_id();\n\n   return asset_objs;\n}\n/******\n * @brief Test various bitasset asserts within the asset_evaluator before the HF 922 / 931\n */\nBOOST_AUTO_TEST_CASE( bitasset_evaluator_test_before_922_931 )\n{\n   BOOST_TEST_MESSAGE(\"Advance to near hard fork 922 / 931\");\n   auto global_params = db.get_global_properties().parameters;\n   generate_blocks( HARDFORK_CORE_922_931_TIME - global_params.maintenance_interval );\n   trx.set_expiration( HARDFORK_CORE_922_931_TIME - global_params.maintenance_interval + global_params.maximum_time_until_expiration );\n\n   ACTORS( (nathan) (john) );\n\n   assets_922_931 asset_objs = create_assets_922_931( this );\n   const asset_id_type bit_usd_id = asset_objs.bit_usd;\n\n   // make a generic operation\n   bitasset_evaluator_wrapper evaluator;\n   evaluator.set_db(db);\n   asset_update_bitasset_operation op;\n   op.asset_to_update = bit_usd_id;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n   op.new_options = asset_objs.bit_usd(db).bitasset_data(db).options;\n\n   // this should pass\n   BOOST_TEST_MESSAGE( \"Evaluating a good operation\" );\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n\n   // test with a market issued asset\n   BOOST_TEST_MESSAGE( \"Sending a non-bitasset.\" );\n   op.asset_to_update = asset_objs.user_issued;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"on a non-BitAsset.\" );\n   op.asset_to_update = bit_usd_id;\n\n   // test changing issuer\n   BOOST_TEST_MESSAGE( \"Test changing issuer.\" );\n   account_id_type original_issuer = op.issuer;\n   op.issuer = john_id;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Only asset issuer can update\" );\n   op.issuer = original_issuer;\n\n   // bad backing_asset\n   BOOST_TEST_MESSAGE( \"Non-existent backing asset.\" );\n   asset_id_type correct_asset_id = op.new_options.short_backing_asset;\n   op.new_options.short_backing_asset = asset_id_type();\n   op.new_options.short_backing_asset.instance = 123;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Unable to find Object\" );\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // now check the things that are wrong, but still pass before HF 922 / 931\n   BOOST_TEST_MESSAGE( \"Now check the things that are wrong, but still pass before HF 922 / 931\" );\n\n   // back by self\n   BOOST_TEST_MESSAGE( \"Message should contain: op.new_options.short_backing_asset == asset_obj.get_id()\" );\n   op.new_options.short_backing_asset = bit_usd_id;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // prediction market with different precision\n   BOOST_TEST_MESSAGE( \"Message should contain: for a PM, asset_obj.precision != new_backing_asset.precision\" );\n   op.asset_to_update = asset_objs.prediction;\n   op.issuer = asset_objs.prediction(db).issuer;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.asset_to_update = bit_usd_id;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n\n   // checking old backing asset instead of new backing asset\n   BOOST_TEST_MESSAGE( \"Message should contain: to be backed by an asset which is not market issued asset nor CORE\" );\n   op.new_options.short_backing_asset = asset_objs.six_precision;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   BOOST_TEST_MESSAGE( \"Message should contain: modified a blockchain-controlled market asset to be backed by an asset \"\n         \"which is not backed by CORE\" );\n   op.new_options.short_backing_asset = asset_objs.prediction;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // CHILDUSER is a non-committee asset backed by PARENT which is backed by CORE\n   // Cannot change PARENT's backing asset from CORE to something that is not [CORE | UIA]\n   // because that will make CHILD be backed by an asset that is not itself backed by CORE or a UIA.\n   BOOST_TEST_MESSAGE( \"Message should contain: but this asset is a backing asset for another MPA, which would cause MPA \"\n         \"backed by MPA backed by MPA.\" );\n   op.asset_to_update = asset_objs.bit_parent;\n   op.issuer = asset_objs.bit_parent(db).issuer;\n   op.new_options.short_backing_asset = asset_objs.bit_usdbacked;\n   // this should generate a warning in the log, but not fail.\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   // changing the backing asset to a UIA should work\n   BOOST_TEST_MESSAGE( \"Switching to a backing asset that is a UIA should work. No warning should be produced.\" );\n   op.new_options.short_backing_asset = asset_objs.user_issued;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   // A -> B -> C, change B to be backed by A (circular backing)\n   BOOST_TEST_MESSAGE( \"Message should contain: A cannot be backed by B which is backed by A.\" );\n   op.new_options.short_backing_asset = asset_objs.bit_child_bitasset;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.new_options.short_backing_asset = asset_objs.user_issued;\n   BOOST_TEST_MESSAGE( \"Message should contain: but this asset is a backing asset for a committee-issued asset.\" );\n   // CHILDCOMMITTEE is a committee asset backed by PARENT which is backed by CORE\n   // Cannot change PARENT's backing asset from CORE to something else because that will make CHILD be backed by\n   // an asset that is not itself backed by CORE\n   create_bitasset( \"CHILDCOMMITTEE\", GRAPHENE_COMMITTEE_ACCOUNT, 100, charge_market_fee, 2,\n         asset_objs.bit_parent );\n   // it should again work, generating 2 warnings in the log. 1 for the above, and 1 new one.\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.asset_to_update = asset_objs.bit_usd;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // USDBACKED is backed by USDBIT (which is backed by CORE)\n   // USDBACKEDII is backed by USDBIT\n   // We should not be able to make USDBACKEDII be backed by USDBACKED\n   // because that would be a MPA backed by MPA backed by MPA.\n   BOOST_TEST_MESSAGE( \"Message should contain: a BitAsset cannot be backed by a BitAsset that \"\n         \"itself is backed by a BitAsset.\" );\n   op.asset_to_update = asset_objs.bit_usdbacked2;\n   op.issuer = asset_objs.bit_usdbacked2(db).issuer;\n   op.new_options.short_backing_asset = asset_objs.bit_usdbacked;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   // set everything to a more normal state\n   op.asset_to_update = asset_objs.bit_usdbacked;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n   op.new_options.short_backing_asset = asset_id_type();\n\n   // Feed lifetime must exceed block interval\n   BOOST_TEST_MESSAGE( \"Message should contain: op.new_options.feed_lifetime_sec <= chain_parameters.block_interval\" );\n   const auto good_feed_lifetime = op.new_options.feed_lifetime_sec;\n   op.new_options.feed_lifetime_sec = db.get_global_properties().parameters.block_interval;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   BOOST_TEST_MESSAGE( \"Message should contain: op.new_options.feed_lifetime_sec <= chain_parameters.block_interval\" );\n   op.new_options.feed_lifetime_sec = db.get_global_properties().parameters.block_interval - 1; // default interval > 1\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.new_options.feed_lifetime_sec = good_feed_lifetime;\n\n   // Force settlement delay must exceed block interval.\n   BOOST_TEST_MESSAGE( \"Message should contain: op.new_options.force_settlement_delay_sec <= chain_parameters.block_interval\" );\n   const auto good_force_settlement_delay_sec = op.new_options.force_settlement_delay_sec;\n   op.new_options.force_settlement_delay_sec = db.get_global_properties().parameters.block_interval;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   BOOST_TEST_MESSAGE( \"Message should contain: op.new_options.force_settlement_delay_sec <= chain_parameters.block_interval\" );\n   op.new_options.force_settlement_delay_sec = db.get_global_properties().parameters.block_interval - 1; // default interval > 1\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   op.new_options.force_settlement_delay_sec = good_force_settlement_delay_sec;\n\n   // this should pass\n   BOOST_TEST_MESSAGE( \"We should be all good again.\" );\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n}\n\n/******\n * @brief Test various bitasset asserts within the asset_evaluator before the HF 922 / 931\n */\nBOOST_AUTO_TEST_CASE( bitasset_evaluator_test_after_922_931 )\n{\n   BOOST_TEST_MESSAGE(\"Advance to after hard fork 922 / 931\");\n   auto global_params = db.get_global_properties().parameters;\n   generate_blocks( HARDFORK_CORE_922_931_TIME + global_params.maintenance_interval );\n   trx.set_expiration( HARDFORK_CORE_922_931_TIME + global_params.maintenance_interval + global_params.maximum_time_until_expiration );\n\n   ACTORS( (nathan) (john) );\n\n   assets_922_931 asset_objs = create_assets_922_931( this );\n   const asset_id_type& bit_usd_id = asset_objs.bit_usd;\n\n   // make a generic operation\n   bitasset_evaluator_wrapper evaluator;\n   evaluator.set_db( db );\n   asset_update_bitasset_operation op;\n   op.asset_to_update = bit_usd_id;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n   op.new_options = asset_objs.bit_usd(db).bitasset_data(db).options;\n\n   // this should pass\n   BOOST_TEST_MESSAGE( \"Evaluating a good operation\" );\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n\n   // test with a market issued asset\n   BOOST_TEST_MESSAGE( \"Sending a non-bitasset.\" );\n   op.asset_to_update = asset_objs.user_issued;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Cannot update BitAsset-specific settings on a non-BitAsset\" );\n   op.asset_to_update = bit_usd_id;\n\n   // test changing issuer\n   BOOST_TEST_MESSAGE( \"Test changing issuer.\" );\n   account_id_type original_issuer = op.issuer;\n   op.issuer = john_id;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Only asset issuer can update\" );\n   op.issuer = original_issuer;\n\n   // bad backing_asset\n   BOOST_TEST_MESSAGE( \"Non-existent backing asset.\" );\n   asset_id_type correct_asset_id = op.new_options.short_backing_asset;\n   op.new_options.short_backing_asset = asset_id_type();\n   op.new_options.short_backing_asset.instance = 123;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Unable to find\" );\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // now check the things that are wrong and won't pass after HF 922 / 931\n   BOOST_TEST_MESSAGE( \"Now check the things that are wrong and won't pass after HF 922 / 931\" );\n\n   // back by self\n   BOOST_TEST_MESSAGE( \"Back by itself\" );\n   op.new_options.short_backing_asset = bit_usd_id;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Cannot update an asset to be backed by itself\" );\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // prediction market with different precision\n   BOOST_TEST_MESSAGE( \"Prediction market with different precision\" );\n   op.asset_to_update = asset_objs.prediction;\n   op.issuer = asset_objs.prediction(db).issuer;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"The precision of the asset and backing asset must\" );\n   op.asset_to_update = bit_usd_id;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n\n   // checking old backing asset instead of new backing asset\n   BOOST_TEST_MESSAGE( \"Correctly checking new backing asset rather than old backing asset\" );\n   op.new_options.short_backing_asset = asset_objs.six_precision;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"which is not market issued asset nor CORE.\" );\n   op.new_options.short_backing_asset = asset_objs.prediction;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"which is not backed by CORE\" );\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // CHILD is a non-committee asset backed by PARENT which is backed by CORE\n   // Cannot change PARENT's backing asset from CORE to something that is not [CORE | UIA]\n   // because that will make CHILD be backed by an asset that is not itself backed by CORE or a UIA.\n   BOOST_TEST_MESSAGE( \"Attempting to change PARENT to be backed by a non-core and non-user-issued asset\" );\n   op.asset_to_update = asset_objs.bit_parent;\n   op.issuer = asset_objs.bit_parent(db).issuer;\n   op.new_options.short_backing_asset = asset_objs.bit_usdbacked;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"A non-blockchain controlled BitAsset would be invalidated\" );\n   // changing the backing asset to a UIA should work\n   BOOST_TEST_MESSAGE( \"Switching to a backing asset that is a UIA should work.\" );\n   op.new_options.short_backing_asset = asset_objs.user_issued;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   // A -> B -> C, change B to be backed by A (circular backing)\n   BOOST_TEST_MESSAGE( \"Check for circular backing. This should generate an exception\" );\n   op.new_options.short_backing_asset = asset_objs.bit_child_bitasset;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"'A' backed by 'B' backed by 'A'\" );\n   op.new_options.short_backing_asset = asset_objs.user_issued;\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n   BOOST_TEST_MESSAGE( \"Creating CHILDCOMMITTEE\" );\n   // CHILDCOMMITTEE is a committee asset backed by PARENT which is backed by CORE\n   // Cannot change PARENT's backing asset from CORE to something else because that will make CHILDCOMMITTEE\n   // be backed by an asset that is not itself backed by CORE\n   create_bitasset( \"CHILDCOMMITTEE\", GRAPHENE_COMMITTEE_ACCOUNT, 100, charge_market_fee, 2,\n         asset_objs.bit_parent );\n   // it should again not work\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"A blockchain-controlled market asset would be invalidated\" );\n   op.asset_to_update = asset_objs.bit_usd;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n   op.new_options.short_backing_asset = correct_asset_id;\n\n   // USDBACKED is backed by USDBIT (which is backed by CORE)\n   // USDBACKEDII is backed by USDBIT\n   // We should not be able to make USDBACKEDII be backed by USDBACKED\n   // because that would be a MPA backed by MPA backed by MPA.\n   BOOST_TEST_MESSAGE( \"MPA -> MPA -> MPA not allowed\" );\n   op.asset_to_update = asset_objs.bit_usdbacked2;\n   op.issuer = asset_objs.bit_usdbacked2(db).issuer;\n   op.new_options.short_backing_asset = asset_objs.bit_usdbacked;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op),\n         \"A BitAsset cannot be backed by a BitAsset that itself is backed by a BitAsset\" );\n   // set everything to a more normal state\n   op.asset_to_update = asset_objs.bit_usdbacked;\n   op.issuer = asset_objs.bit_usd(db).issuer;\n   op.new_options.short_backing_asset = asset_id_type();\n\n   // Feed lifetime must exceed block interval\n   BOOST_TEST_MESSAGE( \"Feed lifetime less than or equal to block interval\" );\n   const auto good_feed_lifetime = op.new_options.feed_lifetime_sec;\n   op.new_options.feed_lifetime_sec = db.get_global_properties().parameters.block_interval;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Feed lifetime must exceed block\" );\n   op.new_options.feed_lifetime_sec = db.get_global_properties().parameters.block_interval - 1; // default interval > 1\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Feed lifetime must exceed block\" );\n   op.new_options.feed_lifetime_sec = good_feed_lifetime;\n\n   // Force settlement delay must exceed block interval.\n   BOOST_TEST_MESSAGE( \"Force settlement delay less than or equal to block interval\" );\n   const auto good_force_settlement_delay_sec = op.new_options.force_settlement_delay_sec;\n   op.new_options.force_settlement_delay_sec = db.get_global_properties().parameters.block_interval;\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Force settlement delay must\" );\n   op.new_options.force_settlement_delay_sec = db.get_global_properties().parameters.block_interval - 1; // default interval > 1\n   REQUIRE_EXCEPTION_WITH_TEXT( evaluator.evaluate(op), \"Force settlement delay must\" );\n   op.new_options.force_settlement_delay_sec = good_force_settlement_delay_sec;\n\n   // this should pass\n   BOOST_TEST_MESSAGE( \"We should be all good again.\" );\n   BOOST_CHECK( evaluator.evaluate(op).is_type<void_result>() );\n\n}\n\n/*********\n * @brief Call price inconsistent when MCR changed\n */\nBOOST_AUTO_TEST_CASE( hf_1270_test )\n{ try {\n   uint32_t skip = database::skip_witness_signature\n                 | database::skip_transaction_signatures\n                 | database::skip_transaction_dupe_check\n                 | database::skip_block_size_check\n                 | database::skip_tapos_check\n                 | database::skip_merkle_check\n                 ;\n   generate_blocks( HARDFORK_615_TIME, true, skip ); // get around Graphene issue #615 feed expiration bug\n   generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip );\n   generate_block( skip );\n\n   for( int i = 0; i < 10; ++i )\n   {\n      idump( (i) );\n      int blocks = 0;\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n      if( i == 2) // go beyond hard fork 890 (which is the same as 935 BTW)\n      {\n         generate_blocks( HARDFORK_CORE_868_890_TIME - mi, true, skip );\n         generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip );\n      }\n      else if( i == 6 ) // go beyond hard fork 1270\n      {\n         generate_blocks( HARDFORK_CORE_1270_TIME - mi, true, skip );\n         generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip );\n      }\n      else if( i == 8 ) // go beyond hard fork BSIP77\n      {\n         generate_blocks( HARDFORK_BSIP_77_TIME, true, skip );\n      }\n      set_expiration( db, trx );\n\n      ACTORS( (seller)(borrower)(feedproducer)(feedproducer2)(feedproducer3) );\n\n      int64_t init_balance( 1000000 );\n\n      transfer( committee_account, borrower_id, asset(init_balance) );\n\n      const auto& bitusd = create_bitasset( \"USDBIT\", feedproducer_id );\n      asset_id_type usd_id = bitusd.id;\n\n      {\n         // set a short feed lifetime\n         const asset_object& asset_to_update = usd_id(db);\n         asset_update_bitasset_operation ba_op;\n         ba_op.asset_to_update = usd_id;\n         ba_op.issuer = asset_to_update.issuer;\n         ba_op.new_options = asset_to_update.bitasset_data(db).options;\n         ba_op.new_options.feed_lifetime_sec = 300;\n         trx.operations.push_back(ba_op);\n         PUSH_TX(db, trx, ~0);\n         trx.clear();\n      }\n\n      // set feed producers\n      flat_set<account_id_type> producers;\n      producers.insert( feedproducer_id );\n      producers.insert( feedproducer2_id );\n      producers.insert( feedproducer3_id );\n      update_feed_producers( usd_id(db), producers );\n\n      // prepare feed data\n      price_feed current_feed;\n      if( i % 2 == 0 ) // MCR test\n      {\n         current_feed.maintenance_collateral_ratio = 3500;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         current_feed.settlement_price = asset(100, usd_id) / asset(5);\n      }\n      else // MSSR test\n      {\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1250;\n         current_feed.settlement_price = asset(100, usd_id) / asset(10);\n         // mssp = 1000/125\n      }\n\n      // set 2 price feeds which should call some later\n      publish_feed( usd_id, feedproducer_id, current_feed );\n      publish_feed( usd_id, feedproducer2_id, current_feed );\n\n      // check median\n      BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n      if( i % 2 == 0 ) // MCR test, MCR should be 350%\n         BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500 );\n      else // MSSR test, MSSR should be 125%\n         BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250 );\n\n      // generate some blocks, let the feeds expire\n      blocks += generate_blocks( db.head_block_time() + 360, true, skip );\n      set_expiration( db, trx );\n\n      // check median, should be null\n      BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price.is_null() );\n\n      // publish a new feed with 175% MCR and 110% MSSR\n      current_feed.settlement_price = asset(100, usd_id) / asset(5);\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      publish_feed( usd_id, feedproducer3_id, current_feed );\n\n      // check median, MCR would be 175%, MSSR would be 110%\n      BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n      BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 1750 );\n      BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1100 );\n\n      // Place some collateralized orders\n      // start out with 300% collateral, call price is 15/175 CORE/USD = 60/700\n      borrow( borrower_id, asset(100, usd_id), asset(15) );\n\n      transfer( borrower_id, seller_id, asset(100, usd_id) );\n\n      if( i % 2 == 1) // MSSR test\n      {\n         // publish a new feed to put the call order into margin call territory\n         current_feed.settlement_price = asset(100, usd_id) / asset(10);\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         publish_feed( usd_id, feedproducer3_id, current_feed );\n         // mssp = 100/11\n      }\n\n      // place a sell order, it won't be matched with the call order now.\n      // For MCR test, the sell order is at feed price (100/5),\n      //   when median MCR changed to 350%, the call order with 300% collateral will be in margin call territory,\n      //   then this limit order should be filled\n      // For MSSR test, the sell order is above 110% of feed price (100/10) but below 125% of feed price,\n      //   when median MSSR changed to 125%, the call order will be matched,\n      //   then this limit order should be filled\n      limit_order_id_type sell_id = ( i % 2 == 0 ) ?\n                                    create_sell_order( seller_id, asset(20, usd_id), asset(1) )->id : // for MCR test\n                                    create_sell_order( seller_id, asset(8, usd_id), asset(1) )->id;  // for MSSR test\n\n      {\n         // change feed lifetime to longer, let all 3 feeds be valid\n         const asset_object& asset_to_update = usd_id(db);\n         asset_update_bitasset_operation ba_op;\n         ba_op.asset_to_update = usd_id;\n         ba_op.issuer = asset_to_update.issuer;\n         ba_op.new_options = asset_to_update.bitasset_data(db).options;\n         ba_op.new_options.feed_lifetime_sec = HARDFORK_CORE_1270_TIME.sec_since_epoch()\n                                             + mi * 3 + 86400 * 2\n                                             - db.head_block_time().sec_since_epoch();\n         trx.operations.push_back(ba_op);\n         PUSH_TX(db, trx, ~0);\n         trx.clear();\n      }\n\n      bool affected_by_hf_343 = false;\n\n      // check\n      if( i / 2 == 0 ) // before hard fork 890\n      {\n         // median feed won't change (issue 890)\n         BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n         BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 1750 );\n         BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1100 );\n         // limit order is still there\n         BOOST_CHECK( db.find<limit_order_object>( sell_id ) );\n\n         // go beyond hard fork 890\n         blocks += generate_blocks( HARDFORK_CORE_868_890_TIME - mi, true, skip );\n         bool was_before_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_343_TIME );\n\n         blocks += generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip );\n         bool now_after_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_343_TIME );\n\n         if( was_before_hf_343 && now_after_hf_343 ) // if hf 343 executed at same maintenance interval, actually after hf 890\n            affected_by_hf_343 = true;\n      }\n\n      // after hard fork 890, if it's before hard fork 935\n      if( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_935_TIME )\n      {\n         // median should have changed\n         BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n         if( i % 2 == 0 ) // MCR test, MCR should be 350%\n            BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500 );\n         else // MSSR test, MSSR should be 125%\n            BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250 );\n\n         if( affected_by_hf_343 ) // if updated bitasset before hf 890, and hf 343 executed after hf 890\n            // the limit order should have been filled\n            BOOST_CHECK( !db.find<limit_order_object>( sell_id ) );\n         else // if not affected by hf 343\n            // the limit order should be still there, because `check_call_order` was incorrectly skipped\n            BOOST_CHECK( db.find<limit_order_object>( sell_id ) );\n\n         // go beyond hard fork 935\n         blocks += generate_blocks(HARDFORK_CORE_935_TIME - mi, true, skip);\n         blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n      }\n\n      // after hard fork 935, the limit order is filled only for the MSSR test\n      if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_935_TIME &&\n          db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_1270_TIME)\n      {\n         // check median\n         BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n         if( i % 2 == 0) { // MCR test, median MCR should be 350% and order will not be filled except when i = 0\n            BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500);\n            if( affected_by_hf_343 )\n               BOOST_CHECK(!db.find<limit_order_object>(sell_id));\n            else\n               BOOST_CHECK(db.find<limit_order_object>(sell_id)); // MCR bug, order still there\n         }\n         else { // MSSR test, MSSR should be 125% and order is filled\n            BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250);\n            BOOST_CHECK(!db.find<limit_order_object>(sell_id)); // order filled\n         }\n\n         // go beyond hard fork 1270\n         blocks += generate_blocks(HARDFORK_CORE_1270_TIME - mi, true, skip);\n         blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n      }\n\n      // after hard fork 1270, the limit order should be filled for MCR test\n      if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_1270_TIME)\n      {\n         // check median\n         BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n         if( i % 2 == 0 ) { // MCR test, order filled\n            BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500);\n            BOOST_CHECK(!db.find<limit_order_object>(sell_id));\n         }\n      }\n\n      // undo above tx's and reset\n      generate_block( skip );\n      ++blocks;\n      while( blocks > 0 )\n      {\n         db.pop_block();\n         --blocks;\n      }\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( bitasset_secondary_index )\n{\n   ACTORS( (nathan) );\n\n   graphene::chain::asset_id_type core_id;\n   BOOST_TEST_MESSAGE( \"Running test bitasset_secondary_index\" );\n   BOOST_TEST_MESSAGE( \"Core asset id: \" + fc::json::to_pretty_string( core_id ) );\n   BOOST_TEST_MESSAGE(\"Create coins\");\n   try\n   {\n      // make 5 coins (backed by core)\n      for(int i = 0; i < 5; i++)\n      {\n         create_bitasset_backed(*this, i, core_id, nathan_private_key);\n      }\n      // make the next 5 (10-14) be backed by COIN1\n      graphene::chain::asset_id_type coin1_id = get_asset(\"COIN1TEST\").get_id();\n      for(int i = 5; i < 10; i++)\n      {\n         create_bitasset_backed(*this, i, coin1_id, nathan_private_key);\n      }\n      // make the next 5 (15-19) be backed by COIN2\n      graphene::chain::asset_id_type coin2_id = get_asset(\"COIN2TEST\").get_id();\n      for(int i = 10; i < 15; i++)\n      {\n         create_bitasset_backed(*this, i, coin2_id, nathan_private_key);\n      }\n      // make the last 5 be backed by core\n      for(int i = 15; i < 20; i++)\n      {\n         create_bitasset_backed(*this, i, core_id, nathan_private_key);\n      }\n\n      BOOST_TEST_MESSAGE(\"Searching for all coins backed by CORE\");\n      const auto& idx = db.get_index_type<graphene::chain::asset_bitasset_data_index>().indices().get<graphene::chain::by_short_backing_asset>();\n      auto core_itr = idx.equal_range( core_id );\n      BOOST_TEST_MESSAGE(\"Searching for all coins backed by COIN1\");\n      auto coin1_itr = idx.equal_range( coin1_id );\n      BOOST_TEST_MESSAGE(\"Searching for all coins backed by COIN2\");\n      auto coin2_itr = idx.equal_range( coin2_id );\n\n      int core_count = 0, coin1_count = 0, coin2_count = 0;\n\n      BOOST_TEST_MESSAGE(\"Counting coins in each category\");\n\n      for( auto itr = core_itr.first ; itr != core_itr.second; ++itr)\n      {\n         BOOST_CHECK(itr->options.short_backing_asset == core_id);\n         BOOST_TEST_MESSAGE( fc::json::to_pretty_string(itr->asset_id) + \" is backed by CORE\" );\n         core_count++;\n      }\n      for( auto itr = coin1_itr.first ; itr != coin1_itr.second; ++itr )\n      {\n         BOOST_CHECK(itr->options.short_backing_asset == coin1_id);\n         BOOST_TEST_MESSAGE( fc::json::to_pretty_string( itr->asset_id) + \" is backed by COIN1TEST\" );\n         coin1_count++;\n      }\n      for( auto itr = coin2_itr.first; itr != coin2_itr.second; ++itr )\n      {\n         BOOST_CHECK(itr->options.short_backing_asset == coin2_id);\n         BOOST_TEST_MESSAGE( fc::json::to_pretty_string( itr->asset_id) + \" is backed by COIN2TEST\" );\n         coin2_count++;\n      }\n\n      BOOST_CHECK( core_count >= 10 );\n      BOOST_CHECK_EQUAL( coin1_count, 5 );\n      BOOST_CHECK_EQUAL( coin2_count, 5 );\n   }\n   catch (fc::exception& ex)\n   {\n      BOOST_FAIL(ex.to_string(fc::log_level(fc::log_level::all)));\n   }\n}\n\n\n/*****\n * @brief make sure feeds work correctly after changing from non-witness-fed to witness-fed before the 868 fork\n * NOTE: This test case is a different issue than what is currently being worked on, and fails. Hopefully it\n * will help when the fix for that issue is being coded.\n */\n/*\nBOOST_AUTO_TEST_CASE( reset_backing_asset_switching_to_witness_fed )\n{\n   ACTORS((nathan)(dan)(ben)(vikram));\n\n   BOOST_TEST_MESSAGE(\"Advance to near hard fork\");\n   auto maint_interval = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks( HARDFORK_CORE_868_890_TIME - maint_interval);\n   trx.set_expiration(HARDFORK_CORE_868_890_TIME - fc::seconds(1));\n\n\n   BOOST_TEST_MESSAGE(\"Create USDBIT\");\n   asset_id_type bit_usd_id = create_bitasset(\"USDBIT\").id;\n   asset_id_type core_id = bit_usd_id(db).bitasset_data(db).options.short_backing_asset;\n\n   {\n      BOOST_TEST_MESSAGE(\"Update the USDBIT asset options\");\n      change_asset_options(*this, nathan_id, nathan_private_key, bit_usd_id, false );\n   }\n\n   BOOST_TEST_MESSAGE(\"Create JMJBIT based on USDBIT.\");\n   asset_id_type bit_jmj_id = create_bitasset(\"JMJBIT\").id;\n   {\n      BOOST_TEST_MESSAGE(\"Update the JMJBIT asset options\");\n      change_asset_options(*this, nathan_id, nathan_private_key, bit_jmj_id, false );\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Update the JMJBIT bitasset options\");\n      asset_update_bitasset_operation ba_op;\n      const asset_object& obj = bit_jmj_id(db);\n      ba_op.asset_to_update = obj.get_id();\n      ba_op.issuer = obj.issuer;\n      ba_op.new_options.short_backing_asset = bit_usd_id;\n      ba_op.new_options.minimum_feeds = 1;\n      trx.operations.push_back(ba_op);\n      sign(trx, nathan_private_key);\n      PUSH_TX(db, trx, ~0);\n      generate_block();\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Set feed producers for JMJBIT\");\n      asset_update_feed_producers_operation op;\n      op.asset_to_update = bit_jmj_id;\n      op.issuer = nathan_id;\n      op.new_feed_producers = {dan_id, ben_id, vikram_id};\n      trx.operations.push_back(op);\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx, ~0 );\n      generate_block();\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Verify feed producers are registered for JMJBIT\");\n      const asset_bitasset_data_object& obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(obj.feeds.size(), 3ul);\n      BOOST_CHECK(obj.current_feed == price_feed());\n\n\n      BOOST_CHECK_EQUAL(\"1\", std::to_string(obj.options.short_backing_asset.space_id));\n      BOOST_CHECK_EQUAL(\"3\", std::to_string(obj.options.short_backing_asset.type_id));\n      BOOST_CHECK_EQUAL(\"1\", std::to_string(obj.options.short_backing_asset.instance.value));\n\n      BOOST_CHECK_EQUAL(\"1\", std::to_string(bit_jmj_id.space_id));\n      BOOST_CHECK_EQUAL(\"3\", std::to_string(bit_jmj_id.type_id));\n      BOOST_CHECK_EQUAL(\"2\", std::to_string(bit_jmj_id.instance.value));\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Adding Vikram's price feed\");\n      add_price_feed(*this, vikram_id, bit_usd_id, 1, bit_jmj_id, 300, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 300.0);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Change JMJBIT to be witness_fed\");\n      optional<account_id_type> noone;\n      change_asset_options(*this, noone, nathan_private_key, bit_jmj_id, true );\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Change underlying asset of bit_jmj from bit_usd to core\");\n      change_backing_asset(*this, nathan_private_key, bit_jmj_id, core_id);\n\n      BOOST_TEST_MESSAGE(\"Verify feed producers have not been reset\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 3ul);\n      int nan_count = 0;\n      for(const auto& feed : jmj_obj.feeds) {\n         if(feed.second.second.settlement_price.is_null())\n            ++nan_count;\n      }\n      BOOST_CHECK_EQUAL(nan_count, 2);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Add a new (and correct) feed price from a witness\");\n      auto& global_props = db.get_global_properties();\n      std::vector<account_id_type> active_witnesses;\n      const witness_id_type& first_witness_id = (*global_props.active_witnesses.begin());\n      const account_id_type witness_account_id = first_witness_id(db).witness_account;\n      add_price_feed(*this, witness_account_id, core_id, 1, bit_jmj_id, 300, core_id);\n\n      // we should have 2 feeds nan, 1 old feed with wrong asset, and 1 witness feed\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 4ul);\n      int nan_count = 0;\n      for(const auto& feed : jmj_obj.feeds) {\n         if ( feed.second.second.settlement_price.is_null() )\n            ++nan_count;\n      }\n      BOOST_CHECK_EQUAL(nan_count, 2);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Advance to past hard fork\");\n      generate_blocks( HARDFORK_CORE_868_890_TIME + maint_interval);\n      trx.set_expiration(HARDFORK_CORE_868_890_TIME + fc::hours(48));\n\n      BOOST_TEST_MESSAGE(\"Verify that the incorrect feeds have been removed\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 1ul);\n      BOOST_CHECK( ! (*jmj_obj.feeds.begin()).second.second.settlement_price.is_null() );\n      // the settlement price will be NaN until 50% of price feeds are valid\n      //BOOST_CHECK_EQUAL(jmj_obj.current_feed.settlement_price.to_real(), 300);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"After hardfork, change underlying asset of bit_jmj from core to bit_usd\");\n      change_backing_asset(*this, nathan_private_key, bit_jmj_id, bit_usd_id);\n\n      BOOST_TEST_MESSAGE(\"Verify feed producers have been reset\");\n      const asset_bitasset_data_object& jmj_obj = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(jmj_obj.feeds.size(), 0ul);\n   }\n   {\n      BOOST_TEST_MESSAGE(\"With underlying bitasset changed from one to another, price feeds should still be publish-able\");\n      auto& global_props = db.get_global_properties();\n      std::vector<account_id_type> active_witnesses;\n      for(const auto& witness_id : global_props.active_witnesses)\n      {\n         active_witnesses.push_back(witness_id(db).witness_account);\n      }\n      BOOST_TEST_MESSAGE(\"Adding Witness 0's price feed\");\n      add_price_feed(*this, active_witnesses[0], bit_usd_id, 1, bit_jmj_id, 30, core_id);\n\n      const asset_bitasset_data_object& bitasset = bit_jmj_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      BOOST_TEST_MESSAGE(\"Adding Witness 1's pricing to JMJBIT\");\n      add_price_feed(*this, active_witnesses[0], bit_usd_id, 1, bit_jmj_id, 25, core_id);\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      BOOST_TEST_MESSAGE(\"Adding Witness 2's pricing to JMJBIT\");\n      add_price_feed(*this, active_witnesses[2], bit_usd_id, 1, bit_jmj_id, 10, core_id);\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 25);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n      generate_block();\n      trx.clear();\n\n      BOOST_CHECK(bitasset.current_feed.core_exchange_rate.base.asset_id != bitasset.current_feed.core_exchange_rate.quote.asset_id);\n   }\n}\n*/\nBOOST_AUTO_TEST_CASE(hf_890_test_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(hf_890_test);\n\n} FC_LOG_AND_RETHROW() }\n\n\n   /**\n    * Test the claiming of collateral asset fees before HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME.\n    *\n    * Test prohibitions against changing of the backing/collateral asset for a smart asset\n    * if any collateral asset fees are available to be claimed.\n    */\n   BOOST_AUTO_TEST_CASE(change_backing_asset_prohibitions) {\n      try {\n         /**\n          * Initialize\n          */\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((smartissuer)(feedproducer)); // Actors for smart asset\n         ACTORS((jill)(izzy)); // Actors for user-issued assets\n         ACTORS((alice)); // Actors who hold balances\n\n         price price(asset(1, asset_id_type(1)), asset(1));\n         uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2, market_fee_percent);\n         generate_block(); trx.clear(); set_expiration(db, trx);\n         const asset_object jillcoin = get_asset(\"JCOIN\");\n         const int64_t jillcoin_unit = 100; // 100 satoshi JILLCOIN in 1 JILLCOIN\n\n         create_user_issued_asset(\"ICOIN\", izzy_id(db), charge_market_fee, price, 2, market_fee_percent);\n         generate_block();\n         const asset_object izzycoin = get_asset(\"ICOIN\");\n\n         // Create the smart asset backed by JCOIN\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent,\n                         charge_market_fee, 2, jillcoin.id);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block(); trx.clear(); set_expiration(db, trx);\n         const asset_object &smartbit = get_asset(\"SMARTBIT\");\n         const asset_bitasset_data_object& smartbit_bitasset_data = (*smartbit.bitasset_data_id)(db);\n         // Confirm that the asset is to be backed by JCOIN\n         BOOST_CHECK(smartbit_bitasset_data.options.short_backing_asset == jillcoin.id);\n\n         // Fund balances of the actors\n         issue_uia(alice, jillcoin.amount(5000 * jillcoin_unit));\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 5000 * jillcoin_unit);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit), 0);\n\n\n         /**\n          * Claim any amount of collateral asset fees.  This should fail because claiming such fees are prohibited\n          * before the HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME.\n          */\n         trx.clear();\n         asset_claim_fees_operation claim_op;\n         claim_op.issuer = smartissuer_id;\n         claim_op.extensions.value.claim_from_asset_id = smartbit.id;\n         claim_op.amount_to_claim = jillcoin.amount(5 * jillcoin_unit);\n         trx.operations.push_back(claim_op);\n         sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Collateral-denominated fees are not yet active\");\n\n\n         /**\n          * Propose to claim any amount of collateral asset fees.\n          * This should fail because claiming such fees are prohibited before HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME.\n          */\n         proposal_create_operation cop;\n         cop.review_period_seconds = 86400;\n         uint32_t buffer_seconds = 60 * 60;\n         cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.proposed_ops.emplace_back(claim_op);\n\n         trx.clear();\n         trx.operations.push_back(cop);\n         // sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Collateral-denominated fees are not yet active\");\n\n\n         /**\n          * Advance to when the collateral fee container is activated\n          */\n         generate_blocks(HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME);\n         generate_block(); trx.clear(); set_expiration(db, trx);\n\n\n         /**\n          * Cause some collateral of JCOIN to be accumulated as collateral fee within the SMARTBIT asset type\n          */\n         // HACK: Before BSIP74 or BSIP87 are introduced, it is not formally possible to accumulate collateral fees.\n         // Therefore, the accumulation for this test will be informally induced by direct manipulation of the database.\n         // More formal tests will be provided with the PR for either BSIP74 or BSIP87.\n         // IMPORTANT: The use of this hack requires that no additional blocks are subsequently generated!\n         asset accumulation_amount = jillcoin.amount(40 * jillcoin_unit); // JCOIN\n         db.adjust_balance(alice_id, -accumulation_amount); // Deduct 40 JCOIN from alice as a \"collateral fee\"\n         smartbit.accumulate_fee(db, accumulation_amount); // Add 40 JCOIN from alice as a \"collateral fee\"\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), (5000 * jillcoin_unit) - (40 * jillcoin_unit));\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees == accumulation_amount.amount);\n\n\n         /**\n          * Attempt to change the backing asset.  This should fail because there are unclaimed collateral fees.\n          */\n         trx.clear();\n         asset_update_bitasset_operation change_backing_asset_op;\n         change_backing_asset_op.asset_to_update = smartbit.id;\n         change_backing_asset_op.issuer = smartissuer_id;\n         change_backing_asset_op.new_options.short_backing_asset = izzycoin.id;\n         trx.operations.push_back(change_backing_asset_op);\n         sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Must claim collateral-denominated fees\");\n\n\n         /**\n          * Attempt to claim a negative amount of the collateral asset fees.\n          * This should fail because positive amounts are required.\n          */\n         trx.clear();\n         claim_op.amount_to_claim = jillcoin.amount(-9 * jillcoin_unit);\n         trx.operations.push_back(claim_op);\n         sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"amount_to_claim.amount > 0\");\n\n\n         /**\n          * Attempt to claim 0 amount of the collateral asset fees.\n          * This should fail because positive amounts are required.\n          */\n         trx.clear();\n         claim_op.amount_to_claim = jillcoin.amount(0 * jillcoin_unit);\n         trx.operations.push_back(claim_op);\n         sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"amount_to_claim.amount > 0\");\n\n\n         /**\n          * Attempt to claim excessive amount of collateral asset fees.\n          * This should fail because there are insufficient collateral fees.\n          */\n         trx.clear();\n         claim_op.amount_to_claim = accumulation_amount + jillcoin.amount(1);\n         trx.operations.push_back(claim_op);\n         sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Attempt to claim more backing-asset fees\");\n\n\n         /**\n          * Claim some of the collateral asset fees\n          */\n         share_type part_of_accumulated_fees = accumulation_amount.amount / 4;\n         FC_ASSERT(part_of_accumulated_fees.value > 0); // Partial claim should be positive\n         share_type remainder_accumulated_fees = accumulation_amount.amount - part_of_accumulated_fees;\n         FC_ASSERT(remainder_accumulated_fees.value > 0); // Planned remainder should be positive\n         trx.clear();\n         claim_op.amount_to_claim = jillcoin.amount(part_of_accumulated_fees);\n         trx.operations.push_back(claim_op);\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx);\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees == remainder_accumulated_fees);\n\n\n         /**\n          * Claim all the remaining collateral asset fees\n          */\n         trx.clear();\n         claim_op.amount_to_claim = jillcoin.amount(remainder_accumulated_fees);\n         trx.operations.push_back(claim_op);\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx);\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees == 0); // 0 remainder\n\n\n         /**\n          * Attempt to change the backing asset.\n          * This should succeed because there are no collateral asset fees are waiting to be claimed.\n          */\n         trx.clear();\n         trx.operations.push_back(change_backing_asset_op);\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx);\n\n         // Confirm the change to the backing asset\n         BOOST_CHECK(smartbit_bitasset_data.options.short_backing_asset == izzycoin.id);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/block_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/witness_schedule_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\ngenesis_state_type make_genesis() {\n   genesis_state_type genesis_state;\n\n   genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );\n\n   auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")));\n   genesis_state.initial_active_witnesses = 10;\n   genesis_state.immutable_parameters.min_committee_member_count = INITIAL_COMMITTEE_MEMBER_COUNT;\n   genesis_state.immutable_parameters.min_witness_count = INITIAL_WITNESS_COUNT;\n\n   for( unsigned int i = 0; i < genesis_state.initial_active_witnesses; ++i )\n   {\n      auto name = \"init\"+fc::to_string(i);\n      genesis_state.initial_accounts.emplace_back(name,\n                                                  init_account_priv_key.get_public_key(),\n                                                  init_account_priv_key.get_public_key(),\n                                                  true);\n      genesis_state.initial_committee_candidates.push_back({name});\n      genesis_state.initial_witness_candidates.push_back({name, init_account_priv_key.get_public_key()});\n   }\n   genesis_state.initial_parameters.get_mutable_fees().zero_all_fees();\n   return genesis_state;\n}\n\nBOOST_AUTO_TEST_SUITE(block_tests)\n\nBOOST_AUTO_TEST_CASE( block_database_test )\n{\n   try {\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n\n      block_database bdb;\n      bdb.open( data_dir.path() );\n      FC_ASSERT( bdb.is_open() );\n      bdb.close();\n      FC_ASSERT( !bdb.is_open() );\n      bdb.open( data_dir.path() );\n\n      clearable_block b;\n      for( uint32_t i = 0; i < 5; ++i )\n      {\n         if( i > 0 ) b.previous = b.id();\n         b.witness = witness_id_type(i+1);\n         b.clear();\n         bdb.store( b.id(), b );\n\n         auto fetch = bdb.fetch_by_number( b.block_num() );\n         FC_ASSERT( fetch.valid() );\n         FC_ASSERT( fetch->witness ==  b.witness );\n         fetch = bdb.fetch_by_number( i+1 );\n         FC_ASSERT( fetch.valid() );\n         FC_ASSERT( fetch->witness ==  b.witness );\n         fetch = bdb.fetch_optional( b.id() );\n         FC_ASSERT( fetch.valid() );\n         FC_ASSERT( fetch->witness ==  b.witness );\n      }\n\n      for( uint32_t i = 1; i < 5; ++i )\n      {\n         auto blk = bdb.fetch_by_number( i );\n         FC_ASSERT( blk.valid() );\n         FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) );\n      }\n\n      auto last = bdb.last();\n      FC_ASSERT( last );\n      FC_ASSERT( last->id() == b.id() );\n\n      bdb.close();\n      bdb.open( data_dir.path() );\n      last = bdb.last();\n      FC_ASSERT( last );\n      FC_ASSERT( last->id() == b.id() );\n\n      for( uint32_t i = 0; i < 5; ++i )\n      {\n         auto blk = bdb.fetch_by_number( i+1 );\n         FC_ASSERT( blk.valid() );\n         FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) );\n      }\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( generate_empty_blocks )\n{\n   try {\n      fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n      signed_block b;\n\n      // TODO:  Don't generate this here\n      auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n      signed_block cutoff_block;\n      uint32_t last_block;\n      {\n         database db;\n         db.open(data_dir.path(), make_genesis, \"TEST\" );\n         b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n\n         // TODO:  Change this test when we correct #406\n         // n.b. we generate GRAPHENE_MIN_UNDO_HISTORY+1 extra blocks which will be discarded on save\n         for( uint32_t i = 1; ; ++i )\n         {\n            BOOST_CHECK( db.head_block_id() == b.id() );\n            //witness_id_type prev_witness = b.witness;\n            witness_id_type cur_witness = db.get_scheduled_witness(1);\n            //BOOST_CHECK( cur_witness != prev_witness );\n            b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing);\n            BOOST_CHECK( b.witness == cur_witness );\n            uint32_t cutoff_height = db.get_dynamic_global_properties().last_irreversible_block_num;\n            if( cutoff_height >= 200 )\n            {\n               cutoff_block = *(db.fetch_block_by_number( cutoff_height ));\n               last_block = db.head_block_num();\n               break;\n            }\n         }\n         db.close();\n      }\n      {\n         database db;\n         db.open(data_dir.path(), []{return genesis_state_type();}, \"TEST\");\n         BOOST_CHECK_EQUAL( db.head_block_num(), last_block );\n         while( db.head_block_num() > cutoff_block.block_num() )\n            db.pop_block();\n         b = cutoff_block;\n         for( uint32_t i = 0; i < 200; ++i )\n         {\n            BOOST_CHECK( db.head_block_id() == b.id() );\n            //witness_id_type prev_witness = b.witness;\n            witness_id_type cur_witness = db.get_scheduled_witness(1);\n            //BOOST_CHECK( cur_witness != prev_witness );\n            b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing);\n         }\n         BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num()+200 );\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( undo_block )\n{\n   try {\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n      {\n         database db;\n         db.open(data_dir.path(), make_genesis, \"TEST\");\n         fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );\n         std::vector< time_point_sec > time_stack;\n\n         auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n         for( uint32_t i = 0; i < 5; ++i )\n         {\n            now = db.get_slot_time(1);\n            time_stack.push_back( now );\n            auto b = db.generate_block( now, db.get_scheduled_witness( 1 ), init_account_priv_key, database::skip_nothing );\n         }\n         BOOST_CHECK( db.head_block_num() == 5 );\n         BOOST_CHECK( db.head_block_time() == now );\n         db.pop_block();\n         time_stack.pop_back();\n         now = time_stack.back();\n         BOOST_CHECK( db.head_block_num() == 4 );\n         BOOST_CHECK( db.head_block_time() == now );\n         db.pop_block();\n         time_stack.pop_back();\n         now = time_stack.back();\n         BOOST_CHECK( db.head_block_num() == 3 );\n         BOOST_CHECK( db.head_block_time() == now );\n         db.pop_block();\n         time_stack.pop_back();\n         now = time_stack.back();\n         BOOST_CHECK( db.head_block_num() == 2 );\n         BOOST_CHECK( db.head_block_time() == now );\n         for( uint32_t i = 0; i < 5; ++i )\n         {\n            now = db.get_slot_time(1);\n            time_stack.push_back( now );\n            auto b = db.generate_block( now, db.get_scheduled_witness( 1 ), init_account_priv_key, database::skip_nothing );\n         }\n         BOOST_CHECK( db.head_block_num() == 7 );\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( change_signing_key_test )\n{\n   try {\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n\n      auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n      auto init_pub_key = init_account_priv_key.get_public_key();\n      auto new_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"new_key\")) );\n      auto new_pub_key = new_key.get_public_key();\n\n      std::map< public_key_type, fc::ecc::private_key > key_map;\n      key_map[init_pub_key] = init_account_priv_key;\n      key_map[new_pub_key] = new_key;\n\n      std::set< witness_id_type > witnesses;\n      for( uint32_t i = 0; i <= 11; ++i ) // 11 init witnesses and 0 is reserved\n         witnesses.insert( witness_id_type(i) );\n\n      auto change_signing_key = [&init_account_priv_key]( database& db, witness_id_type wit, public_key_type new_signing_key ) {\n         witness_update_operation wuop;\n         wuop.witness_account = wit(db).witness_account;\n         wuop.witness = wit;\n         wuop.new_signing_key = new_signing_key;\n         signed_transaction wu_trx;\n         wu_trx.operations.push_back( wuop );\n         wu_trx.set_reference_block( db.head_block_id() );\n         wu_trx.set_expiration( db.head_block_time()\n                               + fc::seconds( 0x1000 * db.get_global_properties().parameters.block_interval ) );\n         wu_trx.sign( init_account_priv_key, db.get_chain_id() );\n         PUSH_TX( db, wu_trx, 0 );\n      };\n\n      {\n         database db;\n\n         // open database\n         db.open(data_dir.path(), make_genesis, \"TEST\");\n\n         // generate some empty blocks with init keys\n         for( uint32_t i = 0; i < 30; ++i )\n         {\n            auto now = db.get_slot_time(1);\n            auto next_witness = db.get_scheduled_witness( 1 );\n            db.generate_block( now, next_witness, init_account_priv_key, database::skip_nothing );\n         }\n\n         // generate some blocks and change keys in same block\n         for( uint32_t i = 0; i < 9; ++i )\n         {\n            auto now = db.get_slot_time(1);\n            auto next_witness = db.get_scheduled_witness( 1 );\n            public_key_type current_key = next_witness(db).signing_key;\n            change_signing_key( db, next_witness, new_key.get_public_key() );\n            idump( (i)(now)(next_witness) );\n            auto b = db.generate_block( now, next_witness, key_map[current_key], database::skip_nothing );\n            idump( (b) );\n         }\n\n         // pop a few blocks and clear pending, some signing keys should be changed back\n         for( uint32_t i = 0; i < 4; ++i )\n         {\n            db.pop_block();\n         }\n         db._popped_tx.clear();\n         db.clear_pending();\n\n         // generate a few blocks and change keys in same block\n         for( uint32_t i = 0; i < 2; ++i )\n         {\n            auto now = db.get_slot_time(1);\n            auto next_witness = db.get_scheduled_witness( 1 );\n            public_key_type current_key = next_witness(db).signing_key;\n            change_signing_key( db, next_witness, new_key.get_public_key() );\n            idump( (i)(now)(next_witness) );\n            auto b = db.generate_block( now, next_witness, key_map[current_key], database::skip_nothing );\n            idump( (b) );\n         }\n\n         // generate some blocks but don't change a key\n         for( uint32_t i = 0; i < 25; ++i )\n         {\n            auto now = db.get_slot_time(1);\n            auto next_witness = db.get_scheduled_witness( 1 );\n            public_key_type current_key = next_witness(db).signing_key;\n            idump( (i)(now)(next_witness) );\n            auto b = db.generate_block( now, next_witness, key_map[current_key], database::skip_nothing );\n            idump( (b) );\n         }\n\n         // close the database, flush all data to disk\n         db.close();\n      }\n      {\n         database db;\n\n         // reopen database, all data should be unchanged\n         db.open(data_dir.path(), make_genesis, \"TEST\");\n\n         // generate more blocks and change keys in same block\n         for( uint32_t i = 0; i < 25; ++i )\n         {\n            auto now = db.get_slot_time(1);\n            auto next_witness = db.get_scheduled_witness( 1 );\n            public_key_type current_key = next_witness(db).signing_key;\n            change_signing_key( db, next_witness, new_key.get_public_key() );\n            idump( (i)(now)(next_witness) );\n            auto b = db.generate_block( now, next_witness, key_map[current_key], database::skip_nothing );\n            idump( (b) );\n         }\n\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( fork_blocks )\n{\n   try {\n      fc::temp_directory data_dir1( graphene::utilities::temp_directory_path() );\n      fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() );\n\n      database db1;\n      db1.open(data_dir1.path(), make_genesis, \"TEST\");\n      database db2;\n      db2.open(data_dir2.path(), make_genesis, \"TEST\");\n      BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );\n\n      auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n\n      {\n         const auto first_slot = db1.get_slot_at_time( fc::time_point::now() );\n         auto b = db1.generate_block( db1.get_slot_time(first_slot), db1.get_scheduled_witness(first_slot),\n                                      init_account_priv_key, database::skip_nothing);\n         PUSH_BLOCK( db2, b );\n      }\n\n      BOOST_TEST_MESSAGE( \"Adding blocks 2 through 11\" );\n      for( uint32_t i = 1; i <= 10; ++i )\n      {\n         auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n         try {\n            PUSH_BLOCK( db2, b );\n         } FC_CAPTURE_AND_RETHROW( (\"db2\") );\n      }\n\n      for( uint32_t j = 0; j <= 4; j += 4 )\n      {\n         // add blocks 12 through 14 to db1 only\n         BOOST_TEST_MESSAGE( \"Adding 3 blocks to db1 only\" );\n         for( uint32_t i = 12 + j; i <= 14 + j; ++i )\n         {\n            BOOST_TEST_MESSAGE( i );\n            auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n         }\n         string db1_tip = db1.head_block_id().str();\n\n         // add different blocks 12 through 14 to db2 only\n         BOOST_TEST_MESSAGE( \"Add 3 different blocks to db2 only\" );\n         uint32_t next_slot = 3;\n         for( uint32_t i = 12 + j; i <= 14 + j; ++i )\n         {\n            BOOST_TEST_MESSAGE( i );\n            auto b =  db2.generate_block(db2.get_slot_time(next_slot), db2.get_scheduled_witness(next_slot), init_account_priv_key, database::skip_nothing);\n            next_slot = 1;\n            // notify both databases of the new block.\n            // only db2 should switch to the new fork, db1 should not\n            PUSH_BLOCK( db1, b );\n            BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip);\n            BOOST_CHECK_EQUAL(db2.head_block_id().str(), b.id().str());\n         }\n\n         //The two databases are on distinct forks now, but at the same height.\n         BOOST_CHECK_EQUAL(db1.head_block_num(), 14u + j);\n         BOOST_CHECK_EQUAL(db2.head_block_num(), 14u + j);\n         BOOST_CHECK( db1.head_block_id() != db2.head_block_id() );\n\n         //Make a block on db2, make it invalid, then\n         //pass it to db1 and assert that db1 doesn't switch to the new fork.\n         signed_block good_block;\n         {\n            auto b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n            good_block = b;\n            b.transactions.emplace_back(signed_transaction());\n            b.transactions.back().operations.emplace_back(transfer_operation());\n            b.sign( init_account_priv_key );\n            BOOST_CHECK_EQUAL(b.block_num(), 15u + j);\n            GRAPHENE_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception);\n\n            // At this point, `fetch_block_by_number` will fetch block from fork_db,\n            //    so unable to reproduce the issue which is fixed in PR #938\n            //    https://github.com/bitshares/bitshares-core/pull/938\n            fc::optional<signed_block> previous_block = db1.fetch_block_by_number(1);\n            BOOST_CHECK ( previous_block.valid() );\n            uint32_t db1_blocks = db1.head_block_num();\n            for( uint32_t curr_block_num = 2; curr_block_num <= db1_blocks; ++curr_block_num )\n            {\n               fc::optional<signed_block> curr_block = db1.fetch_block_by_number( curr_block_num );\n               BOOST_CHECK( curr_block.valid() );\n               BOOST_CHECK_EQUAL( curr_block->previous.str(), previous_block->id().str() );\n               previous_block = curr_block;\n            }\n         }\n         BOOST_CHECK_EQUAL(db1.head_block_num(), 14u + j);\n         BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip);\n\n         if( j == 0 )\n         {\n            // assert that db1 switches to new fork with good block\n            BOOST_CHECK_EQUAL(db2.head_block_num(), 15u + j);\n            PUSH_BLOCK( db1, good_block );\n            BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str());\n         }\n      }\n\n      // generate more blocks to push the forked blocks out of fork_db\n      BOOST_TEST_MESSAGE( \"Adding more blocks to db1, push the forked blocks out of fork_db\" );\n      for( uint32_t i = 1; i <= 50; ++i )\n      {\n         db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      }\n\n      {\n         // PR #938 make sure db is in a good state https://github.com/bitshares/bitshares-core/pull/938\n         BOOST_TEST_MESSAGE( \"Checking whether all blocks on disk are good\" );\n         fc::optional<signed_block> previous_block = db1.fetch_block_by_number(1);\n         BOOST_CHECK ( previous_block.valid() );\n         uint32_t db1_blocks = db1.head_block_num();\n         for( uint32_t curr_block_num = 2; curr_block_num <= db1_blocks; ++curr_block_num )\n         {\n            fc::optional<signed_block> curr_block = db1.fetch_block_by_number( curr_block_num );\n            BOOST_CHECK( curr_block.valid() );\n            BOOST_CHECK_EQUAL( curr_block->previous.str(), previous_block->id().str() );\n            previous_block = curr_block;\n         }\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\n/**\n *  These test has been disabled, out of order blocks should result in the node getting disconnected.\n *  \nBOOST_AUTO_TEST_CASE( fork_db_tests )\n{\n   try {\n     fork_database fdb;\n     signed_block prev;\n     signed_block skipped_block;\n     for( uint32_t i = 0; i < 2000; ++i )\n     {\n        signed_block b;\n        b.previous = prev.id();\n        if( b.block_num() == 1800 )\n           skipped_block = b;\n        else\n           fdb.push_block( b );\n        prev = b;\n     }\n     auto head = fdb.head();\n     FC_ASSERT( head && head->data.block_num() == 1799 );\n\n     fdb.push_block(skipped_block);\n     head = fdb.head();\n     FC_ASSERT( head && head->data.block_num() == 2001, \"\", (\"head\",head->data.block_num()) );\n  } FC_LOG_AND_RETHROW() \n}\nBOOST_AUTO_TEST_CASE( out_of_order_blocks )\n{\n   try {\n      fc::temp_directory data_dir1( graphene::utilities::temp_directory_path() );\n      fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() );\n\n      database db1;\n      db1.open(data_dir1.path(), make_genesis);\n      database db2;\n      db2.open(data_dir2.path(), make_genesis);\n      BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );\n\n      auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n      auto b1 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b2 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b3 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b4 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b5 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b6 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b7 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b8 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b9 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b10 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b11 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      auto b12 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      BOOST_CHECK_EQUAL(db1.head_block_num(), 12);\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 0);\n      PUSH_BLOCK( db2, b1 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 1);\n      PUSH_BLOCK( db2, b3 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 1);\n      PUSH_BLOCK( db2, b2 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 3);\n      PUSH_BLOCK( db2, b5 );\n      PUSH_BLOCK( db2, b6 );\n      PUSH_BLOCK( db2, b7 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 3);\n      PUSH_BLOCK( db2, b4 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 7);\n      PUSH_BLOCK( db2, b8 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 8);\n      PUSH_BLOCK( db2, b11 );\n      PUSH_BLOCK( db2, b10 );\n      PUSH_BLOCK( db2, b12 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 8);\n      PUSH_BLOCK( db2, b9 );\n      BOOST_CHECK_EQUAL(db2.head_block_num(), 12);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n */\n\nBOOST_AUTO_TEST_CASE( undo_pending )\n{\n   try {\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n      {\n         database db;\n         db.open(data_dir.path(), make_genesis, \"TEST\");\n\n         auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n         public_key_type init_account_pub_key  = init_account_priv_key.get_public_key();\n         const graphene::db::index& account_idx = db.get_index(protocol_ids, account_object_type);\n\n         transfer_operation t;\n         t.to = account_id_type(1);\n         t.amount = asset( 10000000 );\n         {\n            signed_transaction trx;\n            set_expiration( db, trx );\n\n            trx.operations.push_back(t);\n            PUSH_TX( db, trx, ~0 );\n\n            auto b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, ~0);\n         }\n\n         signed_transaction trx;\n         set_expiration( db, trx );\n         account_id_type nathan_id = account_idx.get_next_id();\n         account_create_operation cop;\n         cop.registrar = GRAPHENE_TEMP_ACCOUNT;\n         cop.name = \"nathan\";\n         cop.owner = authority(1, init_account_pub_key, 1);\n         cop.active = cop.owner;\n         trx.operations.push_back(cop);\n         //sign( trx,  init_account_priv_key  );\n         PUSH_TX( db, trx );\n\n         auto b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n\n         BOOST_CHECK(nathan_id(db).name == \"nathan\");\n\n         trx.clear();\n         set_expiration( db, trx );\n         t.fee = asset(1);\n         t.from = account_id_type(1);\n         t.to = nathan_id;\n         t.amount = asset(5000);\n         trx.operations.push_back(t);\n         PUSH_TX(db, trx, ~0);\n         trx.clear();\n         set_expiration( db, trx );\n         trx.operations.push_back(t);\n         PUSH_TX(db, trx, ~0);\n\n         BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 10000);\n         db.clear_pending();\n         BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 0);\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( switch_forks_undo_create )\n{\n   try {\n      fc::temp_directory dir1( graphene::utilities::temp_directory_path() ),\n                         dir2( graphene::utilities::temp_directory_path() );\n      database db1,\n               db2;\n      db1.open(dir1.path(), make_genesis, \"TEST\");\n      db2.open(dir2.path(), make_genesis, \"TEST\");\n      BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );\n\n      auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n      public_key_type init_account_pub_key  = init_account_priv_key.get_public_key();\n      const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type);\n\n      signed_transaction trx;\n      set_expiration( db1, trx );\n      account_id_type nathan_id = account_idx.get_next_id();\n      account_create_operation cop;\n      cop.registrar = GRAPHENE_TEMP_ACCOUNT;\n      cop.name = \"nathan\";\n      cop.owner = authority(1, init_account_pub_key, 1);\n      cop.active = cop.owner;\n      trx.operations.push_back(cop);\n      PUSH_TX( db1, trx );\n\n      // generate blocks\n      // db1 : A\n      // db2 : B C D\n\n      auto aw = db1.get_global_properties().active_witnesses;\n      auto b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n\n      BOOST_CHECK(nathan_id(db1).name == \"nathan\");\n\n      b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      db1.push_block(b);\n      aw = db2.get_global_properties().active_witnesses;\n      b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      db1.push_block(b);\n      GRAPHENE_REQUIRE_THROW(nathan_id(db2), fc::exception);\n      nathan_id(db1); /// it should be included in the pending state\n      db1.clear_pending(); // clear it so that we can verify it was properly removed from pending state.\n      GRAPHENE_REQUIRE_THROW(nathan_id(db1), fc::exception);\n\n      PUSH_TX( db2, trx );\n\n      aw = db2.get_global_properties().active_witnesses;\n      b = db2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      db1.push_block(b);\n\n      BOOST_CHECK(nathan_id(db1).name == \"nathan\");\n      BOOST_CHECK(nathan_id(db2).name == \"nathan\");\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( duplicate_transactions )\n{\n   try {\n      fc::temp_directory dir1( graphene::utilities::temp_directory_path() ),\n                         dir2( graphene::utilities::temp_directory_path() );\n      database db1,\n               db2;\n      db1.open(dir1.path(), make_genesis, \"TEST\");\n      db2.open(dir2.path(), make_genesis, \"TEST\");\n      BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() );\n\n      auto skip_sigs = database::skip_transaction_signatures;\n\n      auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n      public_key_type init_account_pub_key  = init_account_priv_key.get_public_key();\n      const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type);\n\n      signed_transaction trx;\n      set_expiration( db1, trx );\n      account_id_type nathan_id = account_idx.get_next_id();\n      account_create_operation cop;\n      cop.name = \"nathan\";\n      cop.owner = authority(1, init_account_pub_key, 1);\n      cop.active = cop.owner;\n      trx.operations.push_back(cop);\n      trx.sign( init_account_priv_key, db1.get_chain_id() );\n      PUSH_TX( db1, trx, skip_sigs );\n\n      trx = decltype(trx)();\n      set_expiration( db1, trx );\n      transfer_operation t;\n      t.to = nathan_id;\n      t.amount = asset(500);\n      trx.operations.push_back(t);\n      trx.sign( init_account_priv_key, db1.get_chain_id() );\n      PUSH_TX( db1, trx, skip_sigs );\n\n      GRAPHENE_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception);\n\n      auto b = db1.generate_block( db1.get_slot_time(1), db1.get_scheduled_witness( 1 ), init_account_priv_key, skip_sigs );\n      PUSH_BLOCK( db2, b, skip_sigs );\n\n      GRAPHENE_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception);\n      GRAPHENE_CHECK_THROW(PUSH_TX( db2, trx, skip_sigs ), fc::exception);\n      BOOST_CHECK_EQUAL(db1.get_balance(nathan_id, asset_id_type()).amount.value, 500);\n      BOOST_CHECK_EQUAL(db2.get_balance(nathan_id, asset_id_type()).amount.value, 500);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( tapos )\n{\n   try {\n      fc::temp_directory dir1( graphene::utilities::temp_directory_path() );\n      database db1;\n      db1.open(dir1.path(), make_genesis, \"TEST\");\n\n      const account_object& init1 = *db1.get_index_type<account_index>().indices().get<by_name>().find(\"init1\");\n\n      auto init_account_priv_key  = fc::ecc::private_key::regenerate(fc::sha256::hash(string(\"null_key\")) );\n      public_key_type init_account_pub_key  = init_account_priv_key.get_public_key();\n      const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type);\n\n      auto b = db1.generate_block( db1.get_slot_time(1), db1.get_scheduled_witness( 1 ), init_account_priv_key, database::skip_nothing);\n\n      signed_transaction trx;\n      //This transaction must be in the next block after its reference, or it is invalid.\n      trx.set_expiration( db1.head_block_time() ); //db1.get_slot_time(1) );\n      trx.set_reference_block( db1.head_block_id() );\n\n      account_id_type nathan_id = account_idx.get_next_id();\n      account_create_operation cop;\n      cop.registrar = init1.id;\n      cop.name = \"nathan\";\n      cop.owner = authority(1, init_account_pub_key, 1);\n      cop.active = cop.owner;\n      trx.operations.push_back(cop);\n      trx.sign( init_account_priv_key, db1.get_chain_id() );\n      PUSH_TX(db1, trx);\n      b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);\n      trx.clear();\n\n      transfer_operation t;\n      t.to = nathan_id;\n      t.amount = asset(50);\n      trx.operations.push_back(t);\n      trx.sign( init_account_priv_key, db1.get_chain_id() );\n      //relative_expiration is 1, but ref block is 2 blocks old, so this should fail.\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures ), fc::exception);\n      set_expiration( db1, trx );\n      trx.clear_signatures();\n      trx.sign( init_account_priv_key, db1.get_chain_id() );\n      PUSH_TX( db1, trx, database::skip_transaction_signatures );\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( optional_tapos, database_fixture )\n{\n   try\n   {\n      ACTORS( (alice)(bob) );\n\n      generate_block();\n\n      BOOST_TEST_MESSAGE( \"Create transaction\" );\n\n      transfer( account_id_type(), alice_id, asset( 1000000 ) );\n      transfer_operation op;\n      op.from = alice_id;\n      op.to = bob_id;\n      op.amount = asset( 1000 );\n      signed_transaction tx;\n      tx.operations.push_back( op );\n      set_expiration( db, tx );\n\n      BOOST_TEST_MESSAGE( \"ref_block_num=0, ref_block_prefix=0\" );\n\n      tx.ref_block_num = 0;\n      tx.ref_block_prefix = 0;\n      tx.clear_signatures();\n      sign( tx, alice_private_key );\n      PUSH_TX( db, tx );\n\n      BOOST_TEST_MESSAGE( \"proper ref_block_num, ref_block_prefix\" );\n\n      set_expiration( db, tx );\n      tx.clear_signatures();\n      sign( tx, alice_private_key );\n      PUSH_TX( db, tx );\n\n      BOOST_TEST_MESSAGE( \"ref_block_num=0, ref_block_prefix=12345678\" );\n\n      tx.ref_block_num = 0;\n      tx.ref_block_prefix = 0x12345678;\n      tx.clear_signatures();\n      sign( tx, alice_private_key );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"ref_block_num=1, ref_block_prefix=12345678\" );\n\n      tx.ref_block_num = 1;\n      tx.ref_block_prefix = 0x12345678;\n      tx.clear_signatures();\n      sign( tx, alice_private_key );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"ref_block_num=9999, ref_block_prefix=12345678\" );\n\n      tx.ref_block_num = 9999;\n      tx.ref_block_prefix = 0x12345678;\n      tx.clear_signatures();\n      sign( tx, alice_private_key );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception );\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture )\n{\n   try {\n      generate_block();\n      BOOST_CHECK_EQUAL(db.head_block_num(), 2u);\n\n      fc::time_point_sec maintenence_time = db.get_dynamic_global_properties().next_maintenance_time;\n      BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch());\n      auto initial_properties = db.get_global_properties();\n      const account_object& nathan = create_account(\"nathan\");\n      upgrade_to_lifetime_member(nathan);\n      const committee_member_object nathans_committee_member = create_committee_member(nathan);\n      {\n         account_update_operation op;\n         op.account = nathan.id;\n         op.new_options = nathan.options;\n         op.new_options->votes.insert(nathans_committee_member.vote_id);\n         trx.operations.push_back(op);\n         PUSH_TX( db, trx, ~0 );\n         trx.operations.clear();\n      }\n      transfer(account_id_type()(db), nathan, asset(5000));\n\n      generate_blocks(maintenence_time - initial_properties.parameters.block_interval);\n      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_transaction_size,\n                        initial_properties.parameters.maximum_transaction_size);\n      BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().next_maintenance_time.sec_since_epoch(),\n                        db.head_block_time().sec_since_epoch() + db.get_global_properties().parameters.block_interval);\n      BOOST_CHECK(db.get_global_properties().active_witnesses == initial_properties.active_witnesses);\n      BOOST_CHECK(db.get_global_properties().active_committee_members == initial_properties.active_committee_members);\n\n      generate_block();\n\n      auto new_properties = db.get_global_properties();\n      BOOST_CHECK(new_properties.active_committee_members != initial_properties.active_committee_members);\n      BOOST_CHECK(std::find(new_properties.active_committee_members.begin(),\n                            new_properties.active_committee_members.end(), nathans_committee_member.id) !=\n                  new_properties.active_committee_members.end());\n      BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().next_maintenance_time.sec_since_epoch(),\n                        maintenence_time.sec_since_epoch() + new_properties.parameters.maintenance_interval);\n      maintenence_time = db.get_dynamic_global_properties().next_maintenance_time;\n      BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch());\n      db.close();\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\nBOOST_FIXTURE_TEST_CASE( limit_order_expiration, database_fixture )\n{ try {\n   //Get a sane head block time\n   generate_block();\n\n   auto* test = &create_bitasset(\"MIATEST\");\n   auto* core = &asset_id_type()(db);\n   auto* nathan = &create_account(\"nathan\");\n   auto* committee = &account_id_type()(db);\n\n   transfer(*committee, *nathan, core->amount(50000));\n\n   BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 50000 );\n\n   limit_order_create_operation op;\n   op.seller = nathan->id;\n   op.amount_to_sell = core->amount(500);\n   op.min_to_receive = test->amount(500);\n   op.expiration = db.head_block_time() + fc::seconds(10);\n   trx.operations.push_back(op);\n   auto ptrx = PUSH_TX( db, trx, ~0 );\n\n   BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 );\n\n   auto ptrx_id = ptrx.operation_results.back().get<object_id_type>();\n   auto limit_index = db.get_index_type<limit_order_index>().indices();\n   auto limit_itr = limit_index.begin();\n   BOOST_REQUIRE( limit_itr != limit_index.end() );\n   BOOST_REQUIRE( limit_itr->id == ptrx_id );\n   BOOST_REQUIRE( db.find_object(limit_itr->id) );\n   BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 );\n   auto id = limit_itr->id;\n\n   generate_blocks(op.expiration, false);\n   test = &get_asset(\"MIATEST\");\n   core = &asset_id_type()(db);\n   nathan = &get_account(\"nathan\");\n   committee = &account_id_type()(db);\n\n   BOOST_CHECK(db.find_object(id) == nullptr);\n   BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 50000 );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture )\n{ try {\n   generate_block();\n   const auto& alice = account_id_type()(db);\n   ACTOR(bob);\n   asset amount(1000);\n\n   set_expiration( db, trx );\n   transfer_operation t;\n   t.from = alice.id;\n   t.to = bob.id;\n   t.amount = amount;\n   trx.operations.push_back(t);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n\n   PUSH_TX(db, trx, ~0);\n\n   trx.operations.clear();\n   t.from = bob.id;\n   t.to = alice.id;\n   t.amount = amount;\n   trx.operations.push_back(t);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n\n   BOOST_TEST_MESSAGE( \"Verify that not-signing causes an exception\" );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), fc::exception );\n\n   BOOST_TEST_MESSAGE( \"Verify that double-signing causes an exception\" );\n   sign( trx, bob_private_key );\n   sign( trx, bob_private_key );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), tx_duplicate_sig );\n\n   BOOST_TEST_MESSAGE( \"Verify that signing with an extra, unused key fails\" );\n   trx.signatures.pop_back();\n   sign( trx, generate_private_key(\"bogus\" ));\n   GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), tx_irrelevant_sig );\n\n   BOOST_TEST_MESSAGE( \"Verify that signing once with the proper key passes\" );\n   trx.signatures.pop_back();\n   PUSH_TX(db, trx, 0);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture )\n{ try {\n   generate_block();\n\n   db.modify(db.get_global_properties(), [](global_property_object& p) {\n      p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();\n   });\n\n   BOOST_TEST_MESSAGE( \"Creating a proposal to change the block_interval to 1 second\" );\n   {\n      proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time());\n      cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n      committee_member_update_global_parameters_operation uop;\n      uop.new_parameters.block_interval = 1;\n      cop.proposed_ops.emplace_back(uop);\n      trx.operations.push_back(cop);\n      PUSH_TX(db, trx);\n   }\n   BOOST_TEST_MESSAGE( \"Updating proposal by signing with the committee_member private key\" );\n   {\n      proposal_update_operation uop;\n      uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      uop.active_approvals_to_add = {get_account(\"init0\").get_id(), get_account(\"init1\").get_id(),\n                                     get_account(\"init2\").get_id(), get_account(\"init3\").get_id(),\n                                     get_account(\"init4\").get_id(), get_account(\"init5\").get_id(),\n                                     get_account(\"init6\").get_id(), get_account(\"init7\").get_id()};\n      trx.operations.push_back(uop);\n      sign( trx, init_account_priv_key );\n      /*\n      sign( trx, get_account(\"init1\" ).active.get_keys().front(),init_account_priv_key);\n      sign( trx, get_account(\"init2\" ).active.get_keys().front(),init_account_priv_key);\n      sign( trx, get_account(\"init3\" ).active.get_keys().front(),init_account_priv_key);\n      sign( trx, get_account(\"init4\" ).active.get_keys().front(),init_account_priv_key);\n      sign( trx, get_account(\"init5\" ).active.get_keys().front(),init_account_priv_key);\n      sign( trx, get_account(\"init6\" ).active.get_keys().front(),init_account_priv_key);\n      sign( trx, get_account(\"init7\" ).active.get_keys().front(),init_account_priv_key);\n      */\n      PUSH_TX(db, trx);\n      BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db));\n   }\n   BOOST_TEST_MESSAGE( \"Verifying that the interval didn't change immediately\" );\n\n   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.block_interval, 5u);\n   auto past_time = db.head_block_time().sec_since_epoch();\n   generate_block();\n   BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 5u);\n   generate_block();\n   BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 10u);\n\n   BOOST_TEST_MESSAGE( \"Generating blocks until proposal expires\" );\n   generate_blocks(proposal_id_type()(db).expiration_time + 5);\n   BOOST_TEST_MESSAGE( \"Verify that the block interval is still 5 seconds\" );\n   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.block_interval, 5u);\n\n   BOOST_TEST_MESSAGE( \"Generating blocks until next maintenance interval\" );\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();   // get the maintenance skip slots out of the way\n\n   BOOST_TEST_MESSAGE( \"Verify that the new block interval is 1 second\" );\n   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.block_interval, 1);\n   past_time = db.head_block_time().sec_since_epoch();\n   generate_block();\n   BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 1u);\n   generate_block();\n   BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 2u);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )\n{\n   try\n   {\n      uint32_t skip_flags = (\n           database::skip_witness_signature\n         | database::skip_transaction_signatures\n         );\n\n      const asset_object& core = asset_id_type()(db);\n\n      // Sam is the creator of accounts\n      private_key_type committee_key = init_account_priv_key;\n      private_key_type sam_key = generate_private_key(\"sam\");\n      account_object sam_account_object = create_account(\"sam\", sam_key);\n\n      //Get a sane head block time\n      generate_block( skip_flags );\n\n      db.modify(db.get_global_properties(), [](global_property_object& p) {\n         p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();\n      });\n\n      transaction tx;\n      processed_transaction ptx;\n\n      account_object committee_account_object = committee_account(db);\n      // transfer from committee account to Sam account\n      transfer(committee_account_object, sam_account_object, core.amount(100000));\n\n      generate_block(skip_flags);\n\n      create_account(\"alice\");\n      generate_block(skip_flags);\n      create_account(\"bob\");\n      generate_block(skip_flags);\n\n      db.pop_block();\n      db.pop_block();\n   } catch(const fc::exception& e) {\n      edump( (e.to_detail_string()) );\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture )\n{\n   try\n   {\n      generate_block();\n\n      auto rsf = [this]() -> string\n      {\n\t fc::uint128_t rsf = db.get_dynamic_global_properties().recent_slots_filled;\n         string result = \"\";\n         result.reserve(128);\n         for( int i=0; i<128; i++ )\n         {\n            result += rsf & 1 ? '1' : '0';\n            rsf >>= 1;\n         }\n         return result;\n      };\n\n      auto pct = []( uint32_t x ) -> uint32_t\n      {\n         return uint64_t( GRAPHENE_100_PERCENT ) * x / 128;\n      };\n\n      BOOST_CHECK_EQUAL( rsf(),\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), (uint32_t)GRAPHENE_100_PERCENT );\n\n      generate_block( ~0, init_account_priv_key, 1 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0111111111111111111111111111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(127) );\n\n      generate_block( ~0, init_account_priv_key, 1 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0101111111111111111111111111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(126) );\n\n      generate_block( ~0, init_account_priv_key, 2 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0010101111111111111111111111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(124) );\n\n      generate_block( ~0, init_account_priv_key, 3 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0001001010111111111111111111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(121) );\n\n      generate_block( ~0, init_account_priv_key, 5 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0000010001001010111111111111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(116) );\n\n      generate_block( ~0, init_account_priv_key, 8 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0000000010000010001001010111111111111111111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(108) );\n\n      generate_block( ~0, init_account_priv_key, 13 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0000000000000100000000100000100010010101111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) );\n\n      generate_block();\n      BOOST_CHECK_EQUAL( rsf(),\n         \"1000000000000010000000010000010001001010111111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) );\n\n      generate_block();\n      BOOST_CHECK_EQUAL( rsf(),\n         \"1100000000000001000000001000001000100101011111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) );\n\n      generate_block();\n      BOOST_CHECK_EQUAL( rsf(),\n         \"1110000000000000100000000100000100010010101111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) );\n\n      generate_block();\n      BOOST_CHECK_EQUAL( rsf(),\n         \"1111000000000000010000000010000010001001010111111111111111111111\"\n         \"1111111111111111111111111111111111111111111111111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(95) );\n\n      generate_block( ~0, init_account_priv_key, 64 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0000000000000000000000000000000000000000000000000000000000000000\"\n         \"1111100000000000001000000001000001000100101011111111111111111111\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(31) );\n\n      generate_block( ~0, init_account_priv_key, 32 );\n      BOOST_CHECK_EQUAL( rsf(),\n         \"0000000000000000000000000000000010000000000000000000000000000000\"\n         \"0000000000000000000000000000000001111100000000000001000000001000\"\n      );\n      BOOST_CHECK_EQUAL( db.witness_participation_rate(), pct(8) );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )\n{\n   try\n   {\n      ACTORS( (alice)(bob) );\n\n      auto generate_block = [&]( database& d, uint32_t skip ) -> signed_block\n      {\n         return d.generate_block(d.get_slot_time(1), d.get_scheduled_witness(1), init_account_priv_key, skip);\n      };\n\n      // tx's created by ACTORS() have bogus authority, so we need to\n      // skip_transaction_signatures in the block where they're included\n      signed_block b1 = generate_block(db, database::skip_transaction_signatures);\n\n      fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() );\n\n      database db2;\n      db2.open(data_dir2.path(), make_genesis, \"TEST\");\n      BOOST_CHECK( db.get_chain_id() == db2.get_chain_id() );\n\n      while( db2.head_block_num() < db.head_block_num() )\n      {\n         optional< signed_block > b = db.fetch_block_by_number( db2.head_block_num()+1 );\n         db2.push_block(*b, database::skip_witness_signature\n                           |database::skip_transaction_signatures );\n      }\n      BOOST_CHECK( db2.get( alice_id ).name == \"alice\" );\n      BOOST_CHECK( db2.get( bob_id ).name == \"bob\" );\n\n      db2.push_block(generate_block(db, database::skip_nothing));\n      transfer( account_id_type(), alice_id, asset( 1000 ) );\n      transfer( account_id_type(),   bob_id, asset( 1000 ) );\n      // need to skip authority check here as well for same reason as above\n      db2.push_block(generate_block(db, database::skip_transaction_signatures), database::skip_transaction_signatures);\n\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 1000);\n      BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 1000);\n      BOOST_CHECK_EQUAL(db2.get_balance(  bob_id, asset_id_type()).amount.value, 1000);\n\n      auto generate_and_send = [&]( int n )\n      {\n         for( int i=0; i<n; i++ )\n         {\n            signed_block b = generate_block(db2, database::skip_nothing);\n            PUSH_BLOCK( db, b );\n         }\n      };\n\n      auto generate_xfer_tx = [&]( account_id_type from, account_id_type to, share_type amount, int blocks_to_expire ) -> signed_transaction\n      {\n         signed_transaction tx;\n         transfer_operation xfer_op;\n         xfer_op.from = from;\n         xfer_op.to = to;\n         xfer_op.amount = asset( amount, asset_id_type() );\n         xfer_op.fee = asset( 0, asset_id_type() );\n         tx.operations.push_back( xfer_op );\n         tx.set_expiration( db.head_block_time() + blocks_to_expire * db.get_global_properties().parameters.block_interval );\n         if( from == alice_id )\n            sign( tx, alice_private_key );\n         else\n            sign( tx, bob_private_key );\n         return tx;\n      };\n\n      signed_transaction tx = generate_xfer_tx( alice_id, bob_id, 1000, 2 );\n      tx.set_expiration( db.head_block_time() + 2 * db.get_global_properties().parameters.block_interval );\n      tx.clear_signatures();\n      sign( tx, alice_private_key );\n      // put the tx in db tx cache\n      PUSH_TX( db, tx );\n\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value,    0);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 2000);\n\n      // generate some blocks with db2, make tx expire in db's cache\n      generate_and_send(3);\n\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 1000);\n\n      // generate a block with db and ensure we don't somehow apply it\n      PUSH_BLOCK(db2, generate_block(db, database::skip_nothing));\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 1000);\n\n      // now the tricky part...\n      // (A) Bob sends 1000 to Alice\n      // (B) Alice sends 2000 to Bob\n      // (C) Alice sends 500 to Bob\n      //\n      // We push AB, then receive a block containing C.\n      // we need to apply the block, then invalidate B in the cache.\n      // AB results in Alice having 0, Bob having 2000.\n      // C results in Alice having 500, Bob having 1500.\n      //\n      // This needs to occur while switching to a fork.\n      //\n\n      signed_transaction tx_a = generate_xfer_tx( bob_id, alice_id, 1000, 2 );\n      signed_transaction tx_b = generate_xfer_tx( alice_id, bob_id, 2000, 10 );\n      signed_transaction tx_c = generate_xfer_tx( alice_id, bob_id,  500, 10 );\n\n      generate_block( db, database::skip_nothing );\n\n      PUSH_TX( db, tx_a );\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 2000);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value,    0);\n\n      PUSH_TX( db, tx_b );\n      PUSH_TX( db2, tx_c );\n\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 0);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 2000);\n\n      BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 500);\n      BOOST_CHECK_EQUAL(db2.get_balance(  bob_id, asset_id_type()).amount.value, 1500);\n\n      // generate enough blocks on db2 to cause db to switch forks\n      generate_and_send(2);\n\n      // db should invalidate B, but still be applying A, so the states don't agree\n\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1500);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 500);\n\n      BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 500);\n      BOOST_CHECK_EQUAL(db2.get_balance(  bob_id, asset_id_type()).amount.value, 1500);\n\n      // This will cause A to expire in db\n      generate_and_send(1);\n\n      BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 500);\n      BOOST_CHECK_EQUAL(db.get_balance(  bob_id, asset_id_type()).amount.value, 1500);\n\n      BOOST_CHECK_EQUAL(db2.get_balance(alice_id, asset_id_type()).amount.value, 500);\n      BOOST_CHECK_EQUAL(db2.get_balance(  bob_id, asset_id_type()).amount.value, 1500);\n\n      // Make sure we can generate and accept a plain old empty block on top of all this!\n      generate_and_send(1);\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( genesis_reserve_ids )\n{\n   try\n   {\n      fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );\n      fc::temp_directory data_dir( graphene::utilities::temp_directory_path() );\n\n      uint32_t num_special_accounts = 100;\n      uint32_t num_special_assets = 30;\n\n      database db;\n      db.open( data_dir.path(), [&]() -> genesis_state_type\n      {\n         genesis_state_type genesis_state = make_genesis();\n         genesis_state_type::initial_asset_type usd;\n\n         usd.symbol = \"USD\";\n         usd.issuer_name = \"init0\";\n         usd.description = \"federally floated\";\n         usd.precision = 4;\n         usd.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n         usd.accumulated_fees = 0;\n         usd.is_bitasset = true;\n         \n         genesis_state.immutable_parameters.num_special_accounts = num_special_accounts;\n         genesis_state.immutable_parameters.num_special_assets = num_special_assets;\n         genesis_state.initial_assets.push_back( usd );\n\n         return genesis_state;\n      }, \"TEST\" );\n\n      const auto& acct_idx = db.get_index_type<account_index>().indices().get<by_name>();\n      auto acct_itr = acct_idx.find(\"init0\");\n      BOOST_REQUIRE( acct_itr != acct_idx.end() );\n      BOOST_CHECK( acct_itr->id == account_id_type( num_special_accounts ) );\n      \n      const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_symbol>();\n      auto asset_itr = asset_idx.find(\"USD\");\n      BOOST_REQUIRE( asset_itr != asset_idx.end() );\n      BOOST_CHECK( asset_itr->id == asset_id_type( num_special_assets ) );\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( miss_some_blocks, database_fixture )\n{ try {\n   // Witnesses scheduled incorrectly in genesis block - reschedule\n   generate_blocks( witness_schedule_id_type()(db).current_shuffled_witnesses.size() );\n   generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n   std::vector<witness_id_type> witnesses = witness_schedule_id_type()(db).current_shuffled_witnesses;\n   BOOST_CHECK_EQUAL( INITIAL_WITNESS_COUNT, witnesses.size() );\n   // database_fixture constructor calls generate_block once, signed by witnesses[0]\n   generate_block(); // witnesses[1]\n   generate_block(); // witnesses[2]\n   for( const auto& id : witnesses )\n      BOOST_CHECK_EQUAL( 0, id(db).total_missed );\n   // generate_blocks generates another block *now* (witnesses[3])\n   // and one at now+9 blocks (witnesses[12%9])\n   generate_blocks( db.head_block_time() + db.get_global_properties().parameters.block_interval * 9, true );\n   // i. e. 7 blocks are missed in between by witness[4..11%9]\n   for( uint32_t i = 0; i < witnesses.size(); i++ )\n      BOOST_CHECK_EQUAL( (i+6) % 9 < 2 ? 0 : 1, witnesses[i](db).total_missed );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_FIXTURE_TEST_CASE( miss_many_blocks, database_fixture )\n{\n   try\n   {\n      // Witnesses scheduled incorrectly in genesis block - reschedule\n      generate_blocks( witness_schedule_id_type()(db).current_shuffled_witnesses.size() );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      auto get_misses = []( database& db ) {\n         std::map< witness_id_type, uint32_t > misses;\n         for( const auto& witness_id : witness_schedule_id_type()(db).current_shuffled_witnesses )\n            misses[witness_id] = witness_id(db).total_missed;\n         return misses;\n      };\n      generate_block();\n      generate_block();\n      generate_block();\n      auto missed_before = get_misses( db );\n      // miss 9 maintenance intervals\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time + \n                       db.get_global_properties().parameters.maintenance_interval * 9, true );\n      generate_block();\n      generate_block();\n      generate_block();\n      auto missed_after = get_misses( db );\n      BOOST_CHECK_EQUAL( missed_before.size(), missed_after.size() );\n      for( const auto& miss : missed_before )\n      {\n          const auto& after = missed_after.find( miss.first );\n          BOOST_REQUIRE( after != missed_after.end() );\n          BOOST_CHECK_EQUAL( miss.second, after->second );\n      }\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture )\n{\n   try\n   {\n      const asset_object& core = asset_id_type()(db);\n      uint32_t skip_flags =\n          database::skip_transaction_dupe_check\n        | database::skip_witness_signature\n        | database::skip_transaction_signatures\n        ;\n\n      // Sam is the creator of accounts\n      private_key_type committee_key = init_account_priv_key;\n      private_key_type sam_key = generate_private_key(\"sam\");\n\n      //\n      // A = old key set\n      // B = new key set\n      //\n      // we measure how many times we test following four cases:\n      //\n      //                                     A-B        B-A\n      // alice     case_count[0]   A == B    empty      empty\n      // bob       case_count[1]   A  < B    empty      nonempty\n      // charlie   case_count[2]   B  < A    nonempty   empty\n      // dan       case_count[3]   A nc B    nonempty   nonempty\n      //\n      // and assert that all four cases were tested at least once\n      //\n      account_object sam_account_object = create_account( \"sam\", sam_key );\n\n      // upgrade sam to LTM\n      upgrade_to_lifetime_member(sam_account_object.id);\n\n      //Get a sane head block time\n      generate_block( skip_flags );\n\n      db.modify(db.get_global_properties(), [](global_property_object& p) {\n         p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();\n      });\n\n      transaction tx;\n      processed_transaction ptx;\n\n      account_object committee_account_object = committee_account(db);\n      // transfer from committee account to Sam account\n      transfer(committee_account_object, sam_account_object, core.amount(100000));\n\n      const int num_keys = 5;\n      vector< private_key_type > numbered_private_keys;\n      vector< vector< public_key_type > > numbered_key_id;\n      numbered_private_keys.reserve( num_keys );\n      numbered_key_id.push_back( vector<public_key_type>() );\n      numbered_key_id.push_back( vector<public_key_type>() );\n\n      for( int i=0; i<num_keys; i++ )\n      {\n         private_key_type privkey = generate_private_key(std::string(\"key_\") + std::to_string(i));\n         public_key_type pubkey = privkey.get_public_key();\n         address addr( pubkey );\n\n         numbered_private_keys.push_back( privkey );\n         numbered_key_id[0].push_back( pubkey );\n         //numbered_key_id[1].push_back( addr );\n      }\n\n      // each element of possible_key_sched is a list of exactly num_keys\n      // indices into numbered_key_id[use_address].  they are defined\n      // by repeating selected elements of\n      // numbered_private_keys given by a different selector.\n      vector< vector< int > > possible_key_sched;\n      const int num_key_sched = (1 << num_keys)-1;\n      possible_key_sched.reserve( num_key_sched );\n\n      for( int s=1; s<=num_key_sched; s++ )\n      {\n         vector< int > v;\n         int i = 0;\n         v.reserve( num_keys );\n         while( v.size() < num_keys )\n         {\n            if( s & (1 << i) )\n               v.push_back( i );\n            i++;\n            if( i >= num_keys )\n               i = 0;\n         }\n         possible_key_sched.push_back( v );\n      }\n\n      // we can only undo in blocks\n      generate_block( skip_flags );\n\n      std::cout << \"update_account_keys:  this test will take a few minutes...\\n\";\n\n      // Originally we had a loop here to go from use_address=0 to 1\n      // Live chain do not allow this so it had to be removed: https://github.com/bitshares/bitshares-core/issues/565\n      vector< public_key_type > key_ids = numbered_key_id[ 0 ];\n      for( int num_owner_keys=1; num_owner_keys<=2; num_owner_keys++ )\n      {\n         for( int num_active_keys=1; num_active_keys<=2; num_active_keys++ )\n         {\n            std::cout << 0 << num_owner_keys << num_active_keys << \"\\n\";\n            for( const vector< int >& key_sched_before : possible_key_sched )\n            {\n               auto it = key_sched_before.begin();\n               vector< const private_key_type* > owner_privkey;\n               vector< const public_key_type* > owner_keyid;\n               owner_privkey.reserve( num_owner_keys );\n\n               trx.clear();\n               account_create_operation create_op;\n               create_op.name = \"alice\";\n\n               for( int owner_index=0; owner_index<num_owner_keys; owner_index++ )\n               {\n                  int i = *(it++);\n                  create_op.owner.key_auths[ key_ids[ i ] ] = 1;\n                  owner_privkey.push_back( &numbered_private_keys[i] );\n                  owner_keyid.push_back( &key_ids[ i ] );\n               }\n               // size() < num_owner_keys is possible when some keys are duplicates\n               create_op.owner.weight_threshold = create_op.owner.key_auths.size();\n\n               for( int active_index=0; active_index<num_active_keys; active_index++ )\n                  create_op.active.key_auths[ key_ids[ *(it++) ] ] = 1;\n               // size() < num_active_keys is possible when some keys are duplicates\n               create_op.active.weight_threshold = create_op.active.key_auths.size();\n\n               create_op.options.memo_key = key_ids[ *(it++) ] ;\n               create_op.registrar = sam_account_object.id;\n               trx.operations.push_back( create_op );\n               // trx.sign( sam_key );\n\n               processed_transaction ptx_create = PUSH_TX( db, trx,\n                  database::skip_transaction_dupe_check |\n                  database::skip_transaction_signatures\n               );\n               account_id_type alice_account_id =\n                  ptx_create.operation_results[0]\n                  .get< object_id_type >();\n\n               generate_block( skip_flags );\n               for( const vector< int >& key_sched_after : possible_key_sched )\n               {\n                  auto it = key_sched_after.begin();\n\n                  trx.clear();\n                  account_update_operation update_op;\n                  update_op.account = alice_account_id;\n                  update_op.owner = authority();\n                  update_op.active = authority();\n                  update_op.new_options = create_op.options;\n\n                  for( int owner_index=0; owner_index<num_owner_keys; owner_index++ )\n                     update_op.owner->key_auths[ key_ids[ *(it++) ] ] = 1;\n                  // size() < num_owner_keys is possible when some keys are duplicates\n                  update_op.owner->weight_threshold = update_op.owner->key_auths.size();\n                  for( int active_index=0; active_index<num_active_keys; active_index++ )\n                     update_op.active->key_auths[ key_ids[ *(it++) ] ] = 1;\n                  // size() < num_active_keys is possible when some keys are duplicates\n                  update_op.active->weight_threshold = update_op.active->key_auths.size();\n                  FC_ASSERT( update_op.new_options.valid() );\n                  update_op.new_options->memo_key = key_ids[ *(it++) ] ;\n\n                  trx.operations.push_back( update_op );\n                  for( int i=0; i<int(create_op.owner.weight_threshold); i++)\n                  {\n                     sign( trx, *owner_privkey[i] );\n                     if( i < int(create_op.owner.weight_threshold-1) )\n                     {\n                        GRAPHENE_REQUIRE_THROW(PUSH_TX(db, trx), fc::exception);\n                     }\n                     else\n                     {\n                        PUSH_TX( db, trx,\n                           database::skip_transaction_dupe_check |\n                           database::skip_transaction_signatures );\n                     }\n                  }\n                  generate_block( skip_flags );\n\n                  db.pop_block();\n               }\n               db.pop_block();\n            }\n         }\n      }\n   }\n   catch( const fc::exception& e )\n   {\n      edump( (e.to_detail_string()) );\n      throw;\n   }\n}\n\n// The next test is commented out as it will fail in current bitshares implementaton\n// where \"witnesses should never sign 2 consecutive blocks\" is not enforced.\n// https://github.com/bitshares/bitshares-core/issues/565\n// Leaving it here to use it if we implement.later\n\n/**\n *  To have a secure random number we need to ensure that the same\n *  witness does not get to produce two blocks in a row.  There is\n *  always a chance that the last witness of one round will be the\n *  first witness of the next round.\n *\n *  This means that when we shuffle witness we need to make sure\n *  that there is at least N/2 witness between consecutive turns\n *  of the same witness.    This means that durring the random\n *  shuffle we need to restrict the placement of witness to maintain\n *  this invariant.\n *\n *  This test checks the requirement using Monte Carlo approach\n *  (produce lots of blocks and check the invariant holds).\n */\n/*\nBOOST_FIXTURE_TEST_CASE( witness_order_mc_test, database_fixture )\n{\n   try {\n      size_t num_witnesses = db.get_global_properties().active_witnesses.size();\n      size_t dmin = num_witnesses >> 1;\n\n      vector< witness_id_type > cur_round;\n      vector< witness_id_type > full_schedule;\n      // if we make the maximum witness count testable,\n      // we'll need to enlarge this.\n      std::bitset< 0x40 > witness_seen;\n      size_t total_blocks = 1000000;\n\n      cur_round.reserve( num_witnesses );\n      full_schedule.reserve( total_blocks );\n      cur_round.push_back( db.get_dynamic_global_properties().current_witness );\n\n      // we assert so the test doesn't continue, which would\n      // corrupt memory\n      assert( num_witnesses <= witness_seen.size() );\n\n      while( full_schedule.size() < total_blocks )\n      {\n         if( (db.head_block_num() & 0x3FFF) == 0 )\n         {\n             wdump( (db.head_block_num()) );\n         }\n         witness_id_type wid = db.get_scheduled_witness( 1 );\n         full_schedule.push_back( wid );\n         cur_round.push_back( wid );\n         if( cur_round.size() == num_witnesses )\n         {\n            // check that the current round contains exactly 1 copy\n            // of each witness\n            witness_seen.reset();\n            for( const witness_id_type& w : cur_round )\n            {\n               uint64_t inst = w.instance.value;\n               BOOST_CHECK( !witness_seen.test( inst ) );\n               assert( !witness_seen.test( inst ) );\n               witness_seen.set( inst );\n            }\n            cur_round.clear();\n         }\n         generate_block();\n      }\n\n      for( size_t i=0,m=full_schedule.size(); i<m; i++ )\n      {\n         for( size_t j=i+1,n=std::min( m, i+dmin ); j<n; j++ )\n         {\n            BOOST_CHECK( full_schedule[i] != full_schedule[j] );\n            assert( full_schedule[i] != full_schedule[j] );\n         }\n      }\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n*/\n\nBOOST_FIXTURE_TEST_CASE( tapos_rollover, database_fixture )\n{\n   try\n   {\n      ACTORS((alice)(bob));\n\n      BOOST_TEST_MESSAGE( \"Give Alice some money\" );\n      transfer(committee_account, alice_id, asset(10000));\n      generate_block();\n\n      BOOST_TEST_MESSAGE( \"Generate up to block 0xFF00\" );\n      generate_blocks( 0xFF00 );\n      signed_transaction xfer_tx;\n\n      BOOST_TEST_MESSAGE( \"Transfer money at/about 0xFF00\" );\n      transfer_operation xfer_op;\n      xfer_op.from = alice_id;\n      xfer_op.to = bob_id;\n      xfer_op.amount = asset(1000);\n\n      xfer_tx.operations.push_back( xfer_op );\n      xfer_tx.set_expiration( db.head_block_time() + fc::seconds( 0x1000 * db.get_global_properties().parameters.block_interval ) );\n      xfer_tx.set_reference_block( db.head_block_id() );\n\n      sign( xfer_tx, alice_private_key );\n      PUSH_TX( db, xfer_tx, 0 );\n      generate_block();\n\n      BOOST_TEST_MESSAGE( \"Sign new tx's\" );\n      xfer_tx.set_expiration( db.head_block_time() + fc::seconds( 0x1000 * db.get_global_properties().parameters.block_interval ) );\n      xfer_tx.set_reference_block( db.head_block_id() );\n      xfer_tx.clear_signatures();\n      sign( xfer_tx, alice_private_key );\n\n      BOOST_TEST_MESSAGE( \"Generate up to block 0x10010\" );\n      generate_blocks( 0x110 );\n\n      BOOST_TEST_MESSAGE( \"Transfer at/about block 0x10010 using reference block at/about 0xFF00\" );\n      PUSH_TX( db, xfer_tx, 0 );\n      generate_block();\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_FIXTURE_TEST_CASE( temp_account_balance, database_fixture )\n{ try {\n   ACTORS( (alice) );\n   fund( alice );\n   create_user_issued_asset( \"UIA\" );\n\n   generate_block();\n   set_expiration( db, trx );\n\n   transfer_operation top;\n   top.amount = asset( 1000 );\n   top.from = alice_id;\n   top.to   = GRAPHENE_TEMP_ACCOUNT;\n   trx.operations.push_back( top );\n\n   limit_order_create_operation loc;\n   loc.amount_to_sell = top.amount;\n   loc.expiration = db.head_block_time() + 1;\n   loc.seller = GRAPHENE_TEMP_ACCOUNT;\n   loc.min_to_receive = asset( 1000, asset_id_type(1) );\n   trx.operations.push_back( loc );\n   sign( trx, alice_private_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n\n   generate_block();\n   generate_block();\n   generate_block();\n\n   top.to = GRAPHENE_COMMITTEE_ACCOUNT;\n   trx.operations.push_back( top );\n   set_expiration( db, trx );\n   trx.clear_signatures();\n   sign( trx, alice_private_key );\n   PUSH_TX( db, trx );\n\n   BOOST_CHECK( get_balance( GRAPHENE_TEMP_ACCOUNT, asset_id_type() ) > 0 );\n} FC_LOG_AND_RETHROW() }\n\n///\n/// This test case tries to\n/// * generate blocks when there are too many pending transactions,\n/// * push blocks that are too large.\n/// If we add some logging in signed_transaction::get_signature_keys(), we can see if the code will extract public key(s)\n/// from signature(s) of same transactions multiple times.\n/// See https://github.com/bitshares/bitshares-core/pull/1251\n///\nBOOST_FIXTURE_TEST_CASE( block_size_test, database_fixture )\n{\n   try\n   {\n      ACTORS((alice)(bob));\n\n      const fc::ecc::private_key& key = generate_private_key(\"null_key\");\n      BOOST_TEST_MESSAGE( \"Give Alice some money\" );\n      transfer(committee_account, alice_id, asset(10000000));\n      generate_block();\n\n      const size_t default_block_header_size = fc::raw::pack_size( signed_block_header() );\n      const auto& gpo = db.get_global_properties();\n      const auto block_interval = gpo.parameters.block_interval;\n      idump( (db.head_block_num())(default_block_header_size)(gpo.parameters.maximum_block_size) );\n\n      BOOST_TEST_MESSAGE( \"Start\" );\n      // Note: a signed transaction with a transfer operation inside is at least 102 bytes;\n      //       after processed, it become 103 bytes;\n      //       an empty block is 112 bytes;\n      //       a block with a transfer is 215 bytes;\n      //       a block with 2 transfers is 318 bytes.\n      uint32_t large_block_count = 0;\n      for( uint64_t i = 90; i <= 230; ++i )\n      {\n         if( i > 120 && i < 200 ) // skip some\n            i = 200;\n\n         // Temporarily disable undo db and change max block size\n         db._undo_db.disable();\n         db.modify( gpo, [i,&default_block_header_size](global_property_object& p) {\n            p.parameters.maximum_block_size = default_block_header_size + i;\n         });\n         db._undo_db.enable();\n         idump( (i)(gpo.parameters.maximum_block_size) );\n\n         // push a transaction\n         signed_transaction xfer_tx;\n         transfer_operation xfer_op;\n         xfer_op.from = alice_id;\n         xfer_op.to = bob_id;\n         xfer_op.amount = asset(i);\n         xfer_tx.operations.push_back( xfer_op );\n         xfer_tx.set_expiration( db.head_block_time() + fc::seconds( 0x1000 * block_interval ) );\n         xfer_tx.set_reference_block( db.head_block_id() );\n         sign( xfer_tx, alice_private_key );\n         auto processed_tx = PUSH_TX( db, xfer_tx, database::skip_nothing );\n\n         // sign a temporary block\n         signed_block maybe_large_block;\n         maybe_large_block.transactions.push_back(processed_tx);\n         maybe_large_block.previous = db.head_block_id();\n         maybe_large_block.timestamp = db.get_slot_time(1);\n         maybe_large_block.transaction_merkle_root = maybe_large_block.calculate_merkle_root();\n         maybe_large_block.witness = db.get_scheduled_witness(1);\n         maybe_large_block.sign(key);\n         auto maybe_large_block_size = fc::raw::pack_size(maybe_large_block);\n         idump( (maybe_large_block_size) );\n\n         // should fail to push if it's too large\n         if( maybe_large_block_size > gpo.parameters.maximum_block_size )\n         {\n            ++large_block_count;\n            BOOST_CHECK_THROW( db.push_block(maybe_large_block), fc::exception );\n         }\n\n         // generate a block normally\n         auto good_block = db.generate_block( db.get_slot_time(1), db.get_scheduled_witness(1), key, database::skip_nothing );\n         idump( (fc::raw::pack_size(good_block)) );\n      }\n      // make sure we have tested at least once pushing a large block\n      BOOST_CHECK_GT( large_block_count, 0u );\n   }\n   catch( fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/bsip48_75_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"../common/database_fixture.hpp\"\n\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <boost/test/unit_test.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( bsip48_75_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( hardfork_protection_test )\n{\n   try {\n\n      // Proceeds to a recent hard fork\n      generate_blocks( HARDFORK_CORE_1270_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((sam)(feeder));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n      fund( feeder, asset(init_amount) );\n\n      uint16_t bitmask = ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK;\n      uint16_t uiamask = DEFAULT_UIA_ASSET_ISSUER_PERMISSION;\n\n      uint16_t bitflag = ~global_settle & ~committee_fed_asset; // high bits are set\n      uint16_t uiaflag = ~(bitmask ^ uiamask); // high bits are set\n\n      vector<operation> ops;\n\n      // Testing asset_create_operation\n      asset_create_operation acop;\n      acop.issuer = sam_id;\n      acop.symbol = \"SAMCOIN\";\n      acop.precision = 2;\n      acop.common_options.core_exchange_rate = price(asset(1,asset_id_type(1)),asset(1));\n      acop.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      acop.common_options.market_fee_percent = 100;\n      acop.common_options.flags = uiaflag;\n      acop.common_options.issuer_permissions = uiamask;\n\n      trx.operations.clear();\n      trx.operations.push_back( acop );\n\n      {\n         auto& op = trx.operations.front().get<asset_create_operation>();\n\n         // Unable to set new permission bits\n         op.common_options.issuer_permissions = ( uiamask | lock_max_supply );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.common_options.issuer_permissions = ( uiamask | disable_new_supply );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.bitasset_opts = bitasset_options();\n         op.bitasset_opts->minimum_feeds = 3;\n         op.common_options.flags = bitflag;\n\n         op.common_options.issuer_permissions = ( bitmask | disable_mcr_update );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.common_options.issuer_permissions = ( bitmask | disable_icr_update );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.common_options.issuer_permissions = ( bitmask | disable_mssr_update );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.common_options.issuer_permissions = bitmask;\n\n         // Unable to set new extensions in bitasset options\n         op.bitasset_opts->extensions.value.maintenance_collateral_ratio = 1500;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.bitasset_opts->extensions.value.maintenance_collateral_ratio = {};\n\n         op.bitasset_opts->extensions.value.maximum_short_squeeze_ratio = 1500;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.bitasset_opts->extensions.value.maximum_short_squeeze_ratio = {};\n\n         acop = op;\n      }\n\n      // Able to create asset without new data\n      processed_transaction ptx = PUSH_TX(db, trx, ~0);\n      const asset_object& samcoin = db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n      asset_id_type samcoin_id = samcoin.id;\n\n      BOOST_CHECK_EQUAL( samcoin.options.market_fee_percent, 100 );\n      BOOST_CHECK_EQUAL( samcoin.bitasset_data(db).options.minimum_feeds, 3 );\n\n      // Unable to propose the invalid operations\n      for( const operation& op : ops )\n         BOOST_CHECK_THROW( propose( op ), fc::exception );\n      ops.clear();\n      // Able to propose the good operation\n      propose( acop );\n\n      // Testing asset_update_operation\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = samcoin_id;\n      auop.new_options = samcoin_id(db).options;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      {\n         auto& op = trx.operations.front().get<asset_update_operation>();\n         op.new_options.market_fee_percent = 200;\n         op.new_options.flags &= ~witness_fed_asset;\n\n         // Unable to set new permission bits\n         op.new_options.issuer_permissions = ( bitmask | lock_max_supply );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.new_options.issuer_permissions = ( bitmask | disable_new_supply );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.new_options.issuer_permissions = ( bitmask | disable_mcr_update );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.new_options.issuer_permissions = ( bitmask | disable_icr_update );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.new_options.issuer_permissions = ( bitmask | disable_mssr_update );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n\n         op.new_options.issuer_permissions = bitmask;\n\n         // Unable to set new extensions\n         op.extensions.value.new_precision = 8;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.extensions.value.new_precision = {};\n\n         op.extensions.value.skip_core_exchange_rate = true;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.extensions.value.skip_core_exchange_rate = {};\n\n         auop = op;\n      }\n\n      // Able to update asset without new data\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( samcoin.options.market_fee_percent, 200 );\n\n      // Unable to propose the invalid operations\n      for( const operation& op : ops )\n         BOOST_CHECK_THROW( propose( op ), fc::exception );\n      ops.clear();\n      // Able to propose the good operation\n      propose( auop );\n\n\n      // Testing asset_update_bitasset_operation\n      asset_update_bitasset_operation aubop;\n      aubop.issuer = sam_id;\n      aubop.asset_to_update = samcoin_id;\n      aubop.new_options = samcoin_id(db).bitasset_data(db).options;\n\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n\n      {\n         auto& op = trx.operations.front().get<asset_update_bitasset_operation>();\n         op.new_options.minimum_feeds = 1;\n\n         // Unable to set new extensions\n         op.new_options.extensions.value.maintenance_collateral_ratio = 1500;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.new_options.extensions.value.maintenance_collateral_ratio = {};\n\n         op.new_options.extensions.value.maximum_short_squeeze_ratio = 1500;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.new_options.extensions.value.maximum_short_squeeze_ratio = {};\n\n         aubop = op;\n      }\n\n      // Able to update bitasset without new data\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( samcoin.bitasset_data(db).options.minimum_feeds, 1 );\n\n      // Unable to propose the invalid operations\n      for( const operation& op : ops )\n         BOOST_CHECK_THROW( propose( op ), fc::exception );\n      ops.clear();\n      // Able to propose the good operation\n      propose( aubop );\n\n      // Testing asset_publish_feed_operation\n      update_feed_producers( samcoin, { feeder_id } );\n\n      price_feed f;\n      f.settlement_price = price( asset(1,samcoin_id), asset(1) );\n      f.core_exchange_rate = price( asset(1,samcoin_id), asset(1) );\n      f.maintenance_collateral_ratio = 1850;\n\n      asset_publish_feed_operation apfop;\n      apfop.publisher = feeder_id;\n      apfop.asset_id = samcoin_id;\n      apfop.feed = f;\n\n      trx.operations.clear();\n      trx.operations.push_back( apfop );\n\n      {\n         auto& op = trx.operations.front().get<asset_publish_feed_operation>();\n\n         // Unable to set new extensions\n         op.extensions.value.initial_collateral_ratio = 1500;\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         ops.push_back( op );\n         op.extensions.value.initial_collateral_ratio = {};\n\n         apfop = op;\n      }\n\n      // Able to publish feed without new data\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( samcoin.bitasset_data(db).current_feed.initial_collateral_ratio,\n                         f.maintenance_collateral_ratio );\n\n      // Unable to propose the invalid operations\n      for( const operation& op : ops )\n         BOOST_CHECK_THROW( propose( op ), fc::exception );\n      ops.clear();\n      // Able to propose the good operation\n      propose( apfop );\n\n      // Check what we have now\n      idump( (samcoin) );\n      idump( (samcoin.bitasset_data(db)) );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( prediction_market_global_settle_permission )\n{\n   try {\n\n      // Proceeds to a recent hard fork\n      generate_blocks( HARDFORK_CORE_1270_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      // create a prediction market\n      const asset_object& pm = create_prediction_market( \"PDM\", sam_id );\n      asset_id_type pm_id = pm.id;\n\n      BOOST_CHECK( pm_id(db).can_global_settle() );\n\n      // disable global_settle permission\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = pm_id;\n      auop.new_options = pm_id(db).options;\n      auop.new_options.issuer_permissions &= ~global_settle;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !pm_id(db).can_global_settle() );\n\n      // create some supply\n      borrow( sam, asset(100, pm_id), asset(100) );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // try to enable global_settle again, should fail\n      auop.new_options.issuer_permissions |= global_settle;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      BOOST_CHECK( !pm_id(db).can_global_settle() );\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 100 );\n      BOOST_CHECK( !pm_id(db).can_global_settle() );\n\n      // try to update the asset without enabling global_settle permission, should fail\n      auop.new_options.issuer_permissions &= ~global_settle;\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      BOOST_CHECK( !pm_id(db).can_global_settle() );\n\n      // try to enable global_settle again, should succeed\n      auop.new_options.issuer_permissions |= global_settle;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( pm_id(db).can_global_settle() );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( update_max_supply )\n{\n   try {\n\n      // Proceeds to a recent hard fork\n      generate_blocks( HARDFORK_CORE_1270_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      // create a UIA\n      const asset_object& uia = create_user_issued_asset( \"UIATEST\", sam, charge_market_fee );\n      asset_id_type uia_id = uia.id;\n\n      // issue some to Sam\n      issue_uia( sam_id, uia.amount( GRAPHENE_MAX_SHARE_SUPPLY - 100 ) );\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // update max supply to a smaller number\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = uia_id;\n      auop.new_options = uia_id(db).options;\n      auop.new_options.max_supply -= 101;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply < current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 101 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, uia_id(db).options.max_supply.value + 1 );\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n\n      // able to set max supply to be equal to current supply\n      auop.new_options.max_supply += 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply == current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // no longer able to set max supply to a number smaller than current supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.max_supply += 1;\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply == current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // increase max supply again\n      auop.new_options.max_supply += 2;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // decrease max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // update flag to disable updating of max supply\n      auop.new_options.flags |= lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // unable to update max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.max_supply += 1;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // update flag to enable updating of max supply\n      auop.new_options.flags &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // able to update max supply\n      auop.new_options.max_supply += 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // update flag to disable updating of max supply\n      auop.new_options.flags |= lock_max_supply;\n      // update permission to disable updating of lock_max_supply flag\n      auop.new_options.issuer_permissions |= lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // Able to propose the operation\n      propose( auop );\n\n      // unable to reinstall the permission\n      auop.new_options.issuer_permissions &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.issuer_permissions |= lock_max_supply;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // unable to update max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.max_supply += 1;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // unable to enable the lock_max_supply flag\n      auop.new_options.flags &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.flags |= lock_max_supply;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n\n      // able to update other parameters\n      auto old_market_fee_percent = auop.new_options.market_fee_percent;\n      BOOST_CHECK_EQUAL( uia_id(db).options.market_fee_percent, old_market_fee_percent );\n\n      auop.new_options.market_fee_percent = 120u;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( uia_id(db).options.market_fee_percent, 120u );\n\n      // reserve all supply\n      reserve_asset( sam_id, uia_id(db).amount( GRAPHENE_MAX_SHARE_SUPPLY - 100 ) );\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // still unable to update max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.max_supply += 1;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // still unable to enable the lock_max_supply flag\n      auop.new_options.flags &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.flags |= lock_max_supply;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // able to reinstall the permission and do it\n      auop.new_options.issuer_permissions &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // still unable to update max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.max_supply += 1;\n\n      BOOST_CHECK( !uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // now able to enable the lock_max_supply flag\n      auop.new_options.flags &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 98 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // update max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // issue some\n      issue_uia( sam_id, uia_id(db).amount( 100 ) );\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // update permission to disable updating of lock_max_supply flag\n      auop.new_options.issuer_permissions |= lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      // still can update max supply\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // unable to reinstall the permission\n      auop.new_options.issuer_permissions &= ~lock_max_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.issuer_permissions |= lock_max_supply;\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 99 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // update max supply\n      auop.new_options.max_supply -= 1;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_update_max_supply() );\n      // max_supply > current_supply\n      BOOST_CHECK_EQUAL( uia_id(db).options.max_supply.value, GRAPHENE_MAX_SHARE_SUPPLY - 100 );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( disable_new_supply_uia )\n{\n   try {\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      // create a UIA\n      const asset_object& uia = create_user_issued_asset( \"UIATEST\", sam, charge_market_fee );\n      asset_id_type uia_id = uia.id;\n\n      BOOST_CHECK( uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // issue some to Sam\n      issue_uia( sam_id, uia_id(db).amount( 100 ) );\n\n      BOOST_CHECK( uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // prepare to update\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = uia_id;\n      auop.new_options = uia_id(db).options;\n\n      // update flag to disable creation of new supply\n      auop.new_options.flags |= disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // unable to issue more coins\n      BOOST_CHECK_THROW( issue_uia( sam_id, uia_id(db).amount( 100 ) ), fc::exception );\n\n      BOOST_CHECK( !uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // update flag to enable creation of new supply\n      auop.new_options.flags &= ~disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // issue some to Sam\n      issue_uia( sam_id, uia_id(db).amount( 100 ) );\n\n      BOOST_CHECK( uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // update flag to disable creation of new supply\n      auop.new_options.flags |= disable_new_supply;\n      // update permission to disable updating of disable_new_supply flag\n      auop.new_options.issuer_permissions |= disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // Able to propose the operation\n      propose( auop );\n\n      // unable to reinstall the permission\n      auop.new_options.issuer_permissions &= ~disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.issuer_permissions |= disable_new_supply;\n\n      BOOST_CHECK( !uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // unable to issue more coins\n      BOOST_CHECK_THROW( issue_uia( sam_id, uia_id(db).amount( 100 ) ), fc::exception );\n\n      BOOST_CHECK( !uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // unable to enable the disable_new_supply flag\n      auop.new_options.flags &= ~disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.flags |= disable_new_supply;\n\n      BOOST_CHECK( !uia_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( disable_new_supply_pm )\n{\n   try {\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      fund( sam, asset(10000) );\n\n      // create a PM\n      const asset_object& pm = create_prediction_market( \"PDM\", sam_id );\n      asset_id_type pm_id = pm.id;\n\n      BOOST_CHECK( pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // Sam borrow some\n      borrow( sam, asset(100, pm_id), asset(100) );\n\n      BOOST_CHECK( pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // prepare to update\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = pm_id;\n      auop.new_options = pm_id(db).options;\n\n      // update flag to disable creation of new supply\n      auop.new_options.flags |= disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // unable to borrow more\n      BOOST_CHECK_THROW( borrow( sam, asset(100, pm_id), asset(100) ), fc::exception );\n\n      BOOST_CHECK( !pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // update flag to enable creation of new supply\n      auop.new_options.flags &= ~disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // Sam borrow some\n      borrow( sam, asset(100, pm_id), asset(100) );\n\n      BOOST_CHECK( pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // update flag to disable creation of new supply\n      auop.new_options.flags |= disable_new_supply;\n      // update permission to disable updating of disable_new_supply flag\n      auop.new_options.issuer_permissions |= disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // Able to propose the operation\n      propose( auop );\n\n      // unable to reinstall the permission\n      auop.new_options.issuer_permissions &= ~disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.issuer_permissions |= disable_new_supply;\n\n      BOOST_CHECK( !pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // unable to borrow more coins\n      BOOST_CHECK_THROW( borrow( sam, asset(100, pm_id), asset(100) ), fc::exception );\n\n      BOOST_CHECK( !pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      // unable to enable the disable_new_supply flag\n      auop.new_options.flags &= ~disable_new_supply;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.flags |= disable_new_supply;\n\n      BOOST_CHECK( !pm_id(db).can_create_new_supply() );\n      BOOST_CHECK_EQUAL( pm_id(db).dynamic_data(db).current_supply.value, 200 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( skip_core_exchange_rate )\n{\n   try {\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      // create a UIA\n      const asset_object& uia = create_user_issued_asset( \"UIATEST\", sam, charge_market_fee );\n      asset_id_type uia_id = uia.id;\n\n      BOOST_CHECK( uia_id(db).options.core_exchange_rate == price(asset(1, uia_id), asset(1)) );\n\n      // prepare to update\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = uia_id;\n      auop.new_options = uia_id(db).options;\n\n      // update CER\n      auop.new_options.core_exchange_rate = price(asset(2, uia_id), asset(1));\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      // CER changed\n      BOOST_CHECK( uia_id(db).options.core_exchange_rate == price(asset(2, uia_id), asset(1)) );\n\n      // save value for later check\n      auto old_market_fee_percent = auop.new_options.market_fee_percent;\n      BOOST_CHECK_EQUAL( uia_id(db).options.market_fee_percent, old_market_fee_percent );\n\n      // set skip_core_exchange_rate to false, should fail\n      auop.new_options.core_exchange_rate = price(asset(3, uia_id), asset(1));\n      auop.extensions.value.skip_core_exchange_rate = false;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // unable to propose either\n      BOOST_CHECK_THROW( propose( auop ), fc::exception );\n\n      // CER didn't change\n      BOOST_CHECK( uia_id(db).options.core_exchange_rate == price(asset(2, uia_id), asset(1)) );\n\n      // skip updating CER\n      auop.extensions.value.skip_core_exchange_rate = true;\n      auop.new_options.market_fee_percent = 120u;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      // CER didn't change\n      BOOST_CHECK( uia_id(db).options.core_exchange_rate == price(asset(2, uia_id), asset(1)) );\n      // market_fee_percent changed\n      BOOST_CHECK_EQUAL( uia_id(db).options.market_fee_percent, 120u );\n\n      // Able to propose the operation\n      propose( auop );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( invalid_flags_in_asset )\n{\n   try {\n\n      // Proceeds to a recent hard fork\n      generate_blocks( HARDFORK_CORE_1270_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((sam)(feeder));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n      fund( feeder, asset(init_amount) );\n\n      uint16_t bitmask = ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK;\n      uint16_t uiamask = DEFAULT_UIA_ASSET_ISSUER_PERMISSION;\n\n      uint16_t bitflag = ~global_settle & ~committee_fed_asset; // high bits are set\n      uint16_t uiaflag = ~(bitmask ^ uiamask); // high bits are set\n\n      // Able to create UIA with invalid flags\n      asset_create_operation acop;\n      acop.issuer = sam_id;\n      acop.symbol = \"SAMCOIN\";\n      acop.precision = 2;\n      acop.common_options.core_exchange_rate = price(asset(1,asset_id_type(1)),asset(1));\n      acop.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      acop.common_options.market_fee_percent = 100;\n      acop.common_options.flags = uiaflag;\n      acop.common_options.issuer_permissions = uiamask;\n\n      trx.operations.clear();\n      trx.operations.push_back( acop );\n\n      processed_transaction ptx = PUSH_TX(db, trx, ~0);\n      const asset_object& samcoin = db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n      asset_id_type samcoin_id = samcoin.id;\n\n      // There are invalid bits in flags\n      BOOST_CHECK( samcoin_id(db).options.flags & ~UIA_VALID_FLAGS_MASK );\n\n      // Able to create MPA with invalid flags\n      asset_create_operation acop2 = acop;\n      acop2.symbol = \"SAMBIT\";\n      acop2.bitasset_opts = bitasset_options();\n      acop2.common_options.flags = bitflag;\n      acop2.common_options.issuer_permissions = bitmask;\n\n      trx.operations.clear();\n      trx.operations.push_back( acop2 );\n\n      ptx = PUSH_TX(db, trx, ~0);\n      const asset_object& sambit = db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n      asset_id_type sambit_id = sambit.id;\n\n      // There are invalid bits in flags\n      BOOST_CHECK( sambit_id(db).options.flags & ~VALID_FLAGS_MASK );\n\n      // Unable to correct the invalid flags of the UIA\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = samcoin_id;\n      auop.new_options = samcoin_id(db).options;\n      auop.new_options.flags = 0;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // Unable to correct the invalid flags of the MPA\n      asset_update_operation auop2;\n      auop2.issuer = sam_id;\n      auop2.asset_to_update = sambit_id;\n      auop2.new_options = sambit_id(db).options;\n      auop2.new_options.flags = 0;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop2 );\n\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      // take a look at flags of UIA\n      BOOST_CHECK( samcoin_id(db).options.flags != UIA_VALID_FLAGS_MASK );\n\n      // Try to update UIA but leave some invalid flags, should fail\n      auop.new_options = samcoin_id(db).options;\n      for( uint16_t bit = 0x8000; bit > 0; bit >>= 1 )\n      {\n         auop.new_options.flags = UIA_VALID_FLAGS_MASK | bit;\n         if( auop.new_options.flags == UIA_VALID_FLAGS_MASK )\n            continue;\n         trx.operations.clear();\n         trx.operations.push_back( auop );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         // Unable to propose either if the bit is not a valid bit for MPA\n         if( !(bit & VALID_FLAGS_MASK) )\n            BOOST_CHECK_THROW( propose( auop ), fc::exception );\n      }\n\n      // Unset the invalid bits in flags, should succeed\n      auop.new_options.flags = UIA_VALID_FLAGS_MASK;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( samcoin_id(db).options.flags, UIA_VALID_FLAGS_MASK );\n\n      // Able to propose too\n      propose( auop );\n\n      // take a look at flags of MPA\n      uint16_t valid_bitflag = VALID_FLAGS_MASK & ~committee_fed_asset;\n      BOOST_CHECK( sambit_id(db).options.flags != valid_bitflag );\n\n      // Try to update MPA but leave some invalid flags, should fail\n      auop2.new_options = sambit_id(db).options;\n      for( uint16_t bit = 0x8000; bit > 0; bit >>= 1 )\n      {\n         auop2.new_options.flags = valid_bitflag | bit;\n         if( auop2.new_options.flags == valid_bitflag )\n            continue;\n         trx.operations.clear();\n         trx.operations.push_back( auop2 );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         // Unable to propose either\n         BOOST_CHECK_THROW( propose( auop2 ), fc::exception );\n      }\n\n      // Unset the invalid bits in flags, should succeed\n      auop2.new_options.flags = valid_bitflag;\n      trx.operations.clear();\n      trx.operations.push_back( auop2 );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( sambit_id(db).options.flags, valid_bitflag );\n\n      // Able to propose too\n      propose( auop2 );\n\n      // Unable to create a new UIA with an unknown bit in flags\n      acop.symbol = \"NEWSAMCOIN\";\n      // With all possible bits in permissions set to 1\n      acop2.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;\n      for( uint16_t bit = 0x8000; bit > 0; bit >>= 1 )\n      {\n         acop.common_options.flags = UIA_VALID_FLAGS_MASK | bit;\n         if( acop.common_options.flags == UIA_VALID_FLAGS_MASK )\n            continue;\n         trx.operations.clear();\n         trx.operations.push_back( acop );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         // Unable to propose either\n         BOOST_CHECK_THROW( propose( acop ), fc::exception );\n      }\n\n      // Able to create a new UIA with a valid flags field\n      acop.common_options.flags = UIA_VALID_FLAGS_MASK;\n      trx.operations.clear();\n      trx.operations.push_back( acop );\n      ptx = PUSH_TX(db, trx, ~0);\n      const asset_object& newsamcoin = db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n      asset_id_type newsamcoin_id = newsamcoin.id;\n\n      BOOST_CHECK_EQUAL( newsamcoin_id(db).options.flags, UIA_VALID_FLAGS_MASK );\n\n      // Able to propose too\n      propose( acop );\n\n      // Unable to create a new MPA with an unknown bit in flags\n      acop2.symbol = \"NEWSAMBIT\";\n      // With all possible bits in permissions set to 1\n      acop2.common_options.issuer_permissions = ASSET_ISSUER_PERMISSION_MASK;\n      for( uint16_t bit = 0x8000; bit > 0; bit >>= 1 )\n      {\n         acop2.common_options.flags = valid_bitflag | bit;\n         if( acop2.common_options.flags == valid_bitflag )\n            continue;\n         trx.operations.clear();\n         trx.operations.push_back( acop2 );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n         // Unable to propose either\n         BOOST_CHECK_THROW( propose( acop2 ), fc::exception );\n      }\n\n      // Able to create a new MPA with a valid flags field\n      acop2.common_options.flags = valid_bitflag;\n      trx.operations.clear();\n      trx.operations.push_back( acop2 );\n      ptx = PUSH_TX(db, trx, ~0);\n      const asset_object& newsambit = db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n      asset_id_type newsambit_id = newsambit.id;\n\n      BOOST_CHECK_EQUAL( newsambit_id(db).options.flags, valid_bitflag );\n\n      BOOST_CHECK( !newsambit_id(db).can_owner_update_icr() );\n      BOOST_CHECK( !newsambit_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( !newsambit_id(db).can_owner_update_mssr() );\n\n      // Able to propose too\n      propose( acop2 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( update_asset_precision )\n{\n   try {\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      // create a prediction market\n      const asset_object& pm = create_prediction_market( \"PDM\", sam_id );\n      asset_id_type pm_id = pm.id;\n\n      BOOST_CHECK_EQUAL( pm_id(db).precision, 5 );\n\n      // prepare to update\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = pm_id;\n      auop.new_options = pm_id(db).options;\n\n      // Unable to update precision of a PM\n      auop.extensions.value.new_precision = 4;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      BOOST_CHECK_EQUAL( pm_id(db).precision, 5 );\n\n      // Able to propose the operation\n      propose( auop );\n\n      // create a UIA\n      const asset_object& uia = create_user_issued_asset( \"UIATEST\", sam, charge_market_fee );\n      asset_id_type uia_id = uia.id;\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 2 );\n\n      // try to set new precision to be the same as the old precision, will fail\n      auop.asset_to_update = uia_id;\n      auop.new_options = uia_id(db).options;\n      auop.extensions.value.new_precision = 2;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 2 );\n\n      // try to set new precision to a number which is too big, will fail\n      auop.asset_to_update = uia_id;\n      auop.new_options = uia_id(db).options;\n      auop.extensions.value.new_precision = 13;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // Unable to propose either\n      BOOST_CHECK_THROW( propose( auop ), fc::exception );\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 2 );\n\n      // update precision to a valid number, should succeed\n      auop.extensions.value.new_precision = 3;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 3 );\n\n      // create some supply\n      issue_uia( sam_id, asset( 100, uia_id ) );\n\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 100 );\n\n      // try to update precision, will fail\n      auop.extensions.value.new_precision = 4;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 3 );\n\n      // destroy all supply\n      reserve_asset( sam_id, asset( 100, uia_id ) );\n\n      BOOST_CHECK_EQUAL( uia_id(db).dynamic_data(db).current_supply.value, 0 );\n\n      // update precision, should succeed\n      auop.extensions.value.new_precision = 4;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 4 );\n\n      // create a MPA which is backed by the UIA\n      const asset_object& mpa = create_bitasset( \"TESTBIT\", sam_id, 10, charge_market_fee, 3, uia_id );\n      asset_id_type mpa_id = mpa.id;\n\n      BOOST_CHECK( mpa_id(db).bitasset_data(db).options.short_backing_asset == uia_id );\n\n      // try to update precision of the UIA, will fail\n      auop.extensions.value.new_precision = 3;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      BOOST_CHECK_EQUAL( uia_id(db).precision, 4 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( asset_owner_permissions_update_icr_mcr_mssr )\n{\n   try {\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam)(feeder));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n      fund( feeder, asset(init_amount) );\n\n      // create a MPA with a zero market_fee_percent\n      const asset_object& mpa = create_bitasset( \"TESTBIT\", sam_id, 0, charge_market_fee );\n      asset_id_type mpa_id = mpa.id;\n\n      BOOST_CHECK( mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      // add a price feed publisher and publish a feed\n      update_feed_producers( mpa_id, { feeder_id } );\n\n      price_feed f;\n      f.settlement_price = price( asset(1,mpa_id), asset(1) );\n      f.core_exchange_rate = price( asset(1,mpa_id), asset(1) );\n      f.maintenance_collateral_ratio = 1850;\n      f.maximum_short_squeeze_ratio = 1250;\n\n      uint16_t feed_icr = 1900;\n\n      publish_feed( mpa_id, feeder_id, f, feed_icr );\n\n      auto current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, f.maintenance_collateral_ratio );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  f.maximum_short_squeeze_ratio );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     feed_icr );\n\n      // disable owner's permission to update icr\n      asset_update_operation auop;\n      auop.issuer = sam_id;\n      auop.asset_to_update = mpa_id;\n      auop.new_options = mpa_id(db).options;\n      auop.new_options.issuer_permissions |= disable_icr_update;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can not update icr\n      asset_update_bitasset_operation aubop;\n      aubop.issuer = sam_id;\n      aubop.asset_to_update = mpa_id;\n      aubop.new_options = mpa_id(db).bitasset_data(db).options;\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1950;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.initial_collateral_ratio.reset();\n\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      // disable owner's permission to update mcr\n      auop.new_options.issuer_permissions &= ~disable_icr_update;\n      auop.new_options.issuer_permissions |= disable_mcr_update;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can not update mcr\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1650;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.maintenance_collateral_ratio.reset();\n\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      // disable owner's permission to update mssr\n      auop.new_options.issuer_permissions &= ~disable_mcr_update;\n      auop.new_options.issuer_permissions |= disable_mssr_update;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can not update mssr\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1150;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio.reset();\n\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_CHECK( !mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      // enable owner's permission to update mssr\n      auop.new_options.issuer_permissions &= ~disable_mssr_update;\n\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can update the ratios\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1950;\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1650;\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1150;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio, 1950 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio, 1650 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio, 1150 );\n\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 1650 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1150 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     1950 );\n\n      // check the ratios' valid range\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1000;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.initial_collateral_ratio = 32001;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1950;\n\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1000;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 32001;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1650;\n\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1000;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 32001;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1150;\n\n      // Sam borrow some\n      borrow( sam, asset(1000, mpa_id), asset(2000) );\n\n      // disable owner's permission to update icr\n      auop.new_options.issuer_permissions |= disable_icr_update;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can not update icr\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1960;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      aubop.new_options.extensions.value.initial_collateral_ratio.reset();\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // able to update other ratios\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1950;\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1600;\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1100;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio, 1950 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio, 1100 );\n\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1100 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     1950 );\n\n      // unable to enable the permission to update icr\n      auop.new_options.issuer_permissions &= ~disable_icr_update;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.issuer_permissions |= disable_icr_update;\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // disable owner's permission to update mcr\n      auop.new_options.issuer_permissions |= disable_mcr_update;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can not update mcr\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1660;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      aubop.new_options.extensions.value.maintenance_collateral_ratio.reset();\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // able to update other params that still has permission E.G. mssr\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1950;\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1600;\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1010;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio, 1950 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio, 1010 );\n\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1010 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     1950 );\n\n      // unable to enable the permission to update mcr\n      auop.new_options.issuer_permissions &= ~disable_mcr_update;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      auop.new_options.issuer_permissions |= disable_mcr_update;\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( mpa_id(db).can_owner_update_mssr() );\n\n      // disable owner's permission to update mssr\n      auop.new_options.issuer_permissions |= disable_mssr_update;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mssr() );\n\n      // check that owner can not update mssr\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1020;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio.reset();\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      // able to update other params that still has permission E.G. force_settlement_delay_sec\n      aubop.new_options.extensions.value.initial_collateral_ratio = 1950;\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 1600;\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1010;\n      aubop.new_options.force_settlement_delay_sec += 1;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      PUSH_TX(db, trx, ~0);\n\n      BOOST_REQUIRE_EQUAL( mpa_id(db).bitasset_data(db).options.force_settlement_delay_sec,\n                           aubop.new_options.force_settlement_delay_sec );\n\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio.valid() );\n      BOOST_REQUIRE( mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio.valid() );\n\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio, 1950 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( *mpa_id(db).bitasset_data(db).options.extensions.value.maximum_short_squeeze_ratio, 1010 );\n\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1010 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     1950 );\n\n      // unable to enable the permission to update mssr\n      auop.new_options.issuer_permissions &= ~disable_mssr_update;\n      trx.operations.clear();\n      trx.operations.push_back( auop );\n      BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n      BOOST_CHECK( !mpa_id(db).can_owner_update_icr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mcr() );\n      BOOST_CHECK( !mpa_id(db).can_owner_update_mssr() );\n\n      // publish a new feed\n      f.settlement_price = price( asset(2,mpa_id), asset(1) );\n      f.core_exchange_rate = price( asset(3,mpa_id), asset(1) );\n      f.maintenance_collateral_ratio = 1830;\n      f.maximum_short_squeeze_ratio = 1230;\n\n      feed_icr = 1930;\n\n      publish_feed( mpa_id, feeder_id, f, feed_icr );\n\n      // the values set by the asset owner still take effect\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 1600 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1010 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     1950 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( asset_owner_update_mcr_mssr )\n{\n   try {\n\n      // advance to bsip48/75 hard fork\n      generate_blocks( HARDFORK_BSIP_48_75_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam)(feeder)(borrower)(seller));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n      fund( feeder, asset(init_amount) );\n      fund( borrower, asset(init_amount) );\n      fund( seller, asset(init_amount) );\n\n      // create a MPA with a zero market_fee_percent\n      const asset_object& mpa = create_bitasset( \"TESTBIT\", sam_id, 0, charge_market_fee );\n      asset_id_type mpa_id = mpa.id;\n      asset_id_type core_id = asset_id_type();\n\n      // add a price feed publisher and publish a feed\n      update_feed_producers( mpa_id, { feeder_id } );\n\n      price_feed f;\n      f.settlement_price = price( asset(1,mpa_id), asset(1) );\n      f.core_exchange_rate = price( asset(1,mpa_id), asset(1) );\n      f.maintenance_collateral_ratio = 1850;\n      f.maximum_short_squeeze_ratio = 1250;\n\n      uint16_t feed_icr = 1900;\n\n      publish_feed( mpa_id, feeder_id, f, feed_icr );\n\n      auto current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, f.maintenance_collateral_ratio );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  f.maximum_short_squeeze_ratio );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     feed_icr );\n\n      // borrower borrows some and sends to seller\n      const call_order_object* call_ptr = borrow( borrower_id, asset(1000, mpa_id), asset(2000) );\n      BOOST_REQUIRE( call_ptr );\n      call_order_id_type call_id = call_ptr->id;\n      BOOST_CHECK_EQUAL( call_id(db).debt.value, 1000 );\n      BOOST_CHECK_EQUAL( call_id(db).collateral.value, 2000 );\n\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, mpa_id ).amount.value, 1000 );\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, core_id ).amount.value, init_amount - 2000 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, mpa_id ).amount.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, core_id ).amount.value, init_amount );\n\n      transfer( borrower_id, seller_id, asset(1000, mpa_id) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, mpa_id ).amount.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, core_id ).amount.value, init_amount - 2000 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, mpa_id ).amount.value, 1000 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, core_id ).amount.value, init_amount );\n\n      // seller places orders\n      const limit_order_object* order1_ptr = create_sell_order( seller, asset(100, mpa_id), asset(105) );\n      BOOST_REQUIRE( order1_ptr );\n      limit_order_id_type order1_id = order1_ptr->id;\n      BOOST_CHECK_EQUAL( order1_id(db).for_sale.value, 100 );\n      BOOST_CHECK_EQUAL( order1_id(db).amount_to_receive().amount.value, 105 );\n\n      const limit_order_object* order2_ptr = create_sell_order( seller, asset(100, mpa_id), asset(115) );\n      BOOST_REQUIRE( order2_ptr );\n      limit_order_id_type order2_id = order2_ptr->id;\n      BOOST_CHECK_EQUAL( order2_id(db).for_sale.value, 100 );\n      BOOST_CHECK_EQUAL( order2_id(db).amount_to_receive().amount.value, 115 );\n\n      BOOST_CHECK_EQUAL( call_id(db).debt.value, 1000 );\n      BOOST_CHECK_EQUAL( call_id(db).collateral.value, 2000 );\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, mpa_id ).amount.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, core_id ).amount.value, init_amount - 2000 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, mpa_id ).amount.value, 800 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, core_id ).amount.value, init_amount );\n\n      // asset owner updates MCR and MSSR\n      asset_update_bitasset_operation aubop;\n      aubop.issuer = sam_id;\n      aubop.asset_to_update = mpa_id;\n      aubop.new_options = mpa_id(db).bitasset_data(db).options;\n      aubop.new_options.extensions.value.maintenance_collateral_ratio = 3000;\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1100;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      PUSH_TX(db, trx, ~0);\n\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 3000 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1100 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     feed_icr );\n\n      // borrower should get margin called\n      BOOST_REQUIRE( db.find( call_id ));\n      BOOST_CHECK_EQUAL( call_id(db).debt.value, 900 );\n      BOOST_CHECK_EQUAL( call_id(db).collateral.value, 1895 );\n\n      // limit order1 should be filled\n      BOOST_CHECK( !db.find( order1_id ));\n\n      // limit order2 should not change due to MSSR\n      BOOST_REQUIRE( db.find( order2_id ));\n      BOOST_CHECK_EQUAL( order2_id(db).for_sale.value, 100 );\n      BOOST_CHECK_EQUAL( order2_id(db).amount_to_receive().amount.value, 115 );\n\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, mpa_id ).amount.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, core_id ).amount.value, init_amount - 2000 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, mpa_id ).amount.value, 800 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, core_id ).amount.value, init_amount + 105 );\n\n      // asset owner updates MSSR\n      aubop.new_options.extensions.value.maximum_short_squeeze_ratio = 1200;\n      trx.operations.clear();\n      trx.operations.push_back( aubop );\n      PUSH_TX(db, trx, ~0);\n\n      current_feed = mpa_id(db).bitasset_data(db).current_feed;\n      BOOST_CHECK( current_feed.settlement_price   == f.settlement_price );\n      BOOST_CHECK( current_feed.core_exchange_rate == f.core_exchange_rate );\n      BOOST_CHECK_EQUAL( current_feed.maintenance_collateral_ratio, 3000 );\n      BOOST_CHECK_EQUAL( current_feed.maximum_short_squeeze_ratio,  1200 );\n      BOOST_CHECK_EQUAL( current_feed.initial_collateral_ratio,     feed_icr );\n\n      // borrower should get margin called\n      BOOST_REQUIRE( db.find( call_id ));\n      BOOST_CHECK_EQUAL( call_id(db).debt.value, 800 );\n      BOOST_CHECK_EQUAL( call_id(db).collateral.value, 1780 );\n\n      // limit order2 should be filled\n      BOOST_CHECK( !db.find( order2_id ));\n\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, mpa_id ).amount.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( borrower_id, core_id ).amount.value, init_amount - 2000 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, mpa_id ).amount.value, 800 );\n      BOOST_CHECK_EQUAL( db.get_balance( seller_id, core_id ).amount.value, init_amount + 105 + 115 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n\n"
  },
  {
    "path": "tests/tests/bsip85_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"../common/database_fixture.hpp\"\n\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <boost/test/unit_test.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( bsip85_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( hardfork_time_test )\n{ try {\n\n   {\n      // The maker fee discount percent is 0 by default\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 );\n\n      // Try to set new committee parameter before hardfork\n      proposal_create_operation cop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time() );\n      cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n      committee_member_update_global_parameters_operation cmuop;\n      cmuop.new_parameters.extensions.value.maker_fee_discount_percent = 1;\n      cop.proposed_ops.emplace_back( cmuop );\n      trx.operations.push_back( cop );\n\n      // It should fail\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      trx.clear();\n\n      // The percent should still be 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 );\n   }\n\n   // Pass the hardfork\n   generate_blocks( HARDFORK_BSIP_85_TIME );\n   set_expiration( db, trx );\n\n   {\n      // The maker fee discount percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 );\n\n      // Try to set new committee parameter after hardfork\n      proposal_create_operation cop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time() );\n      cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n      committee_member_update_global_parameters_operation cmuop;\n      cmuop.new_parameters.extensions.value.maker_fee_discount_percent = 10001; // 100.01%\n      cop.proposed_ops.emplace_back(cmuop);\n      trx.operations.push_back(cop);\n\n      // Should fail since the value is too big\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      // The maker fee discount percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 );\n\n      trx.operations.clear();\n      cop.proposed_ops.clear();\n      cmuop.new_parameters.extensions.value.maker_fee_discount_percent = 1123; // 11.23%\n      cop.proposed_ops.emplace_back(cmuop);\n      trx.operations.push_back(cop);\n\n      // Should succeed\n      processed_transaction ptx = PUSH_TX(db, trx, ~0);\n      trx.operations.clear();\n      proposal_id_type prop_id = ptx.operation_results[0].get<object_id_type>();\n\n      // The maker fee discount percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 );\n\n      // Approve the proposal\n      proposal_update_operation uop;\n      uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      uop.active_approvals_to_add = { get_account(\"init0\").get_id(), get_account(\"init1\").get_id(),\n                                      get_account(\"init2\").get_id(), get_account(\"init3\").get_id(),\n                                      get_account(\"init4\").get_id(), get_account(\"init5\").get_id(),\n                                      get_account(\"init6\").get_id(), get_account(\"init7\").get_id() };\n      trx.operations.push_back(uop);\n      PUSH_TX(db, trx, ~0);\n\n      // The maker fee discount percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 );\n\n      generate_blocks( prop_id( db ).expiration_time + 5 );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      generate_block();\n\n      // The maker fee discount percent should have changed\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 1123 );\n\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( bsip85_maker_fee_discount_test )\n{\n   try\n   {\n      ACTORS((alice)(bob)(izzy));\n\n      int64_t alice_b0 = 1000000, bob_b0 = 1000000;\n      int64_t pool_0 = 1000000, accum_0 = 0;\n\n      transfer( account_id_type(), alice_id, asset(alice_b0) );\n      transfer( account_id_type(), bob_id, asset(bob_b0) );\n\n      asset_id_type core_id = asset_id_type();\n      int64_t cer_core_amount = 1801;\n      int64_t cer_usd_amount = 31;\n      price tmp_cer( asset( cer_core_amount ), asset( cer_usd_amount, asset_id_type(1) ) );\n      const auto& usd_obj = create_user_issued_asset( \"IZZYUSD\", izzy_id(db), charge_market_fee, tmp_cer );\n      asset_id_type usd_id = usd_obj.id;\n      issue_uia( alice_id, asset( alice_b0, usd_id ) );\n      issue_uia( bob_id, asset( bob_b0, usd_id ) );\n\n      fund_fee_pool( committee_account( db ), usd_obj, pool_0 );\n\n      // If pay fee in CORE\n      int64_t order_create_fee = 547;\n      int64_t order_maker_refund = 61; // 547 * 11.23% = 61.4281\n\n      // If pay fee in USD\n      int64_t usd_create_fee = order_create_fee * cer_usd_amount / cer_core_amount;\n      if( usd_create_fee * cer_core_amount != order_create_fee * cer_usd_amount ) usd_create_fee += 1;\n      int64_t usd_maker_refund = usd_create_fee * 1123 / 10000;\n      // amount paid by fee pool\n      int64_t core_create_fee = usd_create_fee * cer_core_amount / cer_usd_amount;\n      int64_t core_maker_refund = usd_maker_refund == 0 ? 0 : core_create_fee * 1123 / 10000;\n\n      fee_parameters::flat_set_type new_fees;\n      limit_order_create_operation::fee_parameters_type create_fee_params;\n      create_fee_params.fee = order_create_fee;\n      new_fees.insert( create_fee_params );\n\n      // Pass BSIP 85 HF time\n      // Note: no test case for the behavior before the HF since it's covered by other test cases\n      INVOKE( hardfork_time_test );\n      set_expiration( db, trx );\n\n      // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation\n      // so we have to do it every time we stop generating/popping blocks and start doing tx's\n      enable_fees();\n      change_fees( new_fees );\n\n      {\n         // prepare params\n         time_point_sec max_exp = time_point_sec::maximum();\n         price cer = usd_id( db ).options.core_exchange_rate;\n         const auto* usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n\n         // balance data\n         int64_t alice_bc = alice_b0, bob_bc = bob_b0; // core balance\n         int64_t alice_bu = alice_b0, bob_bu = bob_b0; // usd balance\n         int64_t pool_b = pool_0, accum_b = accum_0;\n\n         // Check order fill\n         BOOST_TEST_MESSAGE( \"Creating ao1, then be filled by bo1\" );\n         // pays fee in core\n         const limit_order_object* ao1 = create_sell_order( alice_id, asset(1000), asset(200, usd_id) );\n         const limit_order_id_type ao1id = ao1->id;\n         // pays fee in usd\n         const limit_order_object* bo1 = create_sell_order(   bob_id, asset(200, usd_id), asset(1000), max_exp, cer );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao1id ) == nullptr );\n         BOOST_CHECK( bo1 == nullptr );\n\n         // data after order created\n         alice_bc -= 1000; // amount for sale\n         alice_bc -= order_create_fee; // fee\n         bob_bu -= 200; // amount for sale\n         bob_bu -= usd_create_fee; // fee\n         pool_b -= core_create_fee; // fee pool\n         accum_b += 0;\n\n         // data after order filled\n         alice_bu += 200; // bob pays\n         alice_bc += order_maker_refund; // maker fee refund\n         bob_bc += 1000; // alice pays\n         accum_b += usd_create_fee; // bo1 paid fee, was taker, no refund\n         pool_b += 0; // no change\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Check partial fill\n         BOOST_TEST_MESSAGE( \"Creating ao2, then be partially filled by bo2\" );\n         // pays fee in usd\n         const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_id_type ao2id = ao2->id;\n         // pays fee in core\n         const limit_order_object* bo2 = create_sell_order(   bob_id, asset(100, usd_id), asset(500) );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao2id ) != nullptr );\n         BOOST_CHECK( bo2 == nullptr );\n\n         // data after order created\n         alice_bc -= 1000; // amount to sell\n         alice_bu -= usd_create_fee; // fee\n         pool_b -= core_create_fee; // fee pool\n         accum_b += 0;\n         bob_bc -= order_create_fee; // fee\n         bob_bu -= 100; // amount to sell\n\n         // data after order filled\n         alice_bu += 100; // bob pays\n         alice_bu += usd_maker_refund; // maker fee refund\n         bob_bc += 500;\n         accum_b += usd_create_fee - usd_maker_refund; // ao2 paid fee deduct maker refund\n         pool_b += core_maker_refund; // ao2 maker refund\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n\n"
  },
  {
    "path": "tests/tests/bsip86_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"../common/database_fixture.hpp\"\n\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <boost/test/unit_test.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( bsip86_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( hardfork_time_test )\n{ try {\n\n   {\n      // The network fee percent is 0 by default\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 0 );\n\n      // Try to set new committee parameter before hardfork\n      proposal_create_operation cop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time() );\n      cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n      committee_member_update_global_parameters_operation cmuop;\n      cmuop.new_parameters.extensions.value.market_fee_network_percent = 1;\n      cop.proposed_ops.emplace_back( cmuop );\n      trx.operations.push_back( cop );\n\n      // It should fail\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      trx.clear();\n\n      // The percent should still be 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 0 );\n   }\n\n   // Pass the hardfork\n   generate_blocks( HARDFORK_BSIP_86_TIME );\n   set_expiration( db, trx );\n\n   {\n      // The network fee percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 0 );\n\n      // Try to set new committee parameter after hardfork\n      proposal_create_operation cop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time() );\n      cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n      committee_member_update_global_parameters_operation cmuop;\n      cmuop.new_parameters.extensions.value.market_fee_network_percent = 3001; // 30.01%\n      cop.proposed_ops.emplace_back(cmuop);\n      trx.operations.push_back(cop);\n\n      // Should fail since the value is too big\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      // The network fee percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 0 );\n\n      trx.operations.clear();\n      cop.proposed_ops.clear();\n      cmuop.new_parameters.extensions.value.market_fee_network_percent = 1123; // 11.23%\n      cop.proposed_ops.emplace_back(cmuop);\n      trx.operations.push_back(cop);\n\n      // Should succeed\n      processed_transaction ptx = PUSH_TX(db, trx, ~0);\n      trx.operations.clear();\n      proposal_id_type prop_id = ptx.operation_results[0].get<object_id_type>();\n\n      // The network fee percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 0 );\n\n      // Approve the proposal\n      proposal_update_operation uop;\n      uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      uop.active_approvals_to_add = { get_account(\"init0\").get_id(), get_account(\"init1\").get_id(),\n                                      get_account(\"init2\").get_id(), get_account(\"init3\").get_id(),\n                                      get_account(\"init4\").get_id(), get_account(\"init5\").get_id(),\n                                      get_account(\"init6\").get_id(), get_account(\"init7\").get_id() };\n      trx.operations.push_back(uop);\n      PUSH_TX(db, trx, ~0);\n\n      // The network fee percent is still 0\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 0 );\n\n      generate_blocks( prop_id( db ).expiration_time + 5 );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      generate_block();\n\n      // The network fee percent should have changed\n      BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_market_fee_network_percent(), 1123 );\n\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( fee_sharing_test )\n{ try {\n   ACTORS((alice)(bob));\n\n   uint16_t market_fee_percent = 100; // 1%\n   price cer(asset(1, asset_id_type(1)), asset(1));\n\n   const asset_object& alicecoin = create_user_issued_asset( \"ALICECOIN\", alice_id(db), charge_market_fee,\n                                                             cer, 4, market_fee_percent );\n   const asset_object& aliceusd  = create_user_issued_asset( \"ALICEUSD\",  alice_id(db), 0 );\n\n   asset_id_type alicecoin_id = alicecoin.id;\n   asset_id_type aliceusd_id = aliceusd.id;\n\n   // prepare users' balance\n   issue_uia( alice, aliceusd.amount( 20000000 ) );\n   issue_uia( bob, alicecoin.amount( 10000000 ) );\n   transfer( account_id_type(), alice_id, asset(10000000) );\n   transfer( account_id_type(), bob_id, asset(10000000) );\n\n   // match and fill orders\n   create_sell_order( alice_id, aliceusd_id(db).amount(200000), alicecoin_id(db).amount(100000) );\n   create_sell_order( bob_id, alicecoin_id(db).amount(100000), aliceusd_id(db).amount(200000) );\n\n   // check fee sharing\n   BOOST_CHECK_EQUAL( get_market_fee_reward( account_id_type(), alicecoin_id ), 0 );\n   BOOST_CHECK_EQUAL( get_market_fee_reward( account_id_type(), aliceusd_id ), 0 );\n\n   // check issuer fees\n   BOOST_CHECK_EQUAL( alicecoin_id(db).dynamic_data(db).accumulated_fees.value, 1000 );\n   BOOST_CHECK_EQUAL( aliceusd_id(db).dynamic_data(db).accumulated_fees.value, 0 );\n\n   // pass the hard fork\n   INVOKE( hardfork_time_test );\n   set_expiration( db, trx );\n\n   // match and fill orders again\n   create_sell_order( alice_id, aliceusd_id(db).amount(200000), alicecoin_id(db).amount(100000) );\n   create_sell_order( bob_id, alicecoin_id(db).amount(100000), aliceusd_id(db).amount(200000) );\n\n   // check fee sharing\n   BOOST_CHECK_EQUAL( get_market_fee_reward( account_id_type(), alicecoin_id ), 112 ); // 1000*11.23%\n   BOOST_CHECK_EQUAL( get_market_fee_reward( account_id_type(), aliceusd_id ), 0 );\n\n   // check issuer fees\n   BOOST_CHECK_EQUAL( alicecoin_id(db).dynamic_data(db).accumulated_fees.value, 1888 ); // 1000+1000-112\n   BOOST_CHECK_EQUAL( aliceusd_id(db).dynamic_data(db).accumulated_fees.value, 0 );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/call_order_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <random>\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/protocol/market.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( call_order_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( call_order_object_test )\n{ try {\n   // assume GRAPHENE_COLLATERAL_RATIO_DENOM is 1000 in this test case\n   BOOST_REQUIRE_EQUAL( 1000, GRAPHENE_COLLATERAL_RATIO_DENOM );\n\n   // function to create a new call_order_object\n   auto new_call_obj = []( const share_type c, const share_type d, int16_t mcr, optional<uint16_t> tcr = {} ) {\n      call_order_object o;\n      o.collateral = c;\n      o.debt = d;\n      o.call_price = price::call_price( asset( d, asset_id_type(1)), asset(c) , mcr );\n      o.target_collateral_ratio = tcr;\n      return o;\n   };\n\n   // function to validate result of call_order_object::get_max_debt_to_cover(...)\n   auto validate_result = []( const call_order_object& o, const price& match_price, const price& feed_price,\n                              int16_t mcr, const share_type result, bool print_log = true ) {\n      if( result == 0 )\n         return 1;\n\n      BOOST_REQUIRE_GT( result.value, 0 );\n      BOOST_REQUIRE_LE( result.value, o.debt.value );\n\n      BOOST_REQUIRE( match_price.base.asset_id == o.collateral_type() );\n      BOOST_REQUIRE( match_price.quote.asset_id == o.debt_type() );\n      BOOST_REQUIRE( feed_price.base.asset_id == o.collateral_type() );\n      BOOST_REQUIRE( feed_price.quote.asset_id == o.debt_type() );\n\n      // should be in margin call territory\n      price call_price = price::call_price( o.get_debt(), o.get_collateral(), mcr );\n      BOOST_CHECK( call_price <= feed_price );\n\n      if( !o.target_collateral_ratio.valid() )\n      {\n         BOOST_CHECK_EQUAL( result.value, o.debt.value );\n         return 2;\n      }\n\n      auto tcr = *o.target_collateral_ratio;\n      if( tcr == 0 )\n         tcr = 1;\n\n      asset to_cover( result, o.debt_type() );\n      asset to_pay = o.get_collateral();\n      if( result < o.debt )\n      {\n         to_pay = to_cover.multiply_and_round_up( match_price );\n         BOOST_CHECK_LT( to_pay.amount.value, o.collateral.value ); // should cover more on black swan event\n         BOOST_CHECK_EQUAL( result.value, (to_pay * match_price).amount.value ); // should not change after rounded down debt to cover\n\n         // should have target_cr set\n         // after sold some collateral, the collateral ratio will be higher than expected\n         price new_tcr_call_price = price::call_price( o.get_debt() - to_cover, o.get_collateral() - to_pay, tcr );\n         price new_mcr_call_price = price::call_price( o.get_debt() - to_cover, o.get_collateral() - to_pay, mcr );\n         BOOST_CHECK( new_tcr_call_price > feed_price );\n         BOOST_CHECK( new_mcr_call_price > feed_price );\n      }\n\n      // if sell less than calculated, the collateral ratio will not be higher than expected\n      int j = 3;\n      for( int i = 100000; i >= 10; i /= 10, ++j )\n      {\n         int total_passes = 3;\n         for( int k = 1; k <= total_passes; ++k )\n         {\n            bool last_check = (k == total_passes);\n            asset sell_less = to_pay;\n            asset cover_less;\n            for( int m = 0; m < k; ++m )\n            {\n               if( i == 100000 )\n                  sell_less.amount -= 1;\n               else\n                  sell_less.amount -= ( ( sell_less.amount + i - 1 ) / i );\n               cover_less = sell_less * match_price; // round down debt to cover\n               if( cover_less >= to_cover )\n               {\n                  cover_less.amount = to_cover.amount - 1;\n                  sell_less = cover_less * match_price; // round down collateral\n                  cover_less = sell_less * match_price; // round down debt to cover\n               }\n               sell_less = cover_less.multiply_and_round_up( match_price ); // round up to get collateral to sell\n               if( sell_less.amount <= 0 || cover_less.amount <= 0 ) // unable to sell or cover less, we return\n               {\n                  if( to_pay.amount == o.collateral )\n                     return j;\n                  return (j + 10);\n               }\n            }\n            BOOST_REQUIRE_LT( cover_less.amount.value, o.debt.value );\n            BOOST_REQUIRE_LT( sell_less.amount.value, o.collateral.value );\n            price tmp_tcr_call_price = price::call_price( o.get_debt() - cover_less, o.get_collateral() - sell_less, tcr );\n            price tmp_mcr_call_price = price::call_price( o.get_debt() - cover_less, o.get_collateral() - sell_less, mcr );\n            bool cover_less_is_enough = ( tmp_tcr_call_price > feed_price && tmp_mcr_call_price > feed_price );\n            if( !cover_less_is_enough )\n            {\n               if( !last_check )\n                  continue;\n               if( to_pay.amount == o.collateral )\n                  return j;\n               return (j + 10);\n            }\n            if( print_log )\n            {\n               print_log = false;\n               wlog( \"Impefect result >= 1 / ${i}\", (\"i\",i) );\n               wdump( (o)(match_price)(feed_price)(mcr)(result)(sell_less)(cover_less)(tmp_mcr_call_price)(tmp_tcr_call_price) );\n            }\n            break;\n         }\n      }\n      if( to_pay.amount == o.collateral )\n         return j;\n      return (j + 10);\n   };\n\n   // init\n   int16_t mcr = 1750;\n   price mp, fp;\n   call_order_object obj;\n   int64_t expected;\n   share_type result;\n\n   mp = price( asset(1100), asset(1000, asset_id_type(1)) ); // match_price\n   fp = price( asset(1000), asset(1000, asset_id_type(1)) ); // feed_price\n\n   // fixed tests\n   obj = new_call_obj( 1751, 1000, mcr ); // order is not in margin call territory\n   expected = 0;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 1751, 1000, mcr, 10000 ); // order is not in margin call territory\n   expected = 0;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 160, 100, mcr ); // target_cr is not set\n   expected = 100;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 1009, 1000, mcr, 200 ); // target_cr set, but order is in black swan territory\n   expected = 1000;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 1499,  999, mcr, 1600 ); // target_cr is 160%, less than 175%, so use 175%\n   expected = 385;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 1500, 1000, mcr, 1800 ); // target_cr is 180%\n   expected = 429;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 1501, 1001, mcr, 2000 ); // target_cr is 200%\n   expected = 558;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   obj = new_call_obj( 1502, 1002, mcr, 3000 ); // target_cr is 300%\n   expected = 793;\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   BOOST_CHECK_EQUAL( result.value, expected );\n   validate_result( obj, mp, fp, mcr, result );\n\n   mcr = 1750;\n   mp = price( asset(40009), asset(79070, asset_id_type(1)) ); // match_price\n   fp = price( asset(40009), asset(86977, asset_id_type(1)) ); // feed_price\n\n   obj = new_call_obj( 557197, 701502, mcr, 1700 ); // target_cr is less than mcr\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   validate_result( obj, mp, fp, mcr, result );\n\n   mcr = 1455;\n   mp = price( asset(1150171), asset(985450, asset_id_type(1)) ); // match_price\n   fp = price( asset(418244), asset(394180, asset_id_type(1)) ); // feed_price\n\n   obj = new_call_obj( 423536, 302688, mcr, 200 ); // target_cr is less than mcr\n   result = obj.get_max_debt_to_cover( mp, fp, mcr );\n   validate_result( obj, mp, fp, mcr, result );\n\n   // random tests\n   std::mt19937_64 gen( time(NULL) );\n   std::uniform_int_distribution<int64_t> amt_uid(1, GRAPHENE_MAX_SHARE_SUPPLY);\n   std::uniform_int_distribution<int64_t> amt_uid2(1, 1000*1000*1000);\n   std::uniform_int_distribution<int64_t> amt_uid3(1, 1000*1000);\n   std::uniform_int_distribution<int64_t> amt_uid4(1, 300);\n   std::uniform_int_distribution<int64_t> mp_num_uid(800, 1100);\n   std::uniform_int_distribution<int16_t> mcr_uid(1001, 32767);\n   std::uniform_int_distribution<int16_t> mcr_uid2(1001, 3000);\n   std::uniform_int_distribution<uint16_t> tcr_uid(0, 65535);\n   std::uniform_int_distribution<uint16_t> tcr_uid2(0, 3000);\n\n   vector<int> count(20,0);\n   int total = 500*1000;\n   for( int i = total; i > 0; --i )\n   {\n      if( i % 9 == 0 )\n         mcr = 1002;\n      else if( i % 3 == 0 )\n         mcr = 1750;\n      else if( i % 3 == 1 )\n         mcr = mcr_uid(gen);\n      else // if( i % 3 == 2 )\n         mcr = mcr_uid2(gen);\n\n      // call_object\n      if( i % 17 <= 0 )\n         obj = new_call_obj( amt_uid(gen), amt_uid(gen), mcr, tcr_uid(gen) );\n      else if( i % 17 <= 2 )\n         obj = new_call_obj( amt_uid2(gen), amt_uid2(gen), mcr, tcr_uid(gen) );\n      else if( i % 17 <= 3 )\n         obj = new_call_obj( amt_uid3(gen), amt_uid3(gen), mcr, tcr_uid(gen) );\n      else if( i % 17 <= 4 )\n         obj = new_call_obj( amt_uid4(gen), amt_uid4(gen), mcr, tcr_uid(gen) );\n      else if( i % 17 <= 5 )\n         obj = new_call_obj( amt_uid(gen), amt_uid(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 7 )\n         obj = new_call_obj( amt_uid2(gen), amt_uid2(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 8 )\n         obj = new_call_obj( amt_uid3(gen), amt_uid3(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 9 )\n         obj = new_call_obj( amt_uid4(gen), amt_uid4(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 11 )\n         obj = new_call_obj( amt_uid3(gen), amt_uid2(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 12 )\n         obj = new_call_obj( amt_uid2(gen), amt_uid3(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 13 )\n         obj = new_call_obj( amt_uid4(gen), amt_uid2(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 14 )\n         obj = new_call_obj( amt_uid2(gen), amt_uid4(gen), mcr, tcr_uid2(gen) );\n      else if( i % 17 <= 15 )\n         obj = new_call_obj( amt_uid3(gen), amt_uid4(gen), mcr, tcr_uid2(gen) );\n      else // if( i % 17 <= 16 )\n         obj = new_call_obj( amt_uid4(gen), amt_uid3(gen), mcr, tcr_uid2(gen) );\n\n      // call_price\n      price cp = price::call_price( obj.get_debt(), obj.get_collateral(), mcr );\n\n      // get feed_price, and make sure we have sufficient good samples\n      int retry = 20;\n      do {\n         if( i % 5 == 0 )\n            fp = price( asset(amt_uid(gen)), asset(amt_uid(gen), asset_id_type(1)) );\n         else if( i % 5 == 1 )\n            fp = price( asset(amt_uid2(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n         else if( i % 5 == 2 )\n            fp = price( asset(amt_uid3(gen)), asset(amt_uid3(gen), asset_id_type(1)) );\n         else if( i % 25 <= 18 )\n            fp = price( asset(amt_uid4(gen)), asset(amt_uid4(gen), asset_id_type(1)) );\n         else if( i % 25 == 19 )\n            fp = price( asset(amt_uid2(gen)), asset(amt_uid3(gen), asset_id_type(1)) );\n         else if( i % 25 == 20 )\n            fp = price( asset(amt_uid3(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n         else if( i % 25 == 21 )\n            fp = price( asset(amt_uid3(gen)), asset(amt_uid4(gen), asset_id_type(1)) );\n         else if( i % 25 == 22 )\n            fp = price( asset(amt_uid4(gen)), asset(amt_uid3(gen), asset_id_type(1)) );\n         else if( i % 25 == 23 )\n            fp = price( asset(amt_uid4(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n         else // if( i % 25 == 24 )\n            fp = price( asset(amt_uid2(gen)), asset(amt_uid4(gen), asset_id_type(1)) );\n         --retry;\n      } while( retry > 0 && ( cp > fp || cp < ( fp / ratio_type( mcr, 1000 ) ) ) );\n\n      // match_price\n      if( i % 16 == 0 )\n         mp = fp * ratio_type( 1001, 1000 );\n      else if( i % 4 == 0 )\n         mp = fp * ratio_type( 1100, 1000 );\n      else if( i % 4 == 1 )\n         mp = fp * ratio_type( mp_num_uid(gen) , 1000 );\n      else if( i % 8 == 4 )\n         mp = price( asset(amt_uid2(gen)), asset(amt_uid3(gen), asset_id_type(1)) );\n      else if( i % 8 == 5 )\n         mp = price( asset(amt_uid3(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n      else if( i % 8 == 6 )\n         mp = price( asset(amt_uid2(gen)), asset(amt_uid2(gen), asset_id_type(1)) );\n      else // if( i % 8 == 7 )\n         mp = price( asset(amt_uid(gen)), asset(amt_uid(gen), asset_id_type(1)) );\n\n      try {\n         result = obj.get_max_debt_to_cover( mp, fp, mcr );\n         auto vr = validate_result( obj, mp, fp, mcr, result, false );\n         ++count[vr];\n      }\n      catch( fc::assert_exception& e )\n      {\n         BOOST_CHECK( e.to_detail_string().find( \"result <= GRAPHENE_MAX_SHARE_SUPPLY\" ) != string::npos );\n         ++count[0];\n      }\n   }\n   ilog( \"count: [bad_input,sell zero,not set,\"\n         \" sell full (perfect), sell full (<0.01%), sell full (<0.1%),sell full (<1%), sell full (other), ...,\"\n         \" sell some (perfect), sell some (<0.01%), sell some (<0.1%),sell some (<1%), sell some (other), ... ]\" );\n   idump( (total)(count) );\n\n} FC_CAPTURE_LOG_AND_RETHROW( (0) ) }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/confidential_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <graphene/db/simple_index.hpp>\n\n#include <fc/crypto/digest.hpp>\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\n\nBOOST_FIXTURE_TEST_SUITE( confidential_tests, database_fixture )\nBOOST_AUTO_TEST_CASE( confidential_test )\n{ try {\n   ACTORS( (dan)(nathan) )\n   const asset_object& core = asset_id_type()(db);\n\n   transfer(account_id_type()(db), dan, core.amount(1000000));\n\n   transfer_to_blind_operation to_blind;\n   to_blind.amount = core.amount(1000);\n   to_blind.from   = dan.id;\n\n   auto owner1_key = fc::ecc::private_key::generate();\n   auto owner1_pub = owner1_key.get_public_key();\n   auto owner2_key = fc::ecc::private_key::generate();\n   auto owner2_pub = owner2_key.get_public_key();\n  \n   blind_output out1, out2;\n   out1.owner = authority( 1, public_key_type(owner1_pub), 1 );\n   out2.owner = authority( 1, public_key_type(owner2_pub), 1 );\n\n\n   auto InB1  = fc::sha256::hash(\"InB1\");\n   auto InB2  = fc::sha256::hash(\"InB2\");\n   auto nonce1 = fc::sha256::hash(\"nonce\");\n   auto nonce2 = fc::sha256::hash(\"nonce2\");\n\n   out1.commitment  = fc::ecc::blind(InB1,250);\n   out1.range_proof = fc::ecc::range_proof_sign( 0, out1.commitment, InB1, nonce1, 0, 0, 250 );\n\n   out2.commitment = fc::ecc::blind(InB2,750);\n   out2.range_proof = fc::ecc::range_proof_sign( 0, out2.commitment, InB1, nonce2, 0, 0, 750 );\n\n   to_blind.blinding_factor = fc::ecc::blind_sum( {InB1,InB2}, 2 );\n   to_blind.outputs = {out2,out1};\n\n   trx.operations = {to_blind};\n   sign( trx,  dan_private_key  );\n   PUSH_TX(db, trx);\n   trx.clear_signatures();\n\n   BOOST_TEST_MESSAGE( \"Transfering from blind to blind with change address\" );\n   auto Out3B  = fc::sha256::hash(\"Out3B\");\n   auto Out4B  = fc::ecc::blind_sum( {InB2,Out3B}, 1 ); // add InB2 - Out3b\n   blind_output out3, out4;\n   out3.commitment = fc::ecc::blind(Out3B,300);\n   out3.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 300 );\n   out4.commitment = fc::ecc::blind(Out4B,750-300-10);\n   out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-10 );\n\n\n   blind_transfer_operation blind_tr;\n   blind_tr.fee = core.amount(10);\n   blind_tr.inputs.push_back( {out2.commitment, out2.owner} );\n   blind_tr.outputs = {out3,out4};\n   blind_tr.validate();\n   trx.operations = {blind_tr};\n   sign( trx,  owner2_key  );\n   PUSH_TX(db, trx);\n\n   BOOST_TEST_MESSAGE( \"Attempting to double spend the same commitments\" );\n   blind_tr.fee = core.amount(11);\n\n   Out4B  = fc::ecc::blind_sum( {InB2,Out3B}, 1 ); // add InB2 - Out3b\n   out4.commitment = fc::ecc::blind(Out4B,750-300-11);\n   auto out4_amount = 750-300-10;\n   out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-11 );\n   blind_tr.outputs = {out4,out3};\n   trx.operations = {blind_tr};\n   BOOST_REQUIRE_THROW( PUSH_TX(db, trx, ~0), graphene::chain::blind_transfer_unknown_commitment );\n\n\n   BOOST_TEST_MESSAGE( \"Transfering from blind to nathan public\" );\n   out4.commitment = fc::ecc::blind(Out4B,750-300-10);\n\n   transfer_from_blind_operation from_blind;\n   from_blind.fee = core.amount(10);\n   from_blind.to  = nathan.id;\n   from_blind.amount = core.amount( out4_amount - 10 );\n   from_blind.blinding_factor = Out4B;\n   from_blind.inputs.push_back( {out4.commitment, out4.owner} );\n   trx.operations = {from_blind};\n   trx.clear_signatures();\n   PUSH_TX(db, trx);\n\n   BOOST_REQUIRE_EQUAL( get_balance( nathan, core ), 750-300-10-10 );\n\n} FC_LOG_AND_RETHROW() }\n\n\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/custom_authority_tests.cpp",
    "content": "/*\n * Copyright (c) 2019 Contributors\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n/**\n * Readers of these custom active authority (CAA) tests may benefit by reviewing\n *\n * - rejection_indicator variant in restriction_predicate.hpp\n * - function_type enum in restriction.hpp\n * - GRAPHENE_OP_RESTRICTION_ARGUMENTS_VARIADIC in restriction.hpp\n */\n\n#include <string>\n#include <boost/test/unit_test.hpp>\n#include <fc/exception/exception.hpp>\n#include <graphene/protocol/restriction_predicate.hpp>\n#include <graphene/protocol/market.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/custom_authority_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n// Dependencies required by the HTLC-related tests\n#include <random>\n#include <graphene/chain/htlc_object.hpp>\n\n// Dependencies for the voting and witness tests\n#include <graphene/chain/witness_object.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nnamespace graphene { namespace protocol {\nbool operator==(const restriction& a, const restriction& b) {\n   if (std::tie(a.member_index, a.restriction_type) != std::tie(b.member_index, b.restriction_type))\n      return false;\n   if (a.argument.is_type<void_t>())\n      return b.argument.is_type<void_t>();\n   using Value_Argument = static_variant<fc::typelist::slice<restriction::argument_type::list, 1>>;\n   return Value_Argument::import_from(a.argument) == Value_Argument::import_from(b.argument);\n}\n} }\n\n\nBOOST_FIXTURE_TEST_SUITE(custom_authority_tests, database_fixture)\n\n#define FUNC(TYPE) BOOST_PP_CAT(restriction::func_, TYPE)\n\ntemplate<typename Object>\nunsigned_int member_index(string name) {\n   unsigned_int index;\n   fc::typelist::runtime::for_each(typename fc::reflector<Object>::native_members(), [&name, &index](auto t) mutable {\n      if (name == decltype(t)::type::get_name())\n         index = decltype(t)::type::index;\n   });\n   return index;\n}\n\ntemplate<typename Expression>\nvoid expect_exception_string(const string& s, Expression e) {\n   try{\n      e();\n      FC_THROW_EXCEPTION(fc::assert_exception, \"Expected exception with string ${s}, but no exception thrown\",\n                         (\"s\", s));\n   } catch (const fc::exception& e) {\n      FC_ASSERT(e.to_detail_string().find(s) != string::npos, \"Did not find expected string ${s} in exception: ${e}\",\n                (\"s\", s)(\"e\", e));\n   }\n}\n#define EXPECT_EXCEPTION_STRING(S, E) \\\n    BOOST_TEST_CHECKPOINT(\"Expect exception containing string: \" S); \\\n    expect_exception_string(S, E)\n\nBOOST_AUTO_TEST_CASE(restriction_predicate_tests) { try {\n   using namespace graphene::protocol;\n   //////\n   // Create a restriction that authorizes transfers only made to Account ID 12\n   //////\n   vector<restriction> restrictions;\n   auto to_index = member_index<transfer_operation>(\"to\");\n   restrictions.emplace_back(to_index, FUNC(eq), account_id_type(12));\n\n   //////\n   // Create an operation that transfers to Account ID 0\n   // This should violate the restriction\n   //////\n   transfer_operation transfer;\n   // Check that the proposed operation to account ID 0 is not compliant with the restriction to account ID 12\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer) == false);\n   // Inspect the reasons why the proposed operation was rejected\n   // The rejection path will reference portions of the restrictions\n   //[\n   //  {\n   //    \"member_index\": 2,\n   //    \"restriction_type\": 0,\n   //    \"argument\": [\n   //      7,\n   //      \"1.2.12\"\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n   BOOST_CHECK_EQUAL(restriction::restriction_count(restrictions), 1);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path.size() == 2);\n   // Index 0 (the outer-most) rejection path refers to the first and only outer-most sub-restriction\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[0].get<size_t>() == 0);\n   // Index 1 (the inner-most) rejection path refers to the first and only argument for an account ID of 1.2.12\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[1].get<predicate_result::rejection_reason>() == predicate_result::predicate_was_false);\n\n   //////\n   // Create an operation that transfer to Account ID 12\n   // This should satisfy the restriction\n   //////\n   transfer.to = account_id_type(12);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer) == true);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path.size() == 0);\n\n\n   //////\n   // Create an INVALID restriction that references an invalid member index\n   // (Index 6 is greater than the highest 0-based index of 5)\n   // of the transfer operation\n   //////\n   restrictions.front() = restriction(fc::typelist::length<fc::reflector<transfer_operation>::native_members>(),\n                                      FUNC(eq), account_id_type(12));\n   //[\n   //  {\n   //    \"member_index\": 6,\n   //    \"restriction_type\": 0,\n   //    \"argument\": [\n   //      7,\n   //      \"1.2.12\"\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n   //\n   // This restriction should throw an exception related to an invalid member index\n   //   10 assert_exception: Assert Exception\n   //   r.member_index < typelist::length<member_list>(): Invalid member index 6 for object graphene::protocol::transfer_operation\n   //           {\"I\":6,\"O\":\"graphene::protocol::transfer_operation\"}\n   //   th_a  restriction_predicate.hxx:493 create_field_predicate\n   BOOST_CHECK_THROW(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value),\n                     fc::assert_exception);\n\n\n   //////\n   // Create an INVALID restriction that compares a transfer operation's account ID type to an asset ID type\n   //////\n   restrictions.front() = restriction(to_index, FUNC(eq), asset_id_type(12));\n   //[\n   //  {\n   //    \"member_index\": 2,\n   //    \"restriction_type\": 0,\n   //    \"argument\": [\n   //      8,\n   //      \"1.3.12\"\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n   //\n   // This restriction should throw an exception related to invalid type\n   //   10 assert_exception: Assert Exception\n   //   Invalid types for predicate\n   //   {}\n   //   th_a  restriction_predicate.hxx:147 predicate_invalid\n   //\n   //   {\"fc::get_typename<Field>::name()\":\"graphene::protocol::account_id_type\",\"func\":\"func_eq\",\"arg\":[8,\"1.3.12\"]}\n   //   th_a  restriction_predicate.hxx:476 create_predicate_function\n   BOOST_CHECK_THROW(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value),\n                     fc::assert_exception);\n\n   //////\n   // Create a restriction such that the operation fee must be paid with Asset ID 0\n   //////\n   auto fee_index = member_index<transfer_operation>(\"fee\");\n   auto asset_id_index = member_index<asset>(\"asset_id\");\n   restrictions.front() = restriction(fee_index, FUNC(attr),\n                                      vector<restriction>{restriction(asset_id_index, FUNC(eq), asset_id_type(0))});\n\n   //////\n   // Check the transfer operation that pays the fee with Asset ID 0\n   // This should satisfy the restriction.\n   //////\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer) == true);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path.size() == 0);\n\n   //////\n   // Change the restriction such that the operation fee must be paid with Asset ID 1\n   //////\n   restrictions.front().argument.get<vector<restriction>>().front().argument = asset_id_type(1);\n   //[\n   //  {\n   //    \"member_index\": 0,\n   //    \"restriction_type\": 10,\n   //    \"argument\": [\n   //      39,\n   //      [\n   //        {\n   //          \"member_index\": 1,\n   //          \"restriction_type\": 0,\n   //          \"argument\": [\n   //            8,\n   //            \"1.3.1\"\n   //          ],\n   //          \"extensions\": []\n   //        }\n   //      ]\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n\n   BOOST_CHECK_EQUAL(restriction::restriction_count(restrictions), 2);\n   //////\n   // Check the transfer operation that pays the fee with Asset ID 0 against the restriction.\n   // This should violate the restriction.\n   //////\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer) == false);\n   // Inspect the reasons why the proposed operation was rejected\n   // The rejection path will reference portions of the restrictions\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path.size() == 3);\n   // Index 0 (the outer-most) rejection path refers to the first and only outer-most sub-restriction\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[0].get<size_t>() == 0);\n   // Index 1 rejection path refers to the first and only attribute of the restriction\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[1].get<size_t>() == 0);\n   // Index 2 (the inner-most) rejection path refers to the expected rejection reason\n   // The rejection reason should be that the predicate was false\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[2].get<predicate_result::rejection_reason>() == predicate_result::predicate_was_false);\n\n   //////\n   // Create a restriction that authorizes transfers only to Account ID 12\n   //////\n   restrictions.emplace_back(to_index, FUNC(eq), account_id_type(12));\n   //[\n   //  {\n   //    \"member_index\": 0,\n   //    \"restriction_type\": 10,\n   //    \"argument\": [\n   //      39,\n   //      [\n   //        {\n   //          \"member_index\": 1,\n   //          \"restriction_type\": 0,\n   //          \"argument\": [\n   //            8,\n   //            \"1.3.1\"\n   //          ],\n   //          \"extensions\": []\n   //        }\n   //      ]\n   //    ],\n   //    \"extensions\": []\n   //  },\n   //  {\n   //    \"member_index\": 2,\n   //    \"restriction_type\": 0,\n   //    \"argument\": [\n   //      7,\n   //      \"1.2.12\"\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n   BOOST_CHECK_EQUAL(restriction::restriction_count(restrictions), 3);\n\n   //////\n   // Create a transfer operation that authorizes transfer to Account ID 12\n   // This operation should satisfy the restriction\n   //////\n   transfer.to = account_id_type(12);\n   transfer.fee.asset_id = asset_id_type(1);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer) == true);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path.size() == 0);\n\n   //////\n   // Create a transfer operation that transfers to Account ID 10\n   // This operation should violate the restriction\n   //////\n   transfer.to = account_id_type(10);\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer) == false);\n   // Inspect the reasons why the proposed operation was rejected\n   // The rejection path will reference portions of the restrictions\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path.size() == 2);\n   // Index 0 (the outer-most) rejection path refers to the first and only outer-most sub-restriction\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[0].get<size_t>() == 1);\n   // Index 1 (the inner-most) rejection path refers to the first and only argument\n   BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)\n               .rejection_path[1].get<predicate_result::rejection_reason>() == predicate_result::predicate_was_false);\n\n   //////\n   // Create a restriction where the ext.owner_special_authority field is unspecified\n   //////\n   restrictions.clear();\n   auto extensions_index = member_index<account_update_operation>(\"extensions\");\n   auto authority_index = member_index<account_update_operation::ext>(\"owner_special_authority\");\n   restrictions.emplace_back(extensions_index, FUNC(attr),\n                             vector<restriction>{restriction(authority_index, FUNC(eq), void_t())});\n   //[\n   //  {\n   //    \"member_index\": 5,\n   //    \"restriction_type\": 10,\n   //    \"argument\": [\n   //      39,\n   //      [\n   //        {\n   //          \"member_index\": 1,\n   //          \"restriction_type\": 0,\n   //          \"argument\": [\n   //            0,\n   //            {}\n   //          ],\n   //          \"extensions\": []\n   //        }\n   //      ]\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n   BOOST_CHECK_EQUAL(restriction::restriction_count(restrictions), 2);\n   auto predicate = get_restriction_predicate(restrictions, operation::tag<account_update_operation>::value);\n\n   //////\n   // Create an account update operation without any owner_special_authority extension\n   //////\n   account_update_operation update;\n   // The transfer operation should violate the restriction\n   BOOST_CHECK_THROW(predicate(transfer), fc::assert_exception);\n   // The update operation should satisfy the restriction\n   BOOST_CHECK(predicate(update) == true);\n   BOOST_CHECK(predicate(update).rejection_path.size() == 0);\n\n   //////\n   // Change the update operation to include an owner_special_authority\n   // This should violate the restriction\n   //////\n   update.extensions.value.owner_special_authority = special_authority();\n   BOOST_CHECK(predicate(update) == false);\n   BOOST_CHECK_EQUAL(predicate(update).rejection_path.size(), 3);\n   // Index 0 (the outer-most) rejection path refers to the first and only restriction\n   BOOST_CHECK(predicate(update).rejection_path[0].get<size_t>() == 0);\n   // Index 1 rejection path refers to the first and only attribute of the restriction\n   BOOST_CHECK(predicate(update).rejection_path[1].get<size_t>() == 0);\n   // Index 2 (the inner-most) rejection path refers to the expected rejection reason\n   // The rejection reason should be that the predicate was false\n   BOOST_CHECK(predicate(update).rejection_path[2].get<predicate_result::rejection_reason>() ==\n               predicate_result::predicate_was_false);\n\n   //////\n   // Change the restriction where the ext.owner_special_authority field must be specified\n   //////\n   restrictions.front().argument.get<vector<restriction>>().front().restriction_type = FUNC(ne);\n   //[\n   //  {\n   //    \"member_index\": 5,\n   //    \"restriction_type\": 10,\n   //    \"argument\": [\n   //      39,\n   //      [\n   //        {\n   //          \"member_index\": 1,\n   //          \"restriction_type\": 1,\n   //          \"argument\": [\n   //            0,\n   //            {}\n   //          ],\n   //          \"extensions\": []\n   //        }\n   //      ]\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n\n   //////\n   // The update operation should satisfy the new restriction because the ext.owner_special_authority is specified\n   //////\n   predicate = get_restriction_predicate(restrictions, operation::tag<account_update_operation>::value);\n   BOOST_CHECK(predicate(update) == true);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(container_in_not_in_checks) { try {\n   vector<restriction> restrictions;\n   restrictions.emplace_back(member_index<asset_update_feed_producers_operation>(\"new_feed_producers\"), FUNC(in),\n                             flat_set<account_id_type>{account_id_type(5), account_id_type(6), account_id_type(7)});\n   auto pred = get_restriction_predicate(restrictions, operation::tag<asset_update_feed_producers_operation>::value);\n\n   asset_update_feed_producers_operation op;\n   BOOST_CHECK(pred(op));\n   op.new_feed_producers = {account_id_type(1)};\n   BOOST_CHECK(!pred(op));\n   op.new_feed_producers = {account_id_type(5)};\n   BOOST_CHECK(pred(op));\n   op.new_feed_producers = {account_id_type(5), account_id_type(6)};\n   BOOST_CHECK(pred(op));\n   op.new_feed_producers = {account_id_type(5), account_id_type(6), account_id_type(7)};\n   BOOST_CHECK(pred(op));\n   op.new_feed_producers = {account_id_type(1), account_id_type(5), account_id_type(6), account_id_type(7)};\n   BOOST_CHECK(!pred(op));\n   op.new_feed_producers = {account_id_type(5), account_id_type(6), account_id_type(7), account_id_type(8)};\n   BOOST_CHECK(!pred(op));\n\n   restrictions.front().restriction_type = FUNC(not_in);\n   pred = get_restriction_predicate(restrictions, operation::tag<asset_update_feed_producers_operation>::value);\n   op.new_feed_producers.clear();\n   BOOST_CHECK(pred(op));\n   op.new_feed_producers = {account_id_type(1)};\n   BOOST_CHECK(pred(op));\n   op.new_feed_producers = {account_id_type(5)};\n   BOOST_CHECK(!pred(op));\n   op.new_feed_producers = {account_id_type(5), account_id_type(6)};\n   BOOST_CHECK(!pred(op));\n   op.new_feed_producers = {account_id_type(5), account_id_type(6), account_id_type(7)};\n   BOOST_CHECK(!pred(op));\n   op.new_feed_producers = {account_id_type(1), account_id_type(5), account_id_type(6), account_id_type(7)};\n   BOOST_CHECK(!pred(op));\n   op.new_feed_producers = {account_id_type(5), account_id_type(6), account_id_type(7), account_id_type(8)};\n   BOOST_CHECK(!pred(op));\n} FC_LOG_AND_RETHROW() }\n\n   /**\n    * Test predicates containing logical ORs\n    * Test of authorization and revocation of one account (Alice) authorizing multiple other accounts (Bob and Charlie)\n    * to transfer out of her account by using a single custom active authority with two logical OR branches.\n    *\n    * This can alternatively be achieved by using two custom active authority authorizations\n    * as is done in multiple_transfer_custom_auths\n    */\n   BOOST_AUTO_TEST_CASE(logical_or_transfer_predicate_tests) {\n      try {\n         using namespace graphene::protocol;\n         //////\n         // Create a restriction that authorizes transfers only made to Account ID 12 or Account 15\n         //////\n         auto to_index = member_index<transfer_operation>(\"to\");\n         vector<restriction> branch1 = vector<restriction>{restriction(to_index, FUNC(eq), account_id_type(12))};\n         vector<restriction> branch2 = vector<restriction>{restriction(to_index, FUNC(eq), account_id_type(15))};\n         unsigned_int dummy_index = 999;\n         vector<restriction> or_restrictions = {\n                 restriction(dummy_index, FUNC(logical_or), vector<vector<restriction>>{branch1, branch2})};\n         //[\n         //  {\n         //    \"member_index\": 999,\n         //    \"restriction_type\": 11,\n         //    \"argument\": [\n         //      40,\n         //      [\n         //        [\n         //          {\n         //            \"member_index\": 2,\n         //            \"restriction_type\": 0,\n         //            \"argument\": [\n         //              7,\n         //              \"1.2.12\"\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ],\n         //        [\n         //          {\n         //            \"member_index\": 2,\n         //            \"restriction_type\": 0,\n         //            \"argument\": [\n         //              7,\n         //              \"1.2.15\"\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ]\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n         BOOST_CHECK_EQUAL(restriction::restriction_count(or_restrictions), 3);\n         auto predicate = get_restriction_predicate(or_restrictions, operation::tag<transfer_operation>::value);\n\n         //////\n         // Create an operation that transfers to Account ID 12\n         // This should satisfy the restriction because Account ID 12 is authorized to transfer\n         //////\n         transfer_operation transfer_to_12 = transfer_operation();\n         transfer_to_12.to = account_id_type(12);\n         BOOST_CHECK_EQUAL(predicate(transfer_to_12).success, true);\n         BOOST_CHECK_EQUAL(predicate(transfer_to_12).rejection_path.size(), 0);\n\n         //////\n         // Create an operation that transfers to Account ID 15\n         // This should satisfy the restriction because Account ID 15 is authorized to transfer\n         //////\n         transfer_operation transfer_to_15 = transfer_operation();\n         transfer_to_15.to = account_id_type(15);\n         BOOST_CHECK(predicate(transfer_to_15) == true);\n         BOOST_CHECK_EQUAL(predicate(transfer_to_15).rejection_path.size(), 0);\n\n         //////\n         // Create an operation that transfers to Account ID 1\n         // This should violate the restriction because Account 1 is not authorized to transfer\n         //////\n         transfer_operation transfer_to_1;\n         transfer_to_1.to = account_id_type(1);\n         BOOST_CHECK(predicate(transfer_to_1) == false);\n\n         // JSON-formatted Rejection path\n         //[ // A vector of predicate results\n         //  [\n         //    0, // Index 0 (the outer-most) rejection path\n         //    0  // The first and only outer-most sub-restriction\n         //  ],\n         //  [\n         //    1,  // Index 1 (the inner-most) rejection path\n         //    [  // A vector of predicate results\n         //      {\n         //        \"success\": false,\n         //        \"rejection_path\": [\n         //          [\n         //            0, // Index 0 (the outer-most) rejection path\n         //            0  // Restriction 1 along this branch\n         //          ],\n         //          [\n         //            2, // Rejection reason\n         //            \"predicate_was_false\"\n         //          ]\n         //        ]\n         //      },\n         //      {\n         //        \"success\": false,\n         //        \"rejection_path\": [\n         //          [\n         //            0, // Index 0 (the outer-most) rejection path\n         //            0  // Restriction 1 along this branch\n         //          ],\n         //          [\n         //            2, // Rejection reason\n         //            \"predicate_was_false\"\n         //          ]\n         //        ]\n         //      }\n         //    ]\n         //  ]\n         //]\n\n         // C++ style check of the rejection path\n         BOOST_CHECK_EQUAL(predicate(transfer_to_1).rejection_path.size(), 2);\n         // Index 0 (the outer-most) rejection path refers to  and only outer-most sub-restriction\n         BOOST_CHECK(predicate(transfer_to_1).rejection_path[0].get<size_t>() == 0);\n         // Index 1 (the inner-most) rejection path refers to the first and only argument:\n         // the vector of branches each of which are one level deep\n         vector<predicate_result> branch_results = predicate(\n                 transfer_to_1).rejection_path[1].get<vector<predicate_result>>();\n         unsigned long nbr_branches = branch_results.size();\n         BOOST_CHECK_EQUAL(nbr_branches, 2);\n         for (unsigned long j = 0; j < nbr_branches; ++j) {\n            predicate_result &result = branch_results.at(j);\n            BOOST_CHECK_EQUAL(result.success, false);\n\n            BOOST_CHECK_EQUAL(result.rejection_path.size(), 2);\n            // Index 0 (the outer-most) rejection path refers to the first and only restriction\n            BOOST_CHECK_EQUAL(result.rejection_path[0].get<size_t>(), 0);\n            // Index 1 (the inner-most) rejection path refers to the first and only argument for an account ID:\n            // either 1.2.12 or 1.2.15\n            BOOST_CHECK(result.rejection_path[1].get<predicate_result::rejection_reason>() ==\n                        predicate_result::predicate_was_false);\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\nBOOST_AUTO_TEST_CASE(custom_auths) { try {\n   //////\n   // Initialize the test\n   //////\n   generate_blocks(HARDFORK_BSIP_40_TIME);\n   generate_blocks(5);\n   db.modify(global_property_id_type()(db), [](global_property_object& gpo) {\n      gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n   });\n   set_expiration(db, trx);\n   ACTORS((alice)(bob))\n   fund(alice, asset(1000*GRAPHENE_BLOCKCHAIN_PRECISION));\n   fund(bob, asset(1000*GRAPHENE_BLOCKCHAIN_PRECISION));\n\n   //////\n   // Create a custom authority where Bob is authorized to transfer from Alice's account\n   // if and only if the transfer amount is less than 100 of Asset ID 0.\n   // This custom authority is NOT YET published.\n   //////\n   custom_authority_create_operation op;\n   op.account = alice.get_id();\n   op.auth.add_authority(bob.get_id(), 1);\n   op.auth.weight_threshold = 1;\n   op.enabled = true;\n   op.valid_to = db.head_block_time() + 1000;\n   op.operation_type = operation::tag<transfer_operation>::value;\n   auto transfer_amount_index = member_index<transfer_operation>(\"amount\");\n   auto asset_amount_index = member_index<asset>(\"amount\");\n   auto asset_id_index = member_index<asset>(\"asset_id\");\n   op.restrictions = {restriction(transfer_amount_index, restriction::func_attr, vector<restriction>{\n                          restriction(asset_amount_index, restriction::func_lt,\n                                      int64_t(100*GRAPHENE_BLOCKCHAIN_PRECISION)),\n                          restriction(asset_id_index, restriction::func_eq, asset_id_type(0))})};\n   //[\n   //  {\n   //    \"member_index\": 3,\n   //    \"restriction_type\": 10,\n   //    \"argument\": [\n   //      39,\n   //      [\n   //        {\n   //          \"member_index\": 0,\n   //          \"restriction_type\": 2,\n   //          \"argument\": [\n   //            2,\n   //            10000000\n   //          ],\n   //          \"extensions\": []\n   //        },\n   //        {\n   //          \"member_index\": 1,\n   //          \"restriction_type\": 0,\n   //          \"argument\": [\n   //            8,\n   //            \"1.3.0\"\n   //          ],\n   //          \"extensions\": []\n   //        }\n   //      ]\n   //    ],\n   //    \"extensions\": []\n   //  }\n   //]\n   BOOST_CHECK_EQUAL(restriction::restriction_count(op.restrictions), 3);\n\n\n   //////\n   // Bob attempts to transfer 99 CORE from Alice's account\n   // This attempt should fail because it is attempted before the custom authority is published\n   //////\n   transfer_operation top;\n   top.to = bob.get_id();\n   top.from = alice.get_id();\n   top.amount.amount = 99 * GRAPHENE_BLOCKCHAIN_PRECISION;\n   trx.operations = {top};\n   sign(trx, bob_private_key);\n   // No custom auth yet; bob's transfer should reject\n   BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n   //////\n   // Alice publishes the custom authority\n   //////\n   trx.clear();\n   trx.operations = {op};\n   sign(trx, alice_private_key);\n   PUSH_TX(db, trx);\n\n   custom_authority_id_type auth_id =\n           db.get_index_type<custom_authority_index>().indices().get<by_account_custom>().find(alice_id)->id;\n\n   //////\n   // Bob attempts to transfer 99 CORE from Alice's account\n   // This attempt should succeed because it is attempted after the custom authority is published\n   //////\n   trx.clear();\n   trx.operations = {top};\n   sign(trx, bob_private_key);\n   PUSH_TX(db, trx);\n\n   //////\n   // Bob attempts to transfer 100 CORE from Alice's account\n   // This attempt should fail because it exceeds the authorized amount\n   //////\n   trx.operations.front().get<transfer_operation>().amount.amount = 100*GRAPHENE_BLOCKCHAIN_PRECISION;\n   trx.clear_signatures();\n   sign(trx, bob_private_key);\n   // If bob tries to transfer 100, it rejects because the restriction is strictly less than 100\n   EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n   //////\n   // Update the custom authority so that Bob is authorized to transfer from Alice's account\n   // if and only if the transfer amount EXACTLY EQUALS 100 of Asset ID 0.\n   // This custom authority is NOT YET published.\n   //////\n   op.restrictions.front().argument.get<vector<restriction>>().front().restriction_type = restriction::func_eq;\n   custom_authority_update_operation uop;\n   uop.account = alice.get_id();\n   uop.authority_to_update = auth_id;\n   uop.restrictions_to_remove = {0};\n   uop.restrictions_to_add = {op.restrictions.front()};\n   trx.clear();\n   trx.operations = {uop};\n   sign(trx, alice_private_key);\n   PUSH_TX(db, trx);\n\n   BOOST_CHECK(auth_id(db).get_restrictions() == uop.restrictions_to_add);\n\n   //////\n   // Bob attempts to transfer 99 CORE from Alice's account\n   // This attempt should fail because only transfers of 100 CORE are authorized\n   //////\n   trx.clear();\n   trx.operations = {top};\n   trx.expiration += 5;\n   sign(trx, bob_private_key);\n   // The transfer of 99 should reject because the requirement is for exactly 100\n   EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n   //////\n   // Bob attempts to transfer 100 CORE from Alice's account\n   // This attempt should succeed because transfers of exactly 100 CORE are authorized by Alice\n   //////\n   trx.operations.front().get<transfer_operation>().amount.amount = 100*GRAPHENE_BLOCKCHAIN_PRECISION;\n   trx.clear_signatures();\n   sign(trx, bob_private_key);\n   PUSH_TX(db, trx);\n   auto transfer = trx;\n\n   generate_block();\n\n   //////\n   // Bob attempts to transfer 100 CORE from Alice's account AGAIN\n   // This attempt should succeed because there are no limits to the quantity of transfers\n   // besides potentially depleting the CORE in Alice's account\n   //////\n   trx.expiration += 5;\n   trx.clear_signatures();\n   sign(trx, bob_private_key);\n   PUSH_TX(db, trx);\n\n   //////\n   // Alice revokes the custom authority for Bob\n   //////\n   custom_authority_delete_operation dop;\n   dop.account = alice.get_id();\n   dop.authority_to_delete = auth_id;\n   trx.clear();\n   trx.operations = {dop};\n   sign(trx, alice_private_key);\n   PUSH_TX(db, trx);\n\n   //////\n   // Bob attempts to transfer 100 CORE from Alice's account\n   // This attempt should fail because it is attempted after the custom authority has been revoked\n   //////\n   transfer.expiration += 10;\n   transfer.clear_signatures();\n   sign(transfer, bob_private_key);\n   BOOST_CHECK_THROW(PUSH_TX(db, transfer), tx_missing_active_auth);\n} FC_LOG_AND_RETHROW() }\n\n\n   /**\n    * Test of authorization and revocation of one account (Alice) authorizing multiple other accounts (Bob and Charlie)\n    * to transfer out of her account by using two distinct custom active authorities.\n    *\n    * This can alternatively be achieved by using a single custom active authority with two logical OR branches\n    * as is done in logical_or_transfer_predicate_tests\n    */\n   BOOST_AUTO_TEST_CASE(multiple_transfer_custom_auths) {\n      try {\n         //////\n         // Initialize the test\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n         ACTORS((alice)(bob)(charlie)(diana))\n         fund(alice, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n         fund(bob, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Charlie\n         // This attempt should fail because Alice has not authorized anyone to transfer from her account\n         //////\n         transfer_operation bob_transfers_from_alice_to_charlie;\n         bob_transfers_from_alice_to_charlie.to = charlie.get_id();\n         bob_transfers_from_alice_to_charlie.from = alice.get_id();\n         bob_transfers_from_alice_to_charlie.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         trx.operations = {bob_transfers_from_alice_to_charlie};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should fail because Alice has not authorized anyone to transfer from her account\n         //////\n         transfer_operation bob_transfers_from_alice_to_diana;\n         bob_transfers_from_alice_to_diana.to = diana.get_id();\n         bob_transfers_from_alice_to_diana.from = alice.get_id();\n         bob_transfers_from_alice_to_diana.amount.amount = 60 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         trx.operations = {bob_transfers_from_alice_to_diana};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n         //////\n         // Charlie attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should fail because Alice has not authorized anyone to transfer from her account\n         //////\n         transfer_operation charlie_transfers_from_alice_to_diana;\n         charlie_transfers_from_alice_to_diana.to = diana.get_id();\n         charlie_transfers_from_alice_to_diana.from = alice.get_id();\n         charlie_transfers_from_alice_to_diana.amount.amount = 25 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         trx.operations = {charlie_transfers_from_alice_to_diana};\n         sign(trx, charlie_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n         //////\n         // Create a custom authority where Bob is authorized to transfer from Alice's account to Charlie\n         //////\n         custom_authority_create_operation op;\n         op.account = alice.get_id();\n         op.auth.add_authority(bob.get_id(), 1);\n         op.auth.weight_threshold = 1;\n         op.enabled = true;\n         op.valid_to = db.head_block_time() + 1000;\n         op.operation_type = operation::tag<transfer_operation>::value;\n         auto to_index = member_index<transfer_operation>(\"to\");\n         vector<restriction> restrictions;\n         restrictions.emplace_back(to_index, FUNC(eq), charlie.get_id());\n         op.restrictions = restrictions;\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.18\"\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         // Alice publishes the custom authority\n         trx.clear();\n         trx.operations = {op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n         custom_authority_id_type ca_bob_transfers_from_alice_to_charlie =\n                 db.get_index_type<custom_authority_index>().indices().get<by_account_custom>().find(alice_id)->id;\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Charlie\n         // This attempt should succeed because it is attempted after the custom authority is published\n         //////\n         trx.clear();\n         trx.operations = {bob_transfers_from_alice_to_charlie};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should fail because Alice has not authorized Bob to transfer to Diana\n         //////\n         trx.clear();\n         trx.operations = {bob_transfers_from_alice_to_diana};\n         sign(trx, charlie_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // {\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n         //////\n         // Charlie attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should fail because Alice has not authorized Charlie to transfer to Diana\n         //////\n         trx.clear();\n         trx.operations = {charlie_transfers_from_alice_to_diana};\n         sign(trx, charlie_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // {\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the re-used transactions\n         //////\n         generate_blocks(1);\n\n         //////\n         // Create a custom authority where Charlie is authorized to transfer from Alice's account to Diana\n         //////\n         op = custom_authority_create_operation();\n         op.account = alice.get_id();\n         op.auth.add_authority(charlie.get_id(), 1);\n         op.auth.weight_threshold = 1;\n         op.enabled = true;\n         op.valid_to = db.head_block_time() + 1000;\n         op.operation_type = operation::tag<transfer_operation>::value;\n         restrictions.clear();\n         restrictions.emplace_back(to_index, FUNC(eq), diana.get_id());\n         op.restrictions = restrictions;\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.19\"\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         // Alice publishes the additional custom authority\n         trx.clear();\n         trx.operations = {op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n         // Note the additional custom authority\n         const auto &ca_index = db.get_index_type<custom_authority_index>().indices().get<by_account_custom>();\n\n         auto ca_alice_range = ca_index.equal_range(alice_id);\n         long nbr_alice_auths = std::distance(ca_alice_range.first, ca_alice_range.second);\n         BOOST_CHECK_EQUAL(2, nbr_alice_auths);\n         auto iter = ca_alice_range.first;\n         custom_authority_id_type *ca_charlie_transfers_from_alice_to_diana = nullptr;\n         while (iter != ca_index.end()) {\n            custom_authority_id_type ca_id = iter->id;\n            const custom_authority_object *ca = db.find<custom_authority_object>(ca_id);\n            flat_map<account_id_type, weight_type> ca_authorities = ca->auth.account_auths;\n            BOOST_CHECK_EQUAL(1, ca_authorities.size());\n            if (ca_authorities.find(charlie.get_id()) != ca_authorities.end()) {\n               ca_charlie_transfers_from_alice_to_diana = &ca_id;\n               break;\n            }\n\n            iter++;\n         }\n         BOOST_CHECK(ca_charlie_transfers_from_alice_to_diana != nullptr);\n\n         //////\n         // Charlie attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should succeed because it is attempted after the custom authority is published\n         //////\n         trx.clear();\n         trx.operations = {charlie_transfers_from_alice_to_diana};\n         sign(trx, charlie_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob should still be able to transfer from Alice to Charlie\n         // Bob attempts to transfer 100 CORE from Alice's account to Charlie\n         // This attempt should succeed because it was previously authorized by Alice\n         //////\n         trx.clear();\n         trx.operations = {bob_transfers_from_alice_to_charlie};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should fail because Alice has not authorized Bob to transfer to Diana\n         //////\n         trx.clear();\n         trx.operations = {bob_transfers_from_alice_to_diana};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path for the first custom authority\n         // \"rejected_custom_auths\":[[\"1.17.0\",[0,{\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}]]]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         // Check for reference to the second CAA 1.17.0\n         // \"rejected_custom_auths\":[[\"1.17.0\",[0,{\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}]]]\n         EXPECT_EXCEPTION_STRING(\"1.17.0\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the re-used transactions\n         //////\n         generate_blocks(1);\n\n         //////\n         // Alice revokes the custom authority for Bob\n         //////\n         custom_authority_delete_operation revoke_bob_authorization;\n         revoke_bob_authorization.account = alice.get_id();\n         revoke_bob_authorization.authority_to_delete = ca_bob_transfers_from_alice_to_charlie;\n         trx.clear();\n         trx.operations = {revoke_bob_authorization};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Charlie\n         // This attempt should fail because Alice has revoked authorization for Bob to transfer from her account\n         //////\n         trx.clear();\n         trx.operations = {bob_transfers_from_alice_to_charlie};\n         sign(trx, bob_private_key);\n         // General check of the exception\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // Check the rejection path\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         // Check for reference to the second CAA 1.17.1\n         // \"rejected_custom_auths\":[[\"1.17.1\",[0,{\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}]]]\n         EXPECT_EXCEPTION_STRING(\"1.17.1\", [&] {PUSH_TX(db, trx);});\n\n         //////\n         // Charlie attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should succeed because Alice should still be authorized to transfer from Alice account\n         //////\n         trx.clear();\n         trx.operations = {charlie_transfers_from_alice_to_diana};\n         sign(trx, charlie_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to transfer 100 CORE from Alice's account to Diana\n         // This attempt should fail because Alice has not authorized Bob to transfer to Diana\n         //////\n         trx.clear();\n         trx.operations = {bob_transfers_from_alice_to_diana};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n   /**\n    * Test of authorization and revocation of one account (Alice) authorizing another account (Bob)\n    * to trade with her account but not to transfer out of her account\n    */\n   BOOST_AUTO_TEST_CASE(authorized_trader_custom_auths) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Define a market-issued asset called USDBIT\n         //////\n         ACTORS((feedproducer))\n         create_bitasset(\"USDBIT\", feedproducer_id);\n         generate_blocks(1);\n         const auto& bitusd = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"USDBIT\");\n         const auto &core = asset_id_type()(db);\n         update_feed_producers(bitusd, {feedproducer.id});\n\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(5);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         //////\n         // Initialize: Fund some accounts\n         //////\n         ACTORS((alice)(bob)(charlie)(diana))\n         fund(alice, asset(5000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n         fund(bob, asset(100 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         //////\n         // Bob attempts to create a limit order on behalf of Alice\n         // This should fail because Bob is not authorized to trade with her account\n         //////\n         set_expiration( db, trx );\n         trx.operations.clear();\n\n         limit_order_create_operation buy_order;\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = core.amount(59);\n         buy_order.min_to_receive = bitusd.amount(7);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to place limit orders that offer the any asset for sale\n         //////\n         custom_authority_create_operation authorize_limit_orders;\n         authorize_limit_orders.account = alice.get_id();\n         authorize_limit_orders.auth.add_authority(bob.get_id(), 1);\n         authorize_limit_orders.auth.weight_threshold = 1;\n         authorize_limit_orders.enabled = true;\n         authorize_limit_orders.valid_to = db.head_block_time() + 1000;\n         authorize_limit_orders.operation_type = operation::tag<limit_order_create_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_limit_orders};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n         auto caa =\n                 db.get_index_type<custom_authority_index>().indices().get<by_account_custom>().find(alice.get_id());\n         custom_authority_id_type auth_id = caa->id;\n\n         custom_authority_create_operation authorize_limit_order_cancellations;\n         authorize_limit_order_cancellations.account = alice.get_id();\n         authorize_limit_order_cancellations.auth.add_authority(bob.get_id(), 1);\n         authorize_limit_order_cancellations.auth.weight_threshold = 1;\n         authorize_limit_order_cancellations.enabled = true;\n         authorize_limit_order_cancellations.valid_to = db.head_block_time() + 1000;\n         authorize_limit_order_cancellations.operation_type = operation::tag<limit_order_cancel_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_limit_order_cancellations};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the buy order transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to create a limit order on behalf of Alice\n         // This should succeed because Bob is authorized to create limit orders\n         //////\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, bob_private_key);\n         auto processed_buy = PUSH_TX(db, trx);\n         const limit_order_object *buy_order_object = db.find<limit_order_object>( processed_buy.operation_results[0].get<object_id_type>() );\n\n\n         //////\n         // Bob attempts to cancel the limit order on behalf of Alice\n         // This should succeed because Bob is authorized to cancel limit orders\n         //////\n         limit_order_cancel_operation cancel_order;\n         cancel_order.fee_paying_account = alice_id;\n         cancel_order.order = buy_order_object->id;\n         trx.clear();\n         trx.operations = {cancel_order};\n         sign(trx, bob_private_key);\n         auto processed_cancelled = PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to transfer funds out of Alice's account\n         // This should fail because Bob is not authorized to transfer funds out of her account\n         //////\n         transfer_operation top;\n         top.to = bob.get_id();\n         top.from = alice.get_id();\n         top.amount.amount = 99 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the buy order transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Alice attempts to create her own limit order\n         // This should succeed because Alice has not relinquished her own authority to trade\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = core.amount(59);\n         buy_order.min_to_receive = bitusd.amount(7);\n         buy_order.expiration = time_point_sec::maximum();\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Alice revokes/disables the authorization to create limit orders\n         //////\n         custom_authority_update_operation disable_authorizations;\n         disable_authorizations.account = alice.get_id();\n         disable_authorizations.authority_to_update = auth_id;\n         disable_authorizations.new_enabled = false;\n         trx.clear();\n         trx.operations = {disable_authorizations};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the buy order transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to create a limit order on behalf of Alice\n         // This should fail because Bob is not authorized to trade with her account\n         //////\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of authorization of one account (Alice) authorizing another key\n    * for restricted trading between between ACOIN1 and any BCOIN (BCOIN1, BCOIN2, and BCOIN3).\n    *\n    * The restricted trading authortization will be constructed with one custom authority\n    * containing two \"logical_or\" branches.  One branch authorizes selling ACOINs for BCOINs.\n    * Another branch authorizes selling BCOINs for ACOINs.\n    */\n   BOOST_AUTO_TEST_CASE(authorized_restricted_trading_key) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Fund some accounts\n         //////\n         ACTORS((assetissuer)(alice))\n         fund(alice, asset(5000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         //////\n         // Define a key that can be authorized\n         // This can be a new key or an existing key. The existing key may even be the active key of an account.\n         //////\n         fc::ecc::private_key some_private_key = generate_private_key(\"some key\");\n         public_key_type some_public_key = public_key_type(some_private_key.get_public_key());\n\n\n         //////\n         // Initialize: Create user-issued assets\n         //////\n         upgrade_to_lifetime_member(assetissuer);\n         create_user_issued_asset(\"ACOIN1\", assetissuer,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         create_user_issued_asset(\"BCOIN1\", assetissuer,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         create_user_issued_asset(\"BCOIN2\", assetissuer,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         create_user_issued_asset(\"BCOIN3\", assetissuer,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         create_user_issued_asset(\"CCOIN1\", assetissuer,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         generate_blocks(1);\n         const asset_object &acoin1 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"ACOIN1\");\n         const asset_object &bcoin1 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"BCOIN1\");\n         const asset_object &bcoin2 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"BCOIN2\");\n         const asset_object &bcoin3 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"BCOIN3\");\n         const asset_object &ccoin1 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"CCOIN1\");\n\n         //////\n         // Initialize: Issue UIAs\n         //////\n\n         // Lambda for issuing an asset to an account\n         auto issue_amount_to = [](const account_id_type &issuer, const asset &amount, const account_id_type &to) {\n            asset_issue_operation op;\n            op.issuer = issuer;\n            op.asset_to_issue = amount;\n            op.issue_to_account = to;\n\n            return op;\n         };\n\n         // assetissuer issues A1, B1, and C1 to alice\n         asset_issue_operation issue_a1_to_alice_op\n                 = issue_amount_to(assetissuer.get_id(), asset(1000, acoin1.id), alice.get_id());\n         asset_issue_operation issue_b1_to_alice_op\n                 = issue_amount_to(assetissuer.get_id(), asset(2000, bcoin1.id), alice.get_id());\n         asset_issue_operation issue_c1_to_alice_op\n                 = issue_amount_to(assetissuer.get_id(), asset(2000, ccoin1.id), alice.get_id());\n         trx.clear();\n         trx.operations = {issue_a1_to_alice_op, issue_b1_to_alice_op, issue_c1_to_alice_op};\n         sign(trx, assetissuer_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Some key attempts to create a limit order on behalf of Alice\n         // This should fail because the key is not authorized to trade with her account\n         //////\n         set_expiration( db, trx );\n         trx.operations.clear();\n\n         limit_order_create_operation buy_order;\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = acoin1.amount(60);\n         buy_order.min_to_receive = bcoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for the key's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes a particular key to place limit orders that offer the any asset for sale\n         //////\n         custom_authority_create_operation authorize_limit_orders;\n         authorize_limit_orders.account = alice.get_id();\n         authorize_limit_orders.auth.add_authority(some_public_key, 1);\n         authorize_limit_orders.auth.weight_threshold = 1;\n         authorize_limit_orders.enabled = true;\n         authorize_limit_orders.valid_to = db.head_block_time() + 1000;\n         authorize_limit_orders.operation_type = operation::tag<limit_order_create_operation>::value;\n\n         auto amount_to_sell_index = member_index<limit_order_create_operation>(\"amount_to_sell\");\n         auto min_to_receive_index = member_index<limit_order_create_operation>(\"min_to_receive\");\n         auto asset_id_index = member_index<asset>(\"asset_id\");\n\n         // Define the two set of assets: ACOINs and BCOINs\n         restriction is_acoin_rx = restriction(asset_id_index, FUNC(in),\n                                               flat_set<asset_id_type>{acoin1.id});\n         restriction is_bcoin_rx = restriction(asset_id_index, FUNC(in),\n                                               flat_set<asset_id_type>{bcoin1.id, bcoin2.id, bcoin3.id});\n\n         // Custom Authority 1: Sell ACOINs to buy BCOINs\n         restriction sell_acoin_rx = restriction(amount_to_sell_index, FUNC(attr), vector<restriction>{is_acoin_rx});\n\n         restriction buy_bcoin_rx = restriction(min_to_receive_index, FUNC(attr), vector<restriction>{is_bcoin_rx});\n\n         vector<restriction> branch_sell_acoin_buy_bcoin = {sell_acoin_rx, buy_bcoin_rx};\n\n\n         // Custom Authority 2: Sell BCOINs to buy ACOINs\n         restriction sell_bcoin_rx = restriction(amount_to_sell_index, FUNC(attr), vector<restriction>{is_bcoin_rx});\n         restriction buy_acoin_rx = restriction(min_to_receive_index, FUNC(attr), vector<restriction>{is_acoin_rx});\n\n         vector<restriction> branch_sell_bcoin_buy_acoin = {sell_bcoin_rx, buy_acoin_rx};\n\n\n         unsigned_int dummy_index = 999;\n         restriction trade_acoin_for_bcoin_rx = restriction(dummy_index, FUNC(logical_or),\n                                                            vector<vector<restriction>>{branch_sell_acoin_buy_bcoin,\n                                                                                        branch_sell_bcoin_buy_acoin});\n         authorize_limit_orders.restrictions = {trade_acoin_for_bcoin_rx};\n         //[\n         //  {\n         //    \"member_index\": 999,\n         //    \"restriction_type\": 11,\n         //    \"argument\": [\n         //      40,\n         //      [\n         //        [\n         //          {\n         //            \"member_index\": 2,\n         //            \"restriction_type\": 10,\n         //            \"argument\": [\n         //              39,\n         //              [\n         //                {\n         //                  \"member_index\": 1,\n         //                  \"restriction_type\": 6,\n         //                  \"argument\": [\n         //                    27,\n         //                    [\n         //                      \"1.3.2\"\n         //                    ]\n         //                  ],\n         //                  \"extensions\": []\n         //                }\n         //              ]\n         //            ],\n         //            \"extensions\": []\n         //          },\n         //          {\n         //            \"member_index\": 3,\n         //            \"restriction_type\": 10,\n         //            \"argument\": [\n         //              39,\n         //              [\n         //                {\n         //                  \"member_index\": 1,\n         //                  \"restriction_type\": 6,\n         //                  \"argument\": [\n         //                    27,\n         //                    [\n         //                      \"1.3.3\",\n         //                      \"1.3.4\",\n         //                      \"1.3.5\"\n         //                    ]\n         //                  ],\n         //                  \"extensions\": []\n         //                }\n         //              ]\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ],\n         //        [\n         //          {\n         //            \"member_index\": 2,\n         //            \"restriction_type\": 10,\n         //            \"argument\": [\n         //              39,\n         //              [\n         //                {\n         //                  \"member_index\": 1,\n         //                  \"restriction_type\": 6,\n         //                  \"argument\": [\n         //                    27,\n         //                    [\n         //                      \"1.3.3\",\n         //                      \"1.3.4\",\n         //                      \"1.3.5\"\n         //                    ]\n         //                  ],\n         //                  \"extensions\": []\n         //                }\n         //              ]\n         //            ],\n         //            \"extensions\": []\n         //          },\n         //          {\n         //            \"member_index\": 3,\n         //            \"restriction_type\": 10,\n         //            \"argument\": [\n         //              39,\n         //              [\n         //                {\n         //                  \"member_index\": 1,\n         //                  \"restriction_type\": 6,\n         //                  \"argument\": [\n         //                    27,\n         //                    [\n         //                      \"1.3.2\"\n         //                    ]\n         //                  ],\n         //                  \"extensions\": []\n         //                }\n         //              ]\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ]\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         // Broadcast the authorization\n         trx.clear();\n         trx.operations = {authorize_limit_orders};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         // Authorize the cancellation of orders\n         custom_authority_create_operation authorize_limit_order_cancellations;\n         authorize_limit_order_cancellations.account = alice.get_id();\n         authorize_limit_order_cancellations.auth.add_authority(some_public_key, 1);\n         authorize_limit_order_cancellations.auth.weight_threshold = 1;\n         authorize_limit_order_cancellations.enabled = true;\n         authorize_limit_order_cancellations.valid_to = db.head_block_time() + 1000;\n         authorize_limit_order_cancellations.operation_type = operation::tag<limit_order_cancel_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_limit_order_cancellations};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the buy order transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice\n         // This should succeed because Bob is authorized to create limit orders\n         //////\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         auto processed_buy = PUSH_TX(db, trx);\n         const limit_order_object *buy_order_object =\n            db.find<limit_order_object>( processed_buy.operation_results[0].get<object_id_type>() );\n\n\n         //////\n         // The key attempts to cancel the limit order on behalf of Alice\n         // This should succeed because the key is authorized to cancel limit orders\n         //////\n         limit_order_cancel_operation cancel_order;\n         cancel_order.fee_paying_account = alice_id;\n         cancel_order.order = buy_order_object->id;\n         trx.clear();\n         trx.operations = {cancel_order};\n         sign(trx, some_private_key);\n         auto processed_cancelled = PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the buy order transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell ACOIN1 for CCOIN1\n         // This should fail because the key is not authorized to sell ACOIN1 for CCOIN1\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = acoin1.amount(60);\n         buy_order.min_to_receive = ccoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell CCOIN1 for ACOIN1\n         // This should fail because the key is not authorized to create this exchange offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = ccoin1.amount(60);\n         buy_order.min_to_receive = acoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell BCOIN1 for CCOIN1\n         // This should fail because the key is not authorized to create this exchange offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = bcoin1.amount(60);\n         buy_order.min_to_receive = ccoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell CCOIN1 for BCOIN1\n         // This should fail because the key is not authorized to create this exchange offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = ccoin1.amount(60);\n         buy_order.min_to_receive = bcoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell BCOIN1 for BCOIN2\n         // This should fail because the key is NOT authorized to create this exchange offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = bcoin1.amount(60);\n         buy_order.min_to_receive = bcoin2.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell ACOIN1 for BCOIN1\n         // This should succeed because the key is authorized to create this offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = acoin1.amount(60);\n         buy_order.min_to_receive = bcoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell ACOIN1 for BCOIN2\n         // This should succeed because the key is authorized to create this offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = acoin1.amount(60);\n         buy_order.min_to_receive = bcoin2.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell ACOIN1 for BCOIN3\n         // This should succeed because the key is authorized to create this offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = acoin1.amount(60);\n         buy_order.min_to_receive = bcoin3.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // The key attempts to create a limit order on behalf of Alice to sell BCOIN1 for ACOIN1\n         // This should succeed because the key is authorized to create this offer\n         //////\n         buy_order = limit_order_create_operation();\n         buy_order.seller = alice_id;\n         buy_order.amount_to_sell = bcoin1.amount(60);\n         buy_order.min_to_receive = acoin1.amount(15);\n         buy_order.expiration = time_point_sec::maximum();\n\n         trx.clear();\n         trx.operations = {buy_order};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of authorization of one account (feedproducer) authorizing another account (Bob)\n    * to publish feeds. The authorization remains associated with account even when the account changes its keys.\n    */\n   BOOST_AUTO_TEST_CASE(feed_publisher_authorizes_other_account) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Define a market-issued asset called USDBIT\n         //////\n         ACTORS((feedproducer));\n         create_bitasset(\"USDBIT\", feedproducer_id);\n         generate_blocks(1);\n         const auto& bitusd = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"USDBIT\");\n         const auto &core = asset_id_type()(db);\n         update_feed_producers(bitusd, {feedproducer.id});\n\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(5);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         //////\n         // Initialize: Fund other accounts\n         //////\n         ACTORS((bob))\n         fund(bob, asset(100 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the publish feed transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to publish feed of USDBIT on behalf of feedproducer\n         // This should fail because Bob is not authorized to publish the feed\n         //////\n         asset_publish_feed_operation pop;\n         pop.publisher = feedproducer.id;\n         pop.asset_id = bitusd.id;\n         pop.feed = current_feed;\n         if (pop.feed.core_exchange_rate.is_null())\n            pop.feed.core_exchange_rate = pop.feed.settlement_price;\n         trx.clear();\n         trx.operations.emplace_back(std::move(pop));\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // feedproducer authorizes Bob to publish feeds on its behalf\n         //////\n         custom_authority_create_operation authorize_feed_publishing;\n         authorize_feed_publishing.account = feedproducer.get_id();\n         authorize_feed_publishing.auth.add_authority(bob.get_id(), 1);\n         authorize_feed_publishing.auth.weight_threshold = 1;\n         authorize_feed_publishing.enabled = true;\n         authorize_feed_publishing.valid_to = db.head_block_time() + 1000;\n         authorize_feed_publishing.operation_type = operation::tag<asset_publish_feed_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_feed_publishing};\n         sign(trx, feedproducer_private_key);\n         PUSH_TX(db, trx);\n\n         custom_authority_id_type auth_id =\n                 db.get_index_type<custom_authority_index>().indices().get<by_account_custom>().find(feedproducer.id)->id;\n\n         //////\n         // Bob attempts to publish feed of USDBIT on behalf of feedproducer\n         // This should succeed because Bob is authorized by feedproducer to publish the feed\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(pop));\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the publish feed transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob creates a new key\n         //////\n         fc::ecc::private_key new_bob_private_key = generate_private_key(\"new Bob key\");\n         public_key_type new_bob_public_key = public_key_type(new_bob_private_key.get_public_key());\n\n\n         //////\n         // Bob attempts to publish feed of USDBIT on behalf of feedproducer with new key\n         // This should fail because the new key is not associated with Bob on the blockchain\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(pop));\n         sign(trx, new_bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the publish feed transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob changes his account's active key\n         //////\n         account_update_operation uop;\n         uop.account = bob.get_id();\n         uop.active = authority(1, new_bob_public_key, 1);\n         trx.clear();\n         trx.operations.emplace_back(std::move(uop));\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to publish feed of USDBIT on behalf of feedproducer\n         // This should succeed because Bob's new key is associated with Bob's authorized account.\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(pop));\n         sign(trx, new_bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Feedproducer revokes/disables the authorization by disabling it\n         //////\n         custom_authority_update_operation disable_authorizations;\n         disable_authorizations.account = feedproducer.get_id();\n         disable_authorizations.authority_to_update = auth_id;\n         disable_authorizations.new_enabled = false;\n         trx.clear();\n         trx.operations = {disable_authorizations};\n         sign(trx, feedproducer_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the publish feed transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to publish feed of USDBIT on behalf of feedproducer with new key\n         // This should fail because Bob's account is no longer authorized by feedproducer\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(pop));\n         sign(trx, new_bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of authorization of one account (feedproducer) authorizing another key\n    * to publish feeds\n    */\n   BOOST_AUTO_TEST_CASE(authorized_feed_publisher_other_key_custom_auths) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Define a market-issued asset called USDBIT\n         //////\n         ACTORS((feedproducer));\n         const auto &bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n         const auto &core = asset_id_type()(db);\n         update_feed_producers(bitusd, {feedproducer.id});\n\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(5);\n         // publish_feed(bitusd, feedproducer, current_feed);\n         asset_publish_feed_operation pop;\n         pop.publisher = feedproducer.id;\n         pop.asset_id = bitusd.id;\n         pop.feed = current_feed;\n         if (pop.feed.core_exchange_rate.is_null())\n            pop.feed.core_exchange_rate = pop.feed.settlement_price;\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the publish feed transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Define a key that can be authorized\n         // This can be a new key or an existing key. The existing key may even be the active key of an account.\n         //////\n         fc::ecc::private_key some_private_key = generate_private_key(\"some key\");\n         public_key_type some_public_key = public_key_type(some_private_key.get_public_key());\n\n\n         //////\n         // feedproducer authorizes a key to publish feeds on its behalf\n         //////\n         custom_authority_create_operation authorize_feed_publishing;\n         authorize_feed_publishing.account = feedproducer.get_id();\n         authorize_feed_publishing.auth.add_authority(some_public_key, 1);\n         authorize_feed_publishing.auth.weight_threshold = 1;\n         authorize_feed_publishing.enabled = true;\n         authorize_feed_publishing.valid_to = db.head_block_time() + 1000;\n         authorize_feed_publishing.operation_type = operation::tag<asset_publish_feed_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_feed_publishing};\n         sign(trx, feedproducer_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Any software client with this key attempts to publish feed of USDBIT on behalf of feedproducer\n         // This should succeed because the pusher of this transaction signs the transaction with the authorized key\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(pop));\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of authorization of one account (faucet) authorizing another key\n    * to register accounts\n    */\n   BOOST_AUTO_TEST_CASE(authorized_faucet_other_key_custom_auths) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: faucet account\n         //////\n         ACTORS((faucet)(charlie));\n         fund(faucet, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n         account_upgrade_operation uop;\n         uop.account_to_upgrade = faucet.get_id();\n         uop.upgrade_to_lifetime_member = true;\n         trx.clear();\n         trx.operations.emplace_back(std::move(uop));\n         sign(trx, faucet_private_key);\n         PUSH_TX(db, trx);\n\n         // Lambda for creating account\n         auto create_account_by_name = [&](const string &name, const account_object& registrar) {\n            account_create_operation create_op;\n            create_op.name = name;\n            public_key_type new_key = public_key_type(generate_private_key(name + \" seed\").get_public_key());\n            create_op.registrar = registrar.id;\n            create_op.owner = authority(1, new_key, 1);\n            create_op.active = authority(1, new_key, 1);\n            create_op.options.memo_key = new_key;\n            create_op.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n\n            return create_op;\n         };\n\n\n         //////\n         // Attempt to register an account with this key\n         // This should succeed because faucet is a lifetime member account\n         //////\n         string name = \"account1\";\n         account_create_operation create_op = create_account_by_name(name, faucet);\n         trx.clear();\n         trx.operations = {create_op};\n         sign(trx, faucet_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Define a key that can be authorized\n         // This can be a new key or an existing key. The existing key may even be the active key of an account.\n         //////\n         fc::ecc::private_key some_private_key = generate_private_key(\"some key\");\n         public_key_type some_public_key = public_key_type(some_private_key.get_public_key());\n\n\n         //////\n         // Attempt to register an account with this key\n         // This should fail because the key is not authorized to register any accounts\n         //////\n         name = \"account2\";\n         create_op = create_account_by_name(name, faucet);\n         trx.clear();\n         trx.operations = {create_op};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n\n         //////\n         // faucet authorizes a key to register accounts on its behalf\n         //////\n         custom_authority_create_operation authorize_account_registration;\n         authorize_account_registration.account = faucet.get_id();\n         authorize_account_registration.auth.add_authority(some_public_key, 1);\n         authorize_account_registration.auth.weight_threshold = 1;\n         authorize_account_registration.enabled = true;\n         authorize_account_registration.valid_to = db.head_block_time() + 1000;\n         authorize_account_registration.operation_type = operation::tag<account_create_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_account_registration};\n         sign(trx, faucet_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the account registration transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Attempt to register an account with this key\n         // This should succeed because the key is authorized to register any accounts\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(create_op));\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Attempt to register an account with this key\n         // This should succeed because the key is authorized to register any accounts\n         //////\n         create_op = create_account_by_name(\"account3\", faucet);\n         trx.clear();\n         trx.operations = {create_op};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Attempt to transfer funds out of the faucet account\n         // This should fail because the key is not authorized to transfer from the faucet account\n         //////\n         transfer_operation top;\n         top.amount.amount = 99 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         top.from = faucet.get_id();\n         top.to = charlie.get_id();\n         top.fee.asset_id = asset_id_type(1);\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Attempt to register an account with this key\n         // This should succeed because the key is authorized to register any accounts\n         //////\n         create_op = create_account_by_name(\"account4\", faucet);\n         trx.clear();\n         trx.operations = {create_op};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of not equal (ne) restriction on an operation field\n    * Test of CAA for asset_issue_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to issue an asset (ALICECOIN) to any account except a banned account (banned1)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_asset_issue_exceptions_1) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(allowed1)(allowed2)(banned1)(allowed3));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         // Lambda for issuing an asset to an account\n         auto issue_amount_to = [&](const account_id_type &issuer, const asset &amount, const account_id_type &to) {\n            asset_issue_operation op;\n            op.issuer = issuer;\n            op.asset_to_issue = amount;\n            op.issue_to_account = to;\n\n            return op;\n         };\n\n         //////\n         // Create a UIA\n         //////\n         upgrade_to_lifetime_member(alice);\n         create_user_issued_asset(\"ALICECOIN\", alice, white_list);\n         create_user_issued_asset(\"SPECIALCOIN\", alice,  white_list);\n         generate_blocks(1);\n         const asset_object &alicecoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"ALICECOIN\");\n         const asset_object &specialcoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"SPECIALCOIN\");\n         const asset_id_type alicecoin_id = alicecoin.id;\n\n\n         //////\n         // Attempt to issue the UIA to an account with the Alice key\n         // This should succeed because Alice is the issuer\n         //////\n         asset_issue_operation issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin_id), allowed1.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to issue the UIA to an allowed account\n         // This should fail because Bob is not authorized to issue any ALICECOIN\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin_id), allowed2.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to issue assets on its behalf\n         // except for account banned1\n         //////\n         custom_authority_create_operation authorize_to_issue;\n         authorize_to_issue.account = alice.get_id();\n         authorize_to_issue.auth.add_authority(bob.get_id(), 1);\n         authorize_to_issue.auth.weight_threshold = 1;\n         authorize_to_issue.enabled = true;\n         authorize_to_issue.valid_to = db.head_block_time() + 1000;\n         authorize_to_issue.operation_type = operation::tag<asset_issue_operation>::value;\n\n         auto asset_index = member_index<asset_issue_operation>(\"asset_to_issue\");\n         auto asset_id_index = member_index<asset>(\"asset_id\");\n         authorize_to_issue.restrictions.emplace_back(restriction(asset_index, restriction::func_attr, vector<restriction>{\n                 restriction(asset_id_index, restriction::func_eq, alicecoin_id)}));\n         auto issue_to_index = member_index<asset_issue_operation>(\"issue_to_account\");\n         authorize_to_issue.restrictions.emplace_back(issue_to_index, FUNC(ne), banned1.get_id());\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 10,\n         //    \"argument\": [\n         //      39,\n         //      [\n         //        {\n         //          \"member_index\": 1,\n         //          \"restriction_type\": 0,\n         //          \"argument\": [\n         //            8,\n         //            \"1.3.2\"\n         //          ],\n         //          \"extensions\": []\n         //        }\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 1,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.20\"\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {authorize_to_issue};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the reused operation\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to issue the UIA to an allowed account\n         // This should succeed because Bob is now authorized to issue ALICECOIN\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(issue_op));\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to issue the special coin to an allowed account\n         // This should fail because Bob is not authorized to issue SPECIALCOIN to any account\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, specialcoin.id), allowed3.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to issue the UIA to a banned account with the Bob's key\n         // This should fail because Bob is not authorized to issue ALICECOIN to the banned account\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin_id), banned1.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n\n\n   /**\n    * Test of not in (not_in) restriction on an operation field\n    * Test of CAA for asset_issue_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to issue an asset (ALICECOIN) except to 3 banned accounts (banned1, banned2, banned3)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_asset_issue_exceptions_2) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(allowed1)(allowed2)(banned1)(banned2)(banned3)(allowed3));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         // Lambda for issuing an asset to an account\n         auto issue_amount_to = [&](const account_id_type &issuer, const asset &amount, const account_id_type &to) {\n            asset_issue_operation op;\n            op.issuer = issuer;\n            op.asset_to_issue = amount;\n            op.issue_to_account = to;\n\n            return op;\n         };\n\n         //////\n         // Create user-issued assets\n         //////\n         upgrade_to_lifetime_member(alice);\n         create_user_issued_asset(\"ALICECOIN\", alice, white_list);\n         create_user_issued_asset(\"SPECIALCOIN\", alice,  white_list);\n         generate_blocks(1);\n         const asset_object &alicecoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"ALICECOIN\");\n         const asset_object &specialcoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"SPECIALCOIN\");\n         const asset_id_type alicecoin_id = alicecoin.id;\n\n\n         //////\n         // Attempt to issue the UIA to an account with the Alice key\n         // This should succeed because Alice is the issuer\n         //////\n         asset_issue_operation issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin_id), allowed1.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to issue the UIA to an allowed account\n         // This should fail because Bob is not authorized to issue any ALICECOIN\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin_id), allowed2.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to issue assets on its behalf\n         // except for accounts banned1, banned2, and banned3\n         //////\n         custom_authority_create_operation authorize_to_issue;\n         authorize_to_issue.account = alice.get_id();\n         authorize_to_issue.auth.add_authority(bob.get_id(), 1);\n         authorize_to_issue.auth.weight_threshold = 1;\n         authorize_to_issue.enabled = true;\n         authorize_to_issue.valid_to = db.head_block_time() + 1000;\n         authorize_to_issue.operation_type = operation::tag<asset_issue_operation>::value;\n\n         auto asset_index = member_index<asset_issue_operation>(\"asset_to_issue\");\n         auto asset_id_index = member_index<asset>(\"asset_id\");\n         authorize_to_issue.restrictions.emplace_back(restriction(asset_index, restriction::func_attr, vector<restriction>{\n                 restriction(asset_id_index, restriction::func_eq, alicecoin_id)}));\n         auto issue_to_index = member_index<asset_issue_operation>(\"issue_to_account\");\n         authorize_to_issue.restrictions\n                 .emplace_back(issue_to_index, FUNC(not_in),\n                               flat_set<account_id_type>{banned1.get_id(), banned2.get_id(), banned3.get_id()});\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 10,\n         //    \"argument\": [\n         //      39,\n         //      [\n         //        {\n         //          \"member_index\": 1,\n         //          \"restriction_type\": 0,\n         //          \"argument\": [\n         //            8,\n         //            \"1.3.2\"\n         //          ],\n         //          \"extensions\": []\n         //        }\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 7,\n         //    \"argument\": [\n         //      26,\n         //      [\n         //        \"1.2.20\",\n         //        \"1.2.21\",\n         //        \"1.2.22\"\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {authorize_to_issue};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the reused operation\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to issue the UIA to an allowed account\n         // This should succeed because Bob is now authorized to issue ALICECOIN\n         //////\n         trx.clear();\n         trx.operations.emplace_back(std::move(issue_op));\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to issue the special coin to an allowed account\n         // This should fail because Bob is not authorized to issue SPECIALCOIN to any account\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, specialcoin.id), allowed3.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to issue the UIA to a banned account with the Bob's key\n         // This should fail because Bob is not authorized to issue ALICECOIN to banned account (banned1)\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin.id), banned1.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to issue the UIA to a banned account with the Bob's key\n         // This should fail because Bob is not authorized to issue ALICECOIN to banned account (banned2)\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin.id), banned2.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to issue the UIA to a banned account with the Bob's key\n         // This should fail because Bob is not authorized to issue ALICECOIN to banned account (banned3)\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin.id), banned3.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to issue the UIA to an allowed account\n         // This should succeed because Bob is authorized to issue ALICECOIN to any account\n         //////\n         issue_op = issue_amount_to(alice.get_id(), asset(100, alicecoin.id), allowed3.get_id());\n         trx.clear();\n         trx.operations = {issue_op};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of in (in) restriction on an operation field\n    * Test of CAA for override_transfer_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to override transfer an asset (ALICECOIN) from only 2 accounts (suspicious1, suspicious2)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_override_transfer) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(allowed1)(allowed2)(suspicious1)(suspicious2)(allowed3)(arbitrator));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         // Lambda for issuing an asset to an account\n         auto issue_amount_to = [&](const account_id_type &issuer, const asset &amount, const account_id_type &to) {\n            asset_issue_operation op;\n            op.issuer = issuer;\n            op.asset_to_issue = amount;\n            op.issue_to_account = to;\n\n            return op;\n         };\n\n         // Lambda for reserving an asset from an account\n         auto create_override = [&](const account_id_type &issuer, const account_id_type &from, const asset &amount,\n                                      const account_id_type &to) {\n            override_transfer_operation op;\n            op.issuer = issuer;\n            op.from = from;\n            op.amount = amount;\n            op.to = to;\n\n            return op;\n         };\n\n         //////\n         // Initialize: Create user-issued assets\n         //////\n         upgrade_to_lifetime_member(alice);\n         create_user_issued_asset(\"ALICECOIN\", alice, DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         create_user_issued_asset( \"SPECIALCOIN\", alice,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         generate_blocks(1);\n         const asset_object &alicecoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"ALICECOIN\");\n         const asset_object &specialcoin\n                 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"SPECIALCOIN\");\n\n         //////\n         // Initialize: Alice issues her two coins to different accounts\n         //////\n         asset_issue_operation issue_alice_to_allowed1_op\n                 = issue_amount_to(alice.get_id(), asset(100, alicecoin.id), allowed1.get_id());\n         asset_issue_operation issue_alice_to_allowed2_op\n                 = issue_amount_to(alice.get_id(), asset(200, alicecoin.id), allowed2.get_id());\n         asset_issue_operation issue_alice_to_allowed3_op\n                 = issue_amount_to(alice.get_id(), asset(300, alicecoin.id), allowed3.get_id());\n         asset_issue_operation issue_alice_to_suspicious1_op\n                 = issue_amount_to(alice.get_id(), asset(100, alicecoin.id), suspicious1.get_id());\n         asset_issue_operation issue_alice_to_suspicious2_op\n                 = issue_amount_to(alice.get_id(), asset(200, alicecoin.id), suspicious2.get_id());\n\n         asset_issue_operation issue_special_to_allowed1_op\n                 = issue_amount_to(alice.get_id(), asset(1000, specialcoin.id), allowed1.get_id());\n         asset_issue_operation issue_special_to_allowed2_op\n                 = issue_amount_to(alice.get_id(), asset(2000, specialcoin.id), allowed2.get_id());\n         asset_issue_operation issue_special_to_allowed3_op\n                 = issue_amount_to(alice.get_id(), asset(3000, specialcoin.id), allowed3.get_id());\n         asset_issue_operation issue_special_to_suspicious1_op\n                 = issue_amount_to(alice.get_id(), asset(1000, specialcoin.id), suspicious1.get_id());\n         asset_issue_operation issue_special_to_suspicious2_op\n                 = issue_amount_to(alice.get_id(), asset(2000, specialcoin.id), suspicious2.get_id());\n         trx.clear();\n         trx.operations = {issue_alice_to_allowed1_op, issue_alice_to_allowed2_op, issue_alice_to_allowed3_op,\n                 issue_alice_to_suspicious1_op, issue_alice_to_suspicious2_op,\n                 issue_special_to_allowed1_op, issue_special_to_allowed2_op, issue_special_to_allowed3_op,\n                 issue_special_to_suspicious1_op, issue_special_to_suspicious2_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Alice attempts to override some ALICECOIN from some account\n         // This should succeed because Alice is the issuer\n         //////\n         override_transfer_operation override_op\n                 = create_override(alice.get_id(), allowed1.get_id(), asset(20, alicecoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n         int64_t allowed1_balance_alicecoin_after_override1 = get_balance(allowed1.get_id(), alicecoin.get_id());\n         BOOST_CHECK_EQUAL(allowed1_balance_alicecoin_after_override1, 80);\n\n         override_op\n                 = create_override(alice.get_id(), suspicious1.get_id(), asset(20, alicecoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n         int64_t suspicious1_balance_alicecoin_after_override1\n                 = get_balance(suspicious1.get_id(), alicecoin.get_id());\n         BOOST_CHECK_EQUAL(suspicious1_balance_alicecoin_after_override1, 80);\n\n         override_op\n                 = create_override(alice.get_id(), allowed1.get_id(), asset(200, specialcoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n         int64_t allowed1_balance_specialcoin_after_override1 = get_balance(allowed1.get_id(), specialcoin.id);\n         BOOST_CHECK_EQUAL(allowed1_balance_specialcoin_after_override1, 800);\n\n         override_op\n                 = create_override(alice.get_id(), suspicious1.get_id(), asset(200, specialcoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n         int64_t suspicious1_balance_specialcoin_after_override1 = get_balance(suspicious1.get_id(), specialcoin.id);\n         BOOST_CHECK_EQUAL(suspicious1_balance_specialcoin_after_override1, 800);\n\n\n         //////\n         // Bob attempts to override some ALICECOIN and SPECIAL from some accounts\n         // This should fail because Bob is not authorized to override any ALICECOIN nor SPECIALCOIN\n         //////\n         override_op = create_override(alice.get_id(), allowed1.get_id(), asset(25, alicecoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n         override_op = create_override(alice.get_id(), allowed1.get_id(), asset(25, specialcoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to override transfer ALICECOIN on its behalf\n         // only for accounts suspicious1, and suspicious2\n         //////\n         custom_authority_create_operation authorize_to_override;\n         authorize_to_override.account = alice.get_id();\n         authorize_to_override.auth.add_authority(bob.get_id(), 1);\n         authorize_to_override.auth.weight_threshold = 1;\n         authorize_to_override.enabled = true;\n         authorize_to_override.valid_to = db.head_block_time() + 1000;\n         authorize_to_override.operation_type = operation::tag<override_transfer_operation>::value;\n\n         auto amount_index = member_index<override_transfer_operation>(\"amount\");\n         auto asset_id_index = member_index<asset>(\"asset_id\");\n         authorize_to_override.restrictions\n                 .emplace_back(restriction(amount_index, restriction::func_attr, vector<restriction>{\n                 restriction(asset_id_index, restriction::func_eq, alicecoin.get_id())}));\n         auto from_index = member_index<override_transfer_operation>(\"from\");\n         authorize_to_override.restrictions\n                 .emplace_back(from_index, FUNC(in),\n                               flat_set<account_id_type>{suspicious1.get_id(), suspicious2.get_id()});\n         //[\n         //  {\n         //    \"member_index\": 4,\n         //    \"restriction_type\": 10,\n         //    \"argument\": [\n         //      39,\n         //      [\n         //        {\n         //          \"member_index\": 1,\n         //          \"restriction_type\": 0,\n         //          \"argument\": [\n         //            8,\n         //            \"1.3.2\"\n         //          ],\n         //          \"extensions\": []\n         //        }\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 6,\n         //    \"argument\": [\n         //      26,\n         //      [\n         //        \"1.2.20\",\n         //        \"1.2.21\"\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {authorize_to_override};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the reused operation\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to override transfer some ALICECOIN from a suspicious account\n         // This should succeed because Bob is now authorized to override ALICECOIN from some accounts\n         //////\n         override_op = create_override(alice.get_id(), suspicious1.get_id(), asset(25, alicecoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n         int64_t suspicious1_balance_alicecoin_after_override2\n                 = get_balance(suspicious1.get_id(), alicecoin.get_id());\n         BOOST_CHECK_EQUAL(suspicious1_balance_alicecoin_after_override2, suspicious1_balance_alicecoin_after_override1 - 25);\n\n\n         //////\n         // Bob attempts to override transfer some SPECIALCOIN from a suspicious account\n         // This should fail because Bob is not authorized to override SPECIALCOIN from any accounts\n         //////\n         override_op = create_override(alice.get_id(), suspicious1.get_id(), asset(250, specialcoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to override transfer some SPECIALCOIN from an allowed account\n         // This should fail because Bob is not authorized to override SPECIALCOIN from any accounts\n         //////\n         override_op = create_override(alice.get_id(), allowed3.get_id(), asset(250, specialcoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to override transfer some ALICECOIN from an allowed account\n         // This should fail because Bob is only authorized to override ALICECOIN from suspicious accounts\n         //////\n         override_op = create_override(alice.get_id(), allowed2.get_id(), asset(20, alicecoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         int64_t allowed2_balance_alicecoin_after_no_override\n                 = get_balance(allowed2.get_id(), alicecoin.get_id());\n         BOOST_CHECK_EQUAL(allowed2_balance_alicecoin_after_no_override, 200);\n         int64_t allowed2_balance_specialcoin_no_override\n                 = get_balance(allowed2.get_id(), specialcoin.get_id());\n         BOOST_CHECK_EQUAL(allowed2_balance_specialcoin_no_override, 2000);\n\n\n         //////\n         // Alice attempts to override transfer of SPECIAL COIN from an allowed account\n         // This should succeed because Alice has not revoked her own authorities as issuer\n         //////\n         override_op = create_override(alice.get_id(), allowed3.get_id(), asset(500, specialcoin.id), arbitrator.get_id());\n         trx.clear();\n         trx.operations = {override_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n         int64_t allowed3_balance_alicecoin_after_no_override\n                 = get_balance(allowed3.get_id(), alicecoin.get_id());\n         BOOST_CHECK_EQUAL(allowed3_balance_alicecoin_after_no_override, 300);\n         int64_t allowed3_balance_specialcoin_after_override1\n                 = get_balance(allowed3.get_id(), specialcoin.get_id());\n         BOOST_CHECK_EQUAL(allowed3_balance_specialcoin_after_override1, 3000 - 500);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of authorization of a key to transfer one asset type (USDBIT) from one account (coldwallet)\n    * to another account (hotwallet)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_cold_wallet_key_custom_auths) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         ACTORS((feedproducer)(coldwallet)(hotwallet)(hacker));\n         int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n         //////\n         // Initialize: Define a market-issued asset called USDBIT\n         //////\n         // Define core asset\n         const auto &core = asset_id_type()(db);\n         asset_id_type core_id = core.id;\n\n         // Create a smart asset\n         const asset_object &bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n         asset_id_type usd_id = bitusd.id;\n         update_feed_producers(bitusd, {feedproducer.id});\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(5);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         //////\n         // Fund coldwallet with core asset\n         //////\n         fund(coldwallet, asset(init_balance));\n         // coldwallet will borrow 1000 bitUSD\n         borrow(coldwallet, bitusd.amount(1000), asset(15000));\n         int64_t coldwallet_balance_usd_before_offer = get_balance(coldwallet_id, usd_id);\n         BOOST_CHECK_EQUAL( 1000,  coldwallet_balance_usd_before_offer);\n         int64_t coldwallet_balance_core_before_offer = get_balance(coldwallet_id, core_id);\n         BOOST_CHECK_EQUAL( init_balance - 15000, coldwallet_balance_core_before_offer );\n\n\n         //////\n         // Define a key that can be authorized\n         // This can be a new key or an existing key. The existing key may even be the active key of an account.\n         //////\n         fc::ecc::private_key some_private_key = generate_private_key(\"some key\");\n         public_key_type some_public_key = public_key_type(some_private_key.get_public_key());\n\n\n         //////\n         // Create a custom authority where the key is authorized to transfer from the coldwallet account\n         // if and only if the transfer asset type is USDBIT and the recipient account is hotwallet.\n         //////\n         custom_authority_create_operation op;\n         op.account = coldwallet.get_id();\n         op.auth.add_authority(some_public_key, 1);\n         op.auth.weight_threshold = 1;\n         op.enabled = true;\n         op.valid_to = db.head_block_time() + 1000;\n\n         op.operation_type = operation::tag<transfer_operation>::value;\n\n         auto to_index = member_index<transfer_operation>(\"to\");\n         op.restrictions.emplace_back(to_index, FUNC(eq), hotwallet_id);\n\n         auto transfer_amount_index = member_index<transfer_operation>(\"amount\");\n         auto asset_id_index = member_index<asset>(\"asset_id\");\n         op.restrictions.emplace_back(restriction(transfer_amount_index, restriction::func_attr, vector<restriction>{\n                 restriction(asset_id_index, restriction::func_eq, usd_id)}));\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.18\"\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 10,\n         //    \"argument\": [\n         //      39,\n         //      [\n         //        {\n         //          \"member_index\": 1,\n         //          \"restriction_type\": 0,\n         //          \"argument\": [\n         //            8,\n         //            \"1.3.2\"\n         //          ],\n         //          \"extensions\": []\n         //        }\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n         BOOST_CHECK_EQUAL(restriction::restriction_count(op.restrictions), 3);\n\n         // Publish the new custom authority\n         trx.clear();\n         trx.operations = {op};\n         sign(trx, coldwallet_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Attempt to transfer USDBIT asset out of the coldwallet to the hacker account\n         // This should fail because the key is not authorized to transfer to the hacker account\n         //////\n         transfer_operation top;\n         top.from = coldwallet.get_id();\n         top.to = hacker.get_id();\n         top.amount.asset_id = usd_id;\n         top.amount.amount = 99;\n         top.fee.asset_id = core_id;\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Attempt to transfer CORE asset out of the coldwallet to the hotwallet account\n         // This should fail because the key is not authorized to transfer core asset to the hotwallet account\n         //////\n         top = transfer_operation();\n         top.from = coldwallet.get_id();\n         top.to = hotwallet.get_id();\n         top.amount.asset_id = core_id;\n         top.amount.amount = 99;\n         top.fee.asset_id = core_id;\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, some_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,1],[0,0],[2,\"predicate_was_false\"]\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Attempt to transfer USDBIT asset out of the coldwallet to the hotwallet account\n         // This should succeed because the key is authorized to transfer USDBIT asset to the hotwallet account\n         //////\n         top = transfer_operation();\n         top.from = coldwallet.get_id();\n         top.to = hotwallet.get_id();\n         top.amount.asset_id = usd_id;\n         top.amount.amount = 99;\n         top.fee.asset_id = core_id;\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, some_private_key);\n         PUSH_TX(db, trx);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n   /**\n    * Test of a restriction on an optional operation field\n    * Variation of the the original transfer_with_memo test for CAA\n    * Bob is authorized to transfer Alice's account to Charlies's account if\n    * - the memo is not set OR\n    * - the memo is set where the \"from\" equal's Bob's public key and \"to\" equals Diana's public *active* key\n    * (The active key is chosen for simplicity. Other keys such as the memo key or an alternate key could also be used.)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_transfer_with_memo_1) {\n      try {\n         //////\n         // Initialize the test\n         //////\n         ACTORS((alice)(bob)(charlie)(diana))\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object& gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n         transfer(account_id_type(), alice_id, asset(1000));\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 1000);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 00);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n\n         //////\n         // Alice transfers to Charlie with her own authorization\n         //////\n         transfer_operation top;\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         top.memo = memo_data();\n         top.memo->set_message(alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n         trx.operations = {top};\n         trx.sign(alice_private_key, db.get_chain_id());\n         auto processed = PUSH_TX(db, trx);\n\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 950);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 50);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n         auto memo = db.get_recent_transaction(processed.id()).operations.front().get<transfer_operation>().memo;\n         BOOST_CHECK(memo);\n         BOOST_CHECK_EQUAL(memo->get_message(bob_private_key, alice_public_key), \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie\n         // This should fail because Bob is not authorized\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the re-used transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to transfer to Charlie if\n         // - the memo is not set OR\n         // - the memo is set where the \"from\" equal's Bob's public key and \"to\" equals Diana's public key\n         //////\n         custom_authority_create_operation caop;\n         caop.account = alice.get_id();\n         caop.auth.add_authority(bob.get_id(), 1);\n         caop.auth.weight_threshold = 1;\n         caop.enabled = true;\n         caop.valid_to = db.head_block_time() + 1000;\n         caop.operation_type = operation::tag<transfer_operation>::value;\n\n         vector<restriction> restrictions;\n\n         // Restriction 1 should have \"to\" to equal Charlie\n         auto to_index = member_index<transfer_operation>(\"to\");\n         auto memo_index = member_index<transfer_operation>(\"memo\");\n         auto to_inside_memo_index = member_index<memo_data>(\"to\");\n         restrictions.emplace_back(to_index, FUNC(eq), charlie.get_id());\n\n         // Restriction 2 is logical OR restriction\n         // Branch 1 should have the memo \"to\" to not be set (to equal void)\n         vector<restriction> branch1 = vector<restriction>{restriction(memo_index, FUNC(eq), void_t())};\n         // Branch 2 should have the memo \"to\" reference Diana's public *active* key\n         // and \"from\" reference Bob's public *active* key\n         auto from_inside_memo_index = member_index<memo_data>(\"from\");\n         vector<restriction> branch2 = vector<restriction>{restriction(memo_index, restriction::func_attr,\n                                                                       vector<restriction>{\n                 restriction(to_inside_memo_index, FUNC(eq), diana_public_key),\n                 restriction(from_inside_memo_index, FUNC(eq), bob_public_key)})};\n         unsigned_int dummy_index = 999;\n         restriction or_restriction = restriction(dummy_index, FUNC(logical_or), vector<vector<restriction>>{branch1, branch2});\n         restrictions.emplace_back(or_restriction);\n         caop.restrictions = restrictions;\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.18\"\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 999,\n         //    \"restriction_type\": 11,\n         //    \"argument\": [\n         //      40,\n         //      [\n         //        [\n         //          {\n         //            \"member_index\": 4,\n         //            \"restriction_type\": 0,\n         //            \"argument\": [\n         //              0,\n         //              {}\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ],\n         //        [\n         //          {\n         //            \"member_index\": 4,\n         //            \"restriction_type\": 10,\n         //            \"argument\": [\n         //              39,\n         //              [\n         //                {\n         //                  \"member_index\": 1,\n         //                  \"restriction_type\": 0,\n         //                  \"argument\": [\n         //                    5,\n         //                    \"BTS6MWg7PpE6azCGwKuhB17DbtSqhzf8i25hspdhndsf7VfsLee7k\"\n         //                  ],\n         //                  \"extensions\": []\n         //                },\n         //                {\n         //                  \"member_index\": 0,\n         //                  \"restriction_type\": 0,\n         //                  \"argument\": [\n         //                    5,\n         //                    \"BTS5VE6Dgy9FUmd1mFotXwF88HkQN1KysCWLPqpVnDMjRvGRi1YrM\"\n         //                  ],\n         //                  \"extensions\": []\n         //                }\n         //              ]\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ]\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {caop};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie WITHOUT a memo\n         // This should succeed\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the re-used transfer op\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 900);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 100);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie with a memo\n         // where \"from\" equals Bob's public key and \"to\" equals Diana's public key\n         // This should succeed\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the similar transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         top.memo = memo_data();\n         top.memo->set_message(bob_private_key, diana_public_key,\n                               \"Dear Diana,\\n\\nOnly you should be able to read this\\n\\nLove, Bob\");\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         processed = PUSH_TX(db, trx);\n\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 850);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 150);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n         memo = db.get_recent_transaction(processed.id()).operations.front().get<transfer_operation>().memo;\n         BOOST_CHECK(memo);\n         BOOST_CHECK_EQUAL(memo->get_message(diana_private_key, bob_public_key),\n                           \"Dear Diana,\\n\\nOnly you should be able to read this\\n\\nLove, Bob\");\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie with a memo\n         // where \"from\" equals Bob's public key and \"to\" equals Charlie's public key\n         // This should fail because it violates the memo restriction\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the similar transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         top.memo = memo_data();\n         top.memo->set_message(bob_private_key, charlie_public_key,\n                               \"Dear Charlie,\\n\\nOnly you should be able to read this\\n\\nLove, Bob\");\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n\n         // The failure should indicate a violation of both branches of the OR memo restrictions\n         // JSON style check of the rejection path\n         // JSON-formatted Rejection path\n         //[ // A vector of predicate results\n         //  [\n         //    0, // Index 0 (the outer-most) rejection path\n         //    1  // 1 is the index for Restriction 2\n         //  ],\n         //  [\n         //    1, // A (sub-)vector of predicate results\n         //    [\n         //      {\n         //        \"success\": false,\n         //        \"rejection_path\": [\n         //          [\n         //            0, // Index 0 of Branch 1 rejection path\n         //            0  // Restriction 1 along this branch\n         //          ],\n         //          [\n         //            2, // Rejection reason\n         //            \"predicate_was_false\"\n         //          ]\n         //        ]\n         //      },\n         //      {\n         //        \"success\": false,\n         //        \"rejection_path\": [\n         //          [\n         //            0, // Index 0 of Branch 2 rejection path\n         //            0  // Restriction 1 along this branch\n         //          ],\n         //          [\n         //            0, // Index 1 of Branch 2 rejection path\n         //            0  // First and only attribute of sub-restriction\n         //          ],\n         //          [\n         //            2, // Rejection reeason\n         //            \"predicate_was_false\"\n         //          ]\n         //        ]\n         //      }\n         //    ]\n         //  ]\n         //]\n         EXPECT_EXCEPTION_STRING(\"[[0,1],[1,[{\\\"success\\\":false,\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]},{\\\"success\\\":false,\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]]}]]]\", [&] {PUSH_TX(db, trx);});\n\n         //////\n         // Bob attempts to transfer from Alice to Diana\n         // This should fail because the transfer must be to Charlie\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the similar transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = diana.get_id();\n         top.amount = asset(50);\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of a restriction on an optional operation field\n    * Variation of the the original transfer_with_memo test for CAA\n    * Bob is authorized to transfer from Alice's account to Charlies's account only if\n    * - the memo is set where the \"from\" equal's Bob's public key and \"to\" equals Diana's public *active* key\n    * (The active key is chosen for simplicity. Other keys such as the memo key or an alternate key could also be used.)\n    *\n    * A memo field is implicitly required.  Attempts without a memo field should have a rejection reason of null_optional\n    */\n   BOOST_AUTO_TEST_CASE(authorized_transfer_with_memo_2) {\n      try {\n         //////\n         // Initialize the test\n         //////\n         ACTORS((alice)(bob)(charlie)(diana))\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object& gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n         transfer(account_id_type(), alice_id, asset(1000));\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 1000);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 00);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n\n         //////\n         // Alice transfers to Charlie with her own authorization\n         //////\n         transfer_operation top;\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         top.memo = memo_data();\n         top.memo->set_message(alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n         trx.operations = {top};\n         trx.sign(alice_private_key, db.get_chain_id());\n         auto processed = PUSH_TX(db, trx);\n\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 950);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 50);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n         auto memo = db.get_recent_transaction(processed.id()).operations.front().get<transfer_operation>().memo;\n         BOOST_CHECK(memo);\n         BOOST_CHECK_EQUAL(memo->get_message(bob_private_key, alice_public_key), \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie\n         // This should fail because Bob is not authorized\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the re-used transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to transfer to Charlie if\n         // - the memo is set where the \"from\" equal's Bob's public key and \"to\" equals Diana's public key\n         //////\n         custom_authority_create_operation caop;\n         caop.account = alice.get_id();\n         caop.auth.add_authority(bob.get_id(), 1);\n         caop.auth.weight_threshold = 1;\n         caop.enabled = true;\n         caop.valid_to = db.head_block_time() + 1000;\n         caop.operation_type = operation::tag<transfer_operation>::value;\n\n         vector<restriction> restrictions;\n\n         // Restriction 1 should have \"to\" to equal Charlie\n         auto to_index = member_index<transfer_operation>(\"to\");\n         auto memo_index = member_index<transfer_operation>(\"memo\");\n         auto to_inside_memo_index = member_index<memo_data>(\"to\");\n         restrictions.emplace_back(to_index, FUNC(eq), charlie.get_id());\n\n         // Branch 2 should have the memo \"to\" reference Diana's public *active* key\n         // and \"from\" reference Bob's public *active* key\n         auto from_inside_memo_index = member_index<memo_data>(\"from\");\n         restrictions.emplace_back(restriction(memo_index, restriction::func_attr,\n                                               vector<restriction>{\n                                                       restriction(to_inside_memo_index, FUNC(eq), diana_public_key),\n                                                       restriction(from_inside_memo_index, FUNC(eq), bob_public_key)}));\n         caop.restrictions = restrictions;\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.18\"\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 4,\n         //    \"restriction_type\": 10,\n         //    \"argument\": [\n         //      39,\n         //      [\n         //        {\n         //          \"member_index\": 1,\n         //          \"restriction_type\": 0,\n         //          \"argument\": [\n         //            5,\n         //            \"BTS6MWg7PpE6azCGwKuhB17DbtSqhzf8i25hspdhndsf7VfsLee7k\"\n         //          ],\n         //          \"extensions\": []\n         //        },\n         //        {\n         //          \"member_index\": 0,\n         //          \"restriction_type\": 0,\n         //          \"argument\": [\n         //            5,\n         //            \"BTS5VE6Dgy9FUmd1mFotXwF88HkQN1KysCWLPqpVnDMjRvGRi1YrM\"\n         //          ],\n         //          \"extensions\": []\n         //        }\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {caop};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie WITHOUT a memo\n         // This should fail because Restriction 2 expects a memo\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the re-used transfer op\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [2,\"null_optional\"]: 0 is the rejection_indicator for rejection_reason; \"null_optional\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"null_optional\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie with a memo\n         // where \"from\" equals Bob's public key and \"to\" equals Diana's public key\n         // This should succeed\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the similar transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         top.memo = memo_data();\n         top.memo->set_message(bob_private_key, diana_public_key,\n                               \"Dear Diana,\\n\\nOnly you should be able to read this\\n\\nLove, Bob\");\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         processed = PUSH_TX(db, trx);\n\n         BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 900);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, asset_id_type()), 100);\n         BOOST_CHECK_EQUAL(get_balance(diana_id, asset_id_type()), 0);\n\n         memo = db.get_recent_transaction(processed.id()).operations.front().get<transfer_operation>().memo;\n         BOOST_CHECK(memo);\n         BOOST_CHECK_EQUAL(memo->get_message(diana_private_key, bob_public_key),\n                           \"Dear Diana,\\n\\nOnly you should be able to read this\\n\\nLove, Bob\");\n\n         //////\n         // Bob attempts to transfer from Alice to Charlie with a memo\n         // where \"from\" equals Bob's public key and \"to\" equals Charlie's public key\n         // This should fail because it violates the memo restriction\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the similar transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = charlie.get_id();\n         top.amount = asset(50);\n         top.memo = memo_data();\n         top.memo->set_message(bob_private_key, charlie_public_key,\n                               \"Dear Charlie,\\n\\nOnly you should be able to read this\\n\\nLove, Bob\");\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"null_optional\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n         //////\n         // Bob attempts to transfer from Alice to Diana\n         // This should fail because transfer must be to Charlie\n         //////\n         generate_blocks(1); // Advance the blockchain to generate a distinctive hash ID for the similar transfer op\n         top = transfer_operation();\n         top.from = alice.get_id();\n         top.to = diana.get_id();\n         top.amount = asset(50);\n         trx.clear();\n         trx.operations = {top};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of has none (has_none) restriction on a container field\n    * Test of CAA for asset_update_feed_producers_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to update an asset's feed producers as long as the list does not contain\n    * untrusted producers (untrusted1, untrusted2, untrusted3)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_feed_producers_1) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob));\n         ACTORS((trusted1)(trusted2)(trusted3)(trusted4)(trusted5)(trusted6));\n         ACTORS((untrusted1)(untrusted2)(untrusted3));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         // Lambda for update asset feed producers\n         auto create_producers_op = [&](const account_id_type &issuer, const asset_id_type &asset, const flat_set<account_id_type> &new_producers) {\n            asset_update_feed_producers_operation op;\n\n            op.issuer = issuer;\n            op.asset_to_update = asset;\n            op.new_feed_producers = new_producers;\n\n            return op;\n         };\n\n\n         //////\n         // Create user-issued assets\n         //////\n         upgrade_to_lifetime_member(alice);\n         create_bitasset(\"ALICECOIN\", alice.get_id());\n         generate_blocks(1);\n         const asset_object &alicecoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"ALICECOIN\");\n\n\n         //////\n         // Alice attempts to update the feed producers for ALICECOIN\n         // This should succeed because Alice can update her own asset\n         //////\n         flat_set<account_id_type> new_producers = {trusted1.get_id(), trusted2.get_id()};\n         asset_update_feed_producers_operation producers_op\n                 = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the same transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN\n         // This should fail because Bob is not authorized to update feed producers for ALICECOIN\n         //////\n         new_producers = {trusted3.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to update the feed producers\n         // but must not select untrusted1, untrusted2, untrusted3\n         //////\n         custom_authority_create_operation authorize_to_update_feed_producers;\n         authorize_to_update_feed_producers.account = alice.get_id();\n         authorize_to_update_feed_producers.auth.add_authority(bob.get_id(), 1);\n         authorize_to_update_feed_producers.auth.weight_threshold = 1;\n         authorize_to_update_feed_producers.enabled = true;\n         authorize_to_update_feed_producers.valid_to = db.head_block_time() + 1000;\n\n         authorize_to_update_feed_producers.operation_type = operation::tag<asset_update_feed_producers_operation>::value;\n         flat_set<account_id_type> untrusted_producers = {untrusted1.get_id(), untrusted2.get_id(), untrusted3.get_id()};\n         auto new_feed_producers_index = member_index<asset_update_feed_producers_operation>(\"new_feed_producers\");\n         authorize_to_update_feed_producers.restrictions\n                 .emplace_back(new_feed_producers_index, FUNC(has_none), untrusted_producers);\n         //[\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 9,\n         //    \"argument\": [\n         //      26,\n         //      [\n         //        \"1.2.24\",\n         //        \"1.2.25\",\n         //        \"1.2.26\"\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {authorize_to_update_feed_producers};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the same transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN\n         // This should succeed because Bob is now authorized to update the feed producers\n         // and because the selected feed producers are acceptable\n         //////\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with 1 trusted and 1 untrusted account\n         // This should fail because Bob is not authorized to update the feed producers\n         // when an untrusted account is included\n         //////\n         new_producers = {trusted4.get_id(), untrusted1.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with 1 untrusted account\n         // This should fail because Bob is not authorized to update the feed producers\n         // when an untrusted account is included\n         //////\n         new_producers = {trusted4.get_id(), untrusted1.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with two untrusted accounts\n         // This should fail because Bob is not authorized to update the feed producers\n         // when an untrusted account is included\n         //////\n         new_producers = {untrusted2.get_id(), untrusted3.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of has all (has_all) restriction on a container field\n    * Test of CAA for asset_update_feed_producers_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to update an asset's feed producers as long as the list\n    * always includes trusted producers (trusted1, trusted2, trusted3)\n    */\n   BOOST_AUTO_TEST_CASE(authorized_feed_producers_2) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob));\n         ACTORS((trusted1)(trusted2)(trusted3));\n         ACTORS((unknown1)(unknown2)(unknown3)(unknown4)(unknown5)(unknown6)(unknown7)(unknown8)(unknown9));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         // Lambda for update asset feed producers\n         auto create_producers_op = [&](const account_id_type &issuer, const asset_id_type &asset, const flat_set<account_id_type> &new_producers) {\n            asset_update_feed_producers_operation op;\n\n            op.issuer = issuer;\n            op.asset_to_update = asset;\n            op.new_feed_producers = new_producers;\n\n            return op;\n         };\n\n\n         //////\n         // Create user-issued assets\n         //////\n         upgrade_to_lifetime_member(alice);\n         create_bitasset(\"ALICECOIN\", alice.get_id());\n         generate_blocks(1);\n         const asset_object &alicecoin = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"ALICECOIN\");\n\n\n         //////\n         // Alice attempts to update the feed producers for ALICECOIN\n         // This should succeed because Alice can update her own asset\n         //////\n         flat_set<account_id_type> new_producers = {trusted1.get_id(), trusted2.get_id(), trusted3.get_id()};\n         asset_update_feed_producers_operation producers_op\n                 = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the same transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with the required feed producers\n         // and an extra account\n         // This should fail because Bob is not authorized to update feed producers for ALICECOIN\n         //////\n         new_producers = {trusted1.get_id(), trusted2.get_id(), trusted3.get_id(), unknown1.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n         // \"rejected_custom_auths\":[]\n         EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Alice authorizes Bob to update the feed producers\n         // but must not select untrusted1, untrusted2, untrusted3\n         //////\n         custom_authority_create_operation authorize_to_update_feed_producers;\n         authorize_to_update_feed_producers.account = alice.get_id();\n         authorize_to_update_feed_producers.auth.add_authority(bob.get_id(), 1);\n         authorize_to_update_feed_producers.auth.weight_threshold = 1;\n         authorize_to_update_feed_producers.enabled = true;\n         authorize_to_update_feed_producers.valid_to = db.head_block_time() + 1000;\n\n         authorize_to_update_feed_producers.operation_type = operation::tag<asset_update_feed_producers_operation>::value;\n         flat_set<account_id_type> trusted_producers = {trusted1.get_id(), trusted2.get_id(), trusted3.get_id()};\n         auto new_feed_producers_index = member_index<asset_update_feed_producers_operation>(\"new_feed_producers\");\n         authorize_to_update_feed_producers.restrictions\n                 .emplace_back(new_feed_producers_index, FUNC(has_all), trusted_producers);\n         //[\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 8,\n         //    \"argument\": [\n         //      26,\n         //      [\n         //        \"1.2.18\",\n         //        \"1.2.19\",\n         //        \"1.2.20\"\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n         trx.clear();\n         trx.operations = {authorize_to_update_feed_producers};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate a distinctive hash ID for the same transaction\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with the required feed producers\n         // and an extra account\n         // This should succeed because Bob is now authorized to update the feed producers\n         // and because the all of the required feed producers are included\n         //////\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with none of the required feed producers\n         // This should fail not all of the required feed producers are included\n         //////\n         new_producers = {unknown2.get_id(), unknown3.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with only 1 of the required feed producers\n         // and extra accounts\n         // This should fail not all of the required feed producers are included\n         //////\n         new_producers = {trusted1.get_id(), unknown2.get_id(), unknown3.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with only 2 of the required feed producers\n         // and extra accounts\n         // This should fail not all of the required feed producers are included\n         //////\n         new_producers = {trusted1.get_id(), unknown2.get_id(), unknown3.get_id(), trusted2.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n         // The failure should indicate the rejection path\n         // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n         // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n         // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n         EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with all of the required feed producers\n         // and extra accounts\n         // This should succeed because Bob is now authorized to update the feed producers\n         // and because the all of the required feed producers are included\n         //////\n         new_producers = {trusted1.get_id(), unknown2.get_id(), unknown3.get_id(), trusted2.get_id(), trusted3.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n         //////\n         // Bob attempts to update the feed producers for ALICECOIN with all of the required feed producers\n         // in a different order\n         // This should succeed because Bob is now authorized to update the feed producers\n         // and because the all of the required feed producers are included\n         //////\n         new_producers = {trusted3.get_id(), trusted2.get_id(), trusted1.get_id()};\n         producers_op = create_producers_op(alice.get_id(), alicecoin.id, new_producers);\n         trx.clear();\n         trx.operations = {producers_op};\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Generate a random pre-image for HTLC-related tests\n    */\n   void generate_random_preimage(uint16_t key_size, std::vector<char>& vec)\n   {\n      std::independent_bits_engine<std::default_random_engine, sizeof(unsigned), unsigned int> rbe;\n      std::generate(begin(vec), end(vec), std::ref(rbe));\n      return;\n   }\n\n\n   /**\n    * Test of greater than or equal to (ge) restriction on a field\n    * Test of CAA for htlc_create_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to create an HTLC operation as long as the pre-image size is greater than or equal to a specified size\n    *\n    * This test is similar to the HTLC test called \"other_peoples_money\"\n    */\n   BOOST_AUTO_TEST_CASE(authorized_htlc_creation) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         time_point_sec LATER_HF_TIME\n                 = (HARDFORK_BSIP_40_TIME > HARDFORK_CORE_1468_TIME) ? HARDFORK_BSIP_40_TIME : HARDFORK_CORE_1468_TIME;\n         generate_blocks(LATER_HF_TIME);\n         generate_blocks(5);\n         set_expiration(db, trx);\n\n         // Initialize HTLC blockchain parameters\n         trx.clear();\n         set_htlc_committee_parameters();\n         generate_blocks(5);\n\n         // Initialize CAA blockchain parameters\n         trx.clear();\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(gateway));\n         int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n\n\n         //////\n         // Initialize: Pre-image sizes and pre-images to reduce the test variability\n         //////\n         uint16_t pre_image_size_256 = 256;\n         std::vector<char> pre_image_256(pre_image_size_256);\n         generate_random_preimage(pre_image_size_256, pre_image_256);\n\n         // The minimum pre-image size that will be authorized by Alice\n         uint16_t authorized_minimum_pre_image_size_512 = 512;\n\n         int64_t pre_image_size_512 = int64_t(authorized_minimum_pre_image_size_512 + 0);\n         std::vector<char> pre_image_512(pre_image_size_512);\n         generate_random_preimage(pre_image_size_512, pre_image_512);\n\n         int64_t pre_image_size_600 = int64_t(authorized_minimum_pre_image_size_512 + 88);\n         std::vector<char> pre_image_600(pre_image_size_600);\n         generate_random_preimage(pre_image_size_600, pre_image_600);\n\n\n         //////\n         // Alice attempts to put a contract on the blockchain using Alice's funds\n         // This should succeed because Alice is authorized to create HTLC for her own account\n         //////\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 3;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_256 );\n            create_operation.preimage_size = pre_image_size_256;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, alice_private_key);\n            PUSH_TX( db, trx );\n         }\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the similar transactions\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to put a contract on the blockchain using Alice's funds\n         // This should fail because Bob is not authorized to create HTLC on behalf of Alice\n         //////\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 3;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_256 );\n            create_operation.preimage_size = pre_image_size_256;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Alice authorizes Bob to create HTLC only to an account (gateway)\n         // and if the pre-image size is greater than or equal to 512 bytes\n         //////\n         custom_authority_create_operation authorize_htlc_create;\n         authorize_htlc_create.account = alice.get_id();\n         authorize_htlc_create.auth.add_authority(bob.get_id(), 1);\n         authorize_htlc_create.auth.weight_threshold = 1;\n         authorize_htlc_create.enabled = true;\n         authorize_htlc_create.valid_to = db.head_block_time() + 1000;\n         authorize_htlc_create.operation_type = operation::tag<htlc_create_operation>::value;\n\n         auto to_index = member_index<htlc_create_operation>(\"to\");\n         authorize_htlc_create.restrictions.emplace_back(to_index, FUNC(eq), gateway.get_id());\n\n         auto preimage_size_index = member_index<htlc_create_operation>(\"preimage_size\");\n         authorize_htlc_create.restrictions.emplace_back(restriction(preimage_size_index, FUNC(ge), pre_image_size_512));\n         //[\n         //  {\n         //    \"member_index\": 2,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      7,\n         //      \"1.2.18\"\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 5,\n         //    \"restriction_type\": 5,\n         //    \"argument\": [\n         //      2,\n         //      512\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n         trx.clear();\n         trx.operations = {authorize_htlc_create};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the similar transactions\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to put a contract on the blockchain using Alice's funds\n         // with a preimage size of 256.\n         // This should fail because Bob is not authorized to create HTLC on behalf of Alice\n         // if the preimage size is below the minimum value restriction.\n         //////\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 3;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_256 );\n            create_operation.preimage_size = pre_image_size_256;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n            // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n         //////\n         // Bob attempts to put a contract on the blockchain using Alice's funds\n         // with a preimage size of 512.\n         // This should succeed because Bob is authorized to create HTLC on behalf of Alice\n         // and the preimage size equals the minimum value restriction.\n         //////\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 3;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_512 );\n            create_operation.preimage_size = pre_image_size_512;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, bob_private_key);\n            PUSH_TX( db, trx );\n\n         }\n\n\n         //////\n         // Bob attempts to put a contract on the blockchain using Alice's funds\n         // with a preimage size of 600.\n         // This should succeed because Bob is authorized to create HTLC on behalf of Alice\n         // and the preimage size is greater than the minimum value restriction.\n         //////\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 3;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_600 );\n            create_operation.preimage_size = pre_image_size_600;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, bob_private_key);\n            PUSH_TX( db, trx );\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of vector field size comparison\n    * Test of CAA for htlc_redeem_operation\n    *\n    * Scenario: Test of authorization of one account (gateway) authorizing another account (bob)\n    * to redeem an HTLC operation\n    */\n   BOOST_AUTO_TEST_CASE(authorized_htlc_redeem) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         time_point_sec LATER_HF_TIME\n                 = (HARDFORK_BSIP_40_TIME > HARDFORK_CORE_1468_TIME) ? HARDFORK_BSIP_40_TIME : HARDFORK_CORE_1468_TIME;\n         generate_blocks(LATER_HF_TIME);\n         generate_blocks(5);\n         set_expiration(db, trx);\n\n         // Initialize HTLC blockchain parameters\n         trx.clear();\n         set_htlc_committee_parameters();\n         generate_blocks(5);\n\n         // Initialize CAA blockchain parameters\n         trx.clear();\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n\n         // Update the expiration of the re-usable trx relative to the head block time\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(gateway));\n         int64_t init_balance(1000 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n         int64_t init_gateway_balance(50 * GRAPHENE_BLOCKCHAIN_PRECISION);\n         transfer( committee_account, gateway_id, graphene::chain::asset(init_gateway_balance) );\n\n\n         //////\n         // Initialize: Pre-image sizes and pre-images to reduce the test variability\n         //////\n         uint16_t pre_image_size_256 = 256;\n         std::vector<char> pre_image_256(pre_image_size_256);\n         generate_random_preimage(pre_image_size_256, pre_image_256);\n\n\n         //////\n         // Gateway puts a contract on the blockchain\n         // This should succeed because the gateway is authorized to create HTLC for its own account\n         //////\n         share_type htlc_amount = 25 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( htlc_amount );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 86400;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_256 );\n            create_operation.preimage_size = pre_image_size_256;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, alice_private_key);\n            PUSH_TX( db, trx );\n         }\n\n\n         //////\n         // Advance the blockchain to get the finalized HTLC ID\n         //////\n         generate_blocks(1);\n         graphene::chain::htlc_id_type alice_htlc_id =\n                 db.get_index_type<htlc_index>().indices().get<by_from_id>().find(alice.get_id())->id;\n\n\n         //////\n         // Bob attempts to redeem the HTLC on behalf of the gateway\n         // This should fail because Bob is not authorized to redeem on behalf of the gateway\n         //////\n         graphene::chain::htlc_redeem_operation redeem_operation;\n         {\n            redeem_operation.redeemer = gateway_id;\n            redeem_operation.htlc_id = alice_htlc_id;\n            redeem_operation.preimage = pre_image_256;\n            redeem_operation.fee = db.current_fee_schedule().calculate_fee( redeem_operation );\n            trx.clear();\n            trx.operations.push_back(redeem_operation);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Gateway authorizes Bob to redeem an HTLC\n         // only if the preimage length equals 200 bytes\n         // This length is incompatible with the HTLC pre-image that is already on the blockchain\n         //////\n         custom_authority_create_operation authorize_htlc_redeem;\n         authorize_htlc_redeem.account = gateway.get_id();\n         authorize_htlc_redeem.auth.add_authority(bob.get_id(), 1);\n         authorize_htlc_redeem.auth.weight_threshold = 1;\n         authorize_htlc_redeem.enabled = true;\n         authorize_htlc_redeem.valid_to = db.head_block_time() + 1000;\n         authorize_htlc_redeem.operation_type = operation::tag<htlc_redeem_operation>::value;\n\n         auto preimage_index = member_index<htlc_redeem_operation>(\"preimage\");\n         authorize_htlc_redeem.restrictions.emplace_back(preimage_index, FUNC(eq), int64_t(200));\n         //[\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 0,\n         //    \"argument\": [\n         //      2,\n         //      200\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n\n         trx.clear();\n         trx.operations = {authorize_htlc_redeem};\n         sign(trx, gateway_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to get the finalized CAA ID\n         //////\n         generate_blocks(1);\n         auto caa = db.get_index_type<custom_authority_index>().indices().get<by_account_custom>().find(gateway.get_id());\n         custom_authority_id_type caa_id = caa->id;\n\n\n         //////\n         // Bob attempts to redeem the HTLC\n         // This should fail because the authorization's restriction prohibits the redemption of this HTLC\n         //////\n         {\n            trx.clear();\n            trx.operations.push_back(redeem_operation);\n            sign(trx, bob_private_key);\n\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the similar transactions\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Gateway updates the authorization for to redeem an HTLC\n         // only if the preimage length equals 256 bytes\n         // This length is compatible with the HTLC pre-image that is already on the blockchain\n         //////\n         custom_authority_update_operation update_authorization;\n         update_authorization.account = gateway.get_id();\n         update_authorization.authority_to_update = caa_id;\n         uint16_t existing_restriction_index = 0; // The 0-based index of the first and only existing restriction\n         update_authorization.restrictions_to_remove = {existing_restriction_index};\n         update_authorization.restrictions_to_add =\n                 {restriction(preimage_index, FUNC(eq), int64_t(pre_image_size_256))};\n         trx.clear();\n         trx.operations = {update_authorization};\n         sign(trx, gateway_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Bob attempts to redeem the HTLC\n         // This should succeed because the redemption satisfies the authorization\n         //////\n         {\n            trx.clear();\n            trx.operations.push_back(redeem_operation);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of greater than (gt) and less than or equal to (le) restriction on a field\n    * Test of CAA for htlc_extend_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to extend an HTLC operation as long as the extension is within a specified duration\n    */\n   BOOST_AUTO_TEST_CASE(authorized_htlc_extension) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         time_point_sec LATER_HF_TIME\n                 = (HARDFORK_BSIP_40_TIME > HARDFORK_CORE_1468_TIME) ? HARDFORK_BSIP_40_TIME : HARDFORK_CORE_1468_TIME;\n         generate_blocks(LATER_HF_TIME);\n         generate_blocks(5);\n         set_expiration(db, trx);\n\n         // Initialize HTLC blockchain parameters\n         trx.clear();\n         set_htlc_committee_parameters();\n         generate_blocks(5);\n\n         // Initialize CAA blockchain parameters\n         trx.clear();\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(gateway));\n         int64_t init_balance(1000 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n         int64_t init_gateway_balance(50 * GRAPHENE_BLOCKCHAIN_PRECISION);\n         transfer( committee_account, gateway_id, graphene::chain::asset(init_gateway_balance) );\n\n\n         //////\n         // Initialize: Pre-image sizes and pre-images to reduce the test variability\n         //////\n         uint16_t pre_image_size_256 = 256;\n         std::vector<char> pre_image_256(pre_image_size_256);\n         generate_random_preimage(pre_image_size_256, pre_image_256);\n\n\n         //////\n         // Gateway puts a contract on the blockchain\n         // This should succeed because the gateway is authorized to create HTLC for its own account\n         //////\n         share_type htlc_amount = 25 * GRAPHENE_BLOCKCHAIN_PRECISION;\n         {\n            graphene::chain::htlc_create_operation create_operation;\n            create_operation.amount = graphene::chain::asset( htlc_amount );\n            create_operation.from = alice_id;\n            create_operation.to = gateway_id;\n            create_operation.claim_period_seconds = 86400;\n            create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image_256 );\n            create_operation.preimage_size = pre_image_size_256;\n            create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n            trx.clear();\n            trx.operations.push_back(create_operation);\n            sign(trx, alice_private_key);\n            PUSH_TX( db, trx );\n         }\n\n\n         //////\n         // Advance the blockchain to get the finalized HTLC ID\n         //////\n         generate_blocks(1);\n         graphene::chain::htlc_id_type alice_htlc_id =\n                 db.get_index_type<htlc_index>().indices().get<by_from_id>().find(alice.get_id())->id;\n\n\n         //////\n         // Bob attempts to extend the HTLC\n         // This should fail because Bob is not authorized to extend an HTLC on behalf of Alice\n         //////\n         graphene::chain::htlc_extend_operation extend_operation;\n         {\n            extend_operation.update_issuer = alice_id;\n            extend_operation.htlc_id = alice_htlc_id;\n            extend_operation.seconds_to_add = int64_t(24 * 3600);\n            extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation );\n            trx.clear();\n            trx.operations.push_back(extend_operation);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Alice authorizes Bob to extend an HTLC\n         // by greater than 1 hour and less than or equal to 24 hours\n         //////\n         custom_authority_create_operation authorize_htlc_extension;\n         authorize_htlc_extension.account = alice.get_id();\n         authorize_htlc_extension.auth.add_authority(bob.get_id(), 1);\n         authorize_htlc_extension.auth.weight_threshold = 1;\n         authorize_htlc_extension.enabled = true;\n         authorize_htlc_extension.valid_to = db.head_block_time() + 1000;\n         authorize_htlc_extension.operation_type = operation::tag<htlc_extend_operation>::value;\n\n         // Authorization to extend is restricted to greater than 1 hour and less than or equal to 24 hours\n         vector<restriction> restrictions;\n         auto extension_duration_index = member_index<htlc_extend_operation>(\"seconds_to_add\");\n         // Duration extension greater than one hour\n         restriction restriction_gt_duration = restriction(extension_duration_index, FUNC(gt), int64_t(1 * 3600));\n         restrictions.emplace_back(restriction_gt_duration);\n         // Duration extension less than or equal to 24 hours\n         restriction restriction_le_duration = restriction(extension_duration_index, FUNC(le), int64_t(24 * 3600));\n         restrictions.emplace_back(restriction_le_duration);\n         authorize_htlc_extension.restrictions = restrictions;\n         //[\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 4,\n         //    \"argument\": [\n         //      2,\n         //      3600\n         //    ],\n         //    \"extensions\": []\n         //  },\n         //  {\n         //    \"member_index\": 3,\n         //    \"restriction_type\": 3,\n         //    \"argument\": [\n         //      2,\n         //      86400\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n         trx.clear();\n         trx.operations = {authorize_htlc_extension};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the similar transactions\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to extend the HTLC\n         // This should succeed because Bob is conditionally authorized to extend\n         //////\n         {\n            trx.clear();\n            trx.operations.push_back(extend_operation);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n         }\n\n\n         //////\n         // Bob attempts to extend the HTLC by exactly 10 hours\n         // This should succeed because Bob is authorized to extend the HTLC\n         // if greater than 1 hour and less than or equal to 24 hours\n         //////\n         {\n            extend_operation = htlc_extend_operation();\n            extend_operation.update_issuer = alice_id;\n            extend_operation.htlc_id = alice_htlc_id;\n            extend_operation.seconds_to_add = int64_t(10 * 3600);\n            extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation );\n            trx.clear();\n            trx.operations.push_back(extend_operation);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n         }\n\n\n         //////\n         // Bob attempts to extend the HTLC by exactly 1 hour\n         // This should fail because Bob is authorized to extend the HTLC\n         // if greater than 1 hour and less than or equal to 24 hours\n         //////\n         {\n            extend_operation = htlc_extend_operation();\n            extend_operation.update_issuer = alice_id;\n            extend_operation.htlc_id = alice_htlc_id;\n            extend_operation.seconds_to_add = int64_t(1 * 3600);\n            extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation );\n            trx.clear();\n            trx.operations.push_back(extend_operation);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,0],[2,\"predicate_was_false\"]\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Bob attempts to extend the HTLC by 24 hours plus 1 second\n         // This should fail because Bob is authorized to extend the HTLC\n         // if greater than 1 hour and less than or equal to 24 hours\n         //////\n         {\n            extend_operation = htlc_extend_operation();\n            extend_operation.update_issuer = alice_id;\n            extend_operation.htlc_id = alice_htlc_id;\n            extend_operation.seconds_to_add = int64_t( (24 * 3600) + 1);\n            extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation );\n            trx.clear();\n            trx.operations.push_back(extend_operation);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,1],[2,\"predicate_was_false\"]\n            // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of variant assert (variant_assert) restriction on a field\n    * Test of CAA for vesting_balance_create_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to create a coins-day vesting balance with a vesting duration of 800,000 seconds\n    */\n   BOOST_AUTO_TEST_CASE(authorized_vesting_balance_create) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(charlie));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         //////\n         // Bob attempts to create a coins-day vesting balance for Alice\n         // This attempt should fail because Alice has not authorized Bob to create a vesting balance\n         //////\n         vesting_balance_create_operation original_vb_op;\n         time_point_sec policy_start_time = db.head_block_time() + 86400;\n         {\n            vesting_balance_create_operation vb_op;\n            vb_op.creator = alice_id;\n            vb_op.owner = charlie_id;\n            vb_op.amount = graphene::chain::asset(60000);\n            vb_op.policy = cdd_vesting_policy_initializer(800000, policy_start_time);\n            vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n            trx.clear();\n            trx.operations.push_back(vb_op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n            original_vb_op = vb_op;\n         }\n\n\n         //////\n         // Alice authorizes Bob to create a coins-day vesting balance from her funds\n         // only if the vesting duration equals 800,000 seconds\n         //////\n         custom_authority_create_operation authorize_create_vesting;\n         authorize_create_vesting.account = alice.get_id();\n         authorize_create_vesting.auth.add_authority(bob.get_id(), 1);\n         authorize_create_vesting.auth.weight_threshold = 1;\n         authorize_create_vesting.enabled = true;\n         authorize_create_vesting.valid_to = db.head_block_time() + 1000;\n         authorize_create_vesting.operation_type = operation::tag<vesting_balance_create_operation>::value;\n\n         // Restrict authorization to a coin-days vesting policy with a vesting duration of 800000 seconds\n         auto policy_index = member_index<vesting_balance_create_operation>(\"policy\");\n         int64_t policy_tag = vesting_policy_initializer::tag<cdd_vesting_policy_initializer>::value;\n         auto vesting_seconds_index = member_index<cdd_vesting_policy_initializer>(\"vesting_seconds\");\n         vector<restriction> policy_restrictions = {restriction(vesting_seconds_index, FUNC(eq), int64_t(800000))};\n         pair<int64_t, vector<restriction>> policy_argument(policy_tag, policy_restrictions);\n         authorize_create_vesting.restrictions = {restriction(policy_index, FUNC(variant_assert), policy_argument)};\n         //[\n         //  {\n         //    \"member_index\": 4,\n         //    \"restriction_type\": 12,\n         //    \"argument\": [\n         //      41,\n         //      [\n         //        1,\n         //        [\n         //          {\n         //            \"member_index\": 1,\n         //            \"restriction_type\": 0,\n         //            \"argument\": [\n         //              2,\n         //              800000\n         //            ],\n         //            \"extensions\": []\n         //          }\n         //        ]\n         //      ]\n         //    ],\n         //    \"extensions\": []\n         //  }\n         //]\n         trx.clear();\n         trx.operations = {authorize_create_vesting};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to generate distinctive hash IDs for the similar transactions\n         //////\n         generate_blocks(1);\n\n\n         //////\n         // Bob attempts to create a coins-day vesting balance for Alice with a vesting duration of 86400 seconds\n         // This attempt should fail because Alice has not authorized this duration\n         //////\n         {\n            vesting_balance_create_operation vb_op;\n            vb_op.creator = alice_id;\n            vb_op.owner = charlie_id;\n            vb_op.amount = graphene::chain::asset(60000);\n            vb_op.policy = cdd_vesting_policy_initializer(86400, policy_start_time);\n            vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n            trx.clear();\n            trx.operations.push_back(vb_op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,0],[0,0],[2,\"predicate_was_false\"]\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[0,0],[2,\\\"predicate_was_false\\\"]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Bob attempts to create a linear vesting balance for Alice\n         // This attempt should fail because Alice has not authorized this type of vesting balance creation\n         //////\n         {\n            vesting_balance_create_operation vb_op;\n            vb_op.creator = alice_id;\n            vb_op.owner = charlie_id;\n            vb_op.amount = graphene::chain::asset(60000);\n            linear_vesting_policy_initializer policy;\n            policy.begin_timestamp = policy_start_time;\n            policy.vesting_cliff_seconds = 800000;\n            policy.vesting_duration_seconds = 40000;\n            vb_op.policy = policy;\n            vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n            trx.clear();\n            trx.operations.push_back(vb_op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,0],[2,\"incorrect_variant_type\"]\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n            // [2,\"incorrect_variant_type\"]: 0 is the rejection_indicator for rejection_reason; \"incorrect_variant_type\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"incorrect_variant_type\\\"]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Bob attempts to create a coins-day vesting balance for Alice with a vesting duration of 800000 seconds\n         // This attempt should succeed because Alice has authorized authorized this type of vesting balance creation\n         // with this duration\n         //////\n         {\n            trx.clear();\n            trx.operations.push_back(original_vb_op);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of time restrictions on CAA\n    * Test of CAA for vesting_balance_withdraw_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to withdraw vesting for a limited duration\n    */\n   BOOST_AUTO_TEST_CASE(authorized_time_restrictions_1) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob)(charlie));\n         fund(charlie, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         //////\n         // Charlie creates an instant vesting balance for Alice\n         //////\n         vesting_balance_create_operation original_vb_op;\n         time_point_sec policy_start_time = db.head_block_time() + 86400;\n         vesting_balance_create_operation vb_op;\n         vb_op.creator = charlie_id;\n         vb_op.owner = alice_id;\n         vb_op.amount = graphene::chain::asset(60000);\n         vb_op.policy = instant_vesting_policy_initializer();\n         vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n         trx.clear();\n         trx.operations.push_back(vb_op);\n         sign(trx, charlie_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to before withdrawal of vesting balance can start\n         //////\n         generate_blocks(1);\n         set_expiration(db, trx);\n         vesting_balance_id_type vesting_balance_id =\n                 db.get_index_type<vesting_balance_index>().indices().get<by_account>().find(alice.get_id())->id;\n\n\n         //////\n         // Bob attempts to withdraw some of the vesting balance on behalf of Alice\n         // This attempt should fail because Alice has not authorized Bob\n         //////\n         {\n            asset partial_amount = asset(10000);\n\n            vesting_balance_withdraw_operation vb_op;\n            vb_op.vesting_balance = vesting_balance_id;\n            vb_op.owner = alice_id;\n            vb_op.amount = partial_amount;\n            vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n            trx.clear();\n            trx.operations.push_back(vb_op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n\n         //////\n         // Alice authorizes Bob to withdraw her vesting balance\n         //////\n         custom_authority_create_operation authorize_create_vesting;\n         authorize_create_vesting.account = alice.get_id();\n         authorize_create_vesting.auth.add_authority(bob.get_id(), 1);\n         authorize_create_vesting.auth.weight_threshold = 1;\n         authorize_create_vesting.enabled = true;\n         // Authorization is valid only for 3/5 of the maximum duration of a custom authority\n         time_point_sec authorization_end_time = policy_start_time + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 3 / 5);\n         time_point_sec authorization_before_end_time = policy_start_time + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 1 / 5);\n         authorize_create_vesting.valid_to = authorization_end_time;\n         authorize_create_vesting.operation_type = operation::tag<vesting_balance_withdraw_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_create_vesting};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to before the authorization expires\n         //////\n         generate_blocks(authorization_before_end_time);\n         set_expiration(db, trx);\n\n\n         //////\n         // Bob attempts to withdraw the available vesting balance for Alice\n         // This attempt should succeed because the authorization is active\n         //////\n         {\n            asset partial_amount = asset(10000);\n\n            vesting_balance_withdraw_operation vb_op;\n            vb_op.vesting_balance = vesting_balance_id;\n            vb_op.owner = alice_id;\n            vb_op.amount = partial_amount;\n            vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n            trx.clear();\n            trx.operations.push_back(vb_op);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n         }\n\n\n         //////\n         // Advance the blockchain to after the authorization expires\n         //////\n         time_point_sec after_authorization_end_time = authorization_end_time + 86400;\n         generate_blocks(after_authorization_end_time);\n         set_expiration(db, trx);\n\n\n         //////\n         // Bob attempts to withdraw the available vesting balance for Alice\n         // This attempt should fail because the authorization has expired\n         //////\n         {\n            asset partial_amount = asset(10000);\n\n            vesting_balance_withdraw_operation vb_op;\n            vb_op.vesting_balance = vesting_balance_id;\n            vb_op.owner = alice_id;\n            vb_op.amount = partial_amount;\n            vb_op.fee = db.current_fee_schedule().calculate_fee(vb_op);\n            trx.clear();\n            trx.operations.push_back(vb_op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of time restrictions on CAA\n    * Test of CAA for call_order_update_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to update a call order only during a specfied time interval\n    */\n   BOOST_AUTO_TEST_CASE(authorized_time_restrictions_2) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((feedproducer)(alice)(bob));\n         int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n\n         //////\n         // Initialize: Define a market-issued asset called USDBIT\n         //////\n         // Define core asset\n         const auto &core = asset_id_type()(db);\n         asset_id_type core_id = core.id;\n\n         // Create a smart asset\n         create_bitasset(\"USDBIT\", feedproducer_id);\n         generate_blocks(1);\n         const asset_object &bitusd\n                 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"USDBIT\");\n         asset_id_type usd_id = bitusd.id;\n\n         // Configure the smart asset\n         update_feed_producers(bitusd, {feedproducer.id});\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(5);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         //////\n         // Fund alice with core asset\n         //////\n         fund(alice, asset(init_balance));\n         // alice will borrow 1000 bitUSD\n         borrow(alice, bitusd.amount(1000), asset(15000));\n         int64_t alice_balance_usd_before_offer = get_balance(alice_id, usd_id);\n         BOOST_CHECK_EQUAL( 1000,  alice_balance_usd_before_offer);\n         int64_t alice_balance_core_before_offer = get_balance(alice_id, core_id);\n         BOOST_CHECK_EQUAL( init_balance - 15000, alice_balance_core_before_offer );\n\n\n         //////\n         // Alice updates the collateral for the Alice debt position\n         //////\n         {\n            call_order_update_operation op;\n            op.funding_account = alice_id;\n            op.delta_collateral = asset(1000);\n            op.delta_debt = asset(0, usd_id);\n            trx.clear();\n            trx.operations.push_back(op);\n            sign(trx, alice_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This attempt should fail because Bob is not authorized by Alice\n         //////\n         {\n            call_order_update_operation op;\n            op.funding_account = alice_id;\n            op.delta_collateral = asset(2000);\n            op.delta_debt = asset(0, usd_id);\n            trx.clear();\n            trx.operations.push_back(op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // Alice authorizes Bob to update her call order\n         //////\n         custom_authority_create_operation authorize_call_order_update;\n         authorize_call_order_update.account = alice.get_id();\n         authorize_call_order_update.auth.add_authority(bob.get_id(), 1);\n         authorize_call_order_update.auth.weight_threshold = 1;\n         authorize_call_order_update.enabled = true;\n         // Authorization is valid only for 2/5 of the maximum duration of a custom authority\n         time_point_sec before_authorization_start_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 1 / 5);\n         time_point_sec authorization_start_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 2 / 5);\n         time_point_sec authorization_middle_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 3 / 5);\n         time_point_sec authorization_end_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 4 / 5);\n         time_point_sec after_authorization_end_time = authorization_end_time + 86400;\n         authorize_call_order_update.valid_from = authorization_start_time;\n         authorize_call_order_update.valid_to = authorization_end_time;\n         authorize_call_order_update.operation_type = operation::tag<call_order_update_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_call_order_update};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to before the authorization starts\n         //////\n         generate_blocks(before_authorization_start_time);\n         set_expiration(db, trx);\n         publish_feed(bitusd, feedproducer, current_feed); // Update the price feed\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This attempt should fail because authorization is not yet active\n         //////\n         {\n            call_order_update_operation op;\n            op.funding_account = alice_id;\n            op.delta_collateral = asset(3000);\n            op.delta_debt = asset(0, usd_id);\n            trx.clear();\n            trx.operations.push_back(op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because the CAA is not yet active\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // Advance the blockchain to the start of the authorization period\n         //////\n         generate_blocks(authorization_start_time);\n         set_expiration(db, trx);\n         publish_feed(bitusd, feedproducer, current_feed); // Update the price feed\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This attempt should succeed because the Alice authorization is active\n         //////\n         {\n            call_order_update_operation op;\n            op.funding_account = alice_id;\n            op.delta_collateral = asset(4000);\n            op.delta_debt = asset(0, usd_id);\n            trx.clear();\n            trx.operations.push_back(op);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Advance the blockchain to the end of the authorization period\n         //////\n         generate_blocks(authorization_middle_time);\n         set_expiration(db, trx);\n         publish_feed(bitusd, feedproducer, current_feed); // Update the price feed\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This attempt should succeed because the Alice authorization is active\n         //////\n         {\n            call_order_update_operation op;\n            op.funding_account = alice_id;\n            op.delta_collateral = asset(5000);\n            op.delta_debt = asset(0, usd_id);\n            trx.clear();\n            trx.operations.push_back(op);\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Advance the blockchain to after the authorization expires\n         //////\n         generate_blocks(after_authorization_end_time);\n         set_expiration(db, trx);\n         publish_feed(bitusd, feedproducer, current_feed); // Update the price feed\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This attempt should fail because the authorization has expired\n         //////\n         {\n            call_order_update_operation op;\n            op.funding_account = alice_id;\n            op.delta_collateral = asset(6000);\n            op.delta_debt = asset(0, usd_id);\n            trx.clear();\n            trx.operations.push_back(op);\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of time restrictions on CAA\n    * Test of CAA for asset_reserve_operation\n    * Test of CAA in a proposed operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to reserve (burn) an asset only during a specfied timespan\n    */\n   BOOST_AUTO_TEST_CASE(authorized_time_restrictions_3) {\n      try {\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((assetissuer)(feedproducer)(alice)(bob)(charlie));\n         int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n         fund(alice, asset(init_balance));\n\n\n         // Lambda for issuing an asset to an account\n         auto issue_amount_to = [&](const account_id_type &issuer, const asset &amount, const account_id_type &to) {\n            asset_issue_operation op;\n            op.issuer = issuer;\n            op.asset_to_issue = amount;\n            op.issue_to_account = to;\n\n            return op;\n         };\n\n         // Lambda for reserving an asset from an account\n         auto reserve_asset = [&](const account_id_type &reserver, const asset &amount) {\n            asset_reserve_operation op;\n            op.payer = reserver;\n            op.amount_to_reserve = amount;\n\n            return op;\n         };\n\n\n         //////\n         // Initialize: Create user-issued assets\n         //////\n         upgrade_to_lifetime_member(assetissuer);\n         create_user_issued_asset(\"SPECIALCOIN\", assetissuer,  DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n         generate_blocks(1);\n         const asset_object &specialcoin\n                 = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(\"SPECIALCOIN\");\n\n\n         //////\n         // Initialize: assetissuer issues SPECIALCOIN to different accounts\n         //////\n         asset_issue_operation issue_special_to_alice_op\n                 = issue_amount_to(assetissuer.get_id(), asset(1000, specialcoin.id), alice.get_id());\n         asset_issue_operation issue_special_to_charlie_op\n                 = issue_amount_to(assetissuer.get_id(), asset(2000, specialcoin.id), charlie.get_id());\n         trx.clear();\n         trx.operations = {issue_special_to_alice_op, issue_special_to_charlie_op};\n         sign(trx, assetissuer_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Alice reserves some SPECIALCOIN from her account\n         //////\n         asset_reserve_operation reserve_op = reserve_asset(alice.get_id(), asset(200, specialcoin.id));\n         trx.clear();\n         trx.operations = {reserve_op};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n         int64_t allowed1_balance_specialcoin_after_override1 = get_balance(alice.get_id(), specialcoin.id);\n         BOOST_CHECK_EQUAL(allowed1_balance_specialcoin_after_override1, 800);\n\n\n         //////\n         // Charlie reserves some SPECIALCOIN from his account\n         //////\n         reserve_op = reserve_asset(charlie.get_id(), asset(200, specialcoin.id));\n         trx.clear();\n         trx.operations = {reserve_op};\n         sign(trx, charlie_private_key);\n         PUSH_TX(db, trx);\n         int64_t charlie_balance_specialcoin_after_override1 = get_balance(charlie.get_id(), specialcoin.id);\n         BOOST_CHECK_EQUAL(charlie_balance_specialcoin_after_override1, 1800);\n\n\n         //////\n         // Alice authorizes Bob to reserve her SPECIALCOIN\n         // This attempt should fail because the blockchain has not yet been initialized for CAA\n         //////\n         custom_authority_create_operation authorize_reserve;\n         authorize_reserve.account = alice.get_id();\n         authorize_reserve.auth.add_authority(bob.get_id(), 1);\n         authorize_reserve.auth.weight_threshold = 1;\n         authorize_reserve.enabled = true;\n         // Authorization is valid only for 2/5 of the maximum duration of a custom authority\n         time_point_sec before_authorization_start_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 1 / 5);\n         time_point_sec authorization_start_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 2 / 5);\n         time_point_sec authorization_middle_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 3 / 5);\n         time_point_sec authorization_end_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 4 / 5);\n         time_point_sec after_authorization_end_time = authorization_end_time + 86400;\n         authorize_reserve.valid_from = authorization_start_time;\n         authorize_reserve.valid_to = authorization_end_time;\n         authorize_reserve.operation_type = operation::tag<asset_reserve_operation>::value;\n         trx.clear();\n         trx.operations = {authorize_reserve};\n         sign(trx, alice_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::assert_exception);\n\n\n         //////\n         // Alice creates a PROPOSAL to authorize Bob to reserve her SPECIALCOIN\n         // This attempt should fail because the blockchain has not yet been initialized for CAA\n         //////\n         proposal_create_operation proposal;\n         proposal.fee_paying_account = alice.get_id();\n         proposal.proposed_ops = {op_wrapper(authorize_reserve)};\n         proposal.expiration_time = db.head_block_time() + 86400;\n         trx.clear();\n         trx.operations = {authorize_reserve};\n         sign(trx, alice_private_key);\n         BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::assert_exception);\n\n\n         //////\n         // Initialize the blockchain for CAA\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Alice creates a PROPOSAL to authorize Bob to reserve her SPECIALCOIN\n         // Authorization is valid only for 2/5 of the maximum duration of a custom authority\n         // This attempt should succeed because the blockchain is initialized for CAA\n         //////\n         before_authorization_start_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 1 / 5);\n         authorization_start_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 2 / 5);\n         authorization_middle_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 3 / 5);\n         authorization_end_time =\n                 db.head_block_time() + (GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS * 4 / 5);\n         after_authorization_end_time = authorization_end_time + 86400;\n         authorize_reserve.valid_from = authorization_start_time;\n         authorize_reserve.valid_to = authorization_end_time;\n\n         proposal.fee_paying_account = alice.get_id();\n         proposal.proposed_ops = {op_wrapper(authorize_reserve)};\n         proposal.expiration_time = db.head_block_time() + 86400;\n         trx.clear();\n         trx.operations = {proposal};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to get the finalized proposal ID\n         //////\n         generate_blocks(1);\n         const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();\n         proposal_id_type proposal_id = prop.id;\n\n         // Alice approves the proposal\n         proposal_update_operation approve_proposal;\n         approve_proposal.proposal = proposal_id;\n         approve_proposal.fee_paying_account = alice.get_id();\n         approve_proposal.active_approvals_to_add = {alice.get_id()};\n         trx.clear();\n         trx.operations = {approve_proposal};\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx);\n\n\n         //////\n         // Advance the blockchain to before the authorization starts\n         //////\n         generate_blocks(before_authorization_start_time);\n         set_expiration(db, trx);\n\n\n         //////\n         // Bob attempts to reserve some of Alice's SPECIALCOIN\n         // This attempt should fail because Bob the Alice authorization is not yet active\n         //////\n         {\n            asset_reserve_operation reserve_op = reserve_asset(alice.get_id(), asset(200, specialcoin.id));\n            trx.clear();\n            trx.operations = {reserve_op};\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because the CAA is not yet active\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // Advance the blockchain to the start of the authorization period\n         //////\n         generate_blocks(authorization_start_time);\n         set_expiration(db, trx);\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This should succeed because the authorization is active\n         //////\n         {\n            asset_reserve_operation reserve_op = reserve_asset(alice.get_id(), asset(200, specialcoin.id));\n            trx.clear();\n            trx.operations = {reserve_op};\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Advance the blockchain to the end of the authorization period\n         //////\n         generate_blocks(authorization_middle_time);\n         set_expiration(db, trx);\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This should succeed because the authorization is active\n         //////\n         {\n            asset_reserve_operation reserve_op = reserve_asset(alice.get_id(), asset(200, specialcoin.id));\n            trx.clear();\n            trx.operations = {reserve_op};\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Advance the blockchain to after the authorization expires\n         //////\n         generate_blocks(after_authorization_end_time);\n         set_expiration(db, trx);\n\n\n         //////\n         // Bob attempts to update the collateral for Alice's debt position\n         // This should fail because Bob the authorization has expired\n         //////\n         {\n            asset_reserve_operation reserve_op = reserve_asset(alice.get_id(), asset(200, specialcoin.id));\n            trx.clear();\n            trx.operations = {reserve_op};\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of string field restriction\n    * Test of CAA for asset_create_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing another account (bob)\n    * to create an asset with a description that starts with the literal string \"ACOIN.\"\n    */\n   BOOST_AUTO_TEST_CASE(authorized_asset_creation) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice)(bob));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n         upgrade_to_lifetime_member(alice);\n         fund(bob, asset(200000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n\n         // Lambda for issuing an asset to an account\n         auto create_uia = [&](const string& name,\n                                    const account_object& issuer,\n                                    uint16_t flags,\n                                    const additional_asset_options_t& options = additional_asset_options_t(),\n                                    const price& core_exchange_rate = price(asset(1, asset_id_type(1)), asset(1)),\n                                    uint8_t precision = 2 /* traditional precision for tests */,\n                                    uint16_t market_fee_percent = 0) {\n\n            asset_create_operation op;\n\n            op.issuer = issuer.id;\n            op.fee = asset();\n            op.symbol = name;\n            op.common_options.max_supply = 0;\n            op.precision = precision;\n            op.common_options.core_exchange_rate = core_exchange_rate;\n            op.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n            op.common_options.flags = flags;\n            op.common_options.issuer_permissions = flags;\n            op.common_options.market_fee_percent = market_fee_percent;\n            op.common_options.extensions = std::move(options);\n\n            return op;\n         };\n\n\n         //////\n         // Alice creates a UIA\n         //////\n         {\n            asset_create_operation create_uia_op = create_uia(\"ACOIN\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, alice_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Bob attempts to create a UIA\n         // This should fail because Bob is not authorized by Alice to create any coin with Alice as the issuer\n         //////\n         {\n            asset_create_operation create_uia_op = create_uia(\"ACOIN.BOB\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // Alice authorizes Bob to create sub-token UIAs below ACOIN\n         //////\n         {\n            custom_authority_create_operation authorize_uia_creation;\n            authorize_uia_creation.account = alice.get_id();\n            authorize_uia_creation.auth.add_authority(bob.get_id(), 1);\n            authorize_uia_creation.auth.weight_threshold = 1;\n            authorize_uia_creation.enabled = true;\n            authorize_uia_creation.valid_to = db.head_block_time() + 86400;\n            authorize_uia_creation.operation_type = operation::tag<asset_create_operation>::value;\n\n            auto symbol_index = member_index<asset_create_operation>(\"symbol\");\n            authorize_uia_creation.restrictions.emplace_back(symbol_index, FUNC(gt), string(\"ACOIN.\"));\n            authorize_uia_creation.restrictions.emplace_back(symbol_index, FUNC(le), string(\"ACOIN.ZZZZZZZZZZZZZZZZ\"));\n            //[\n            //  {\n            //    \"member_index\": 2,\n            //    \"restriction_type\": 4,\n            //    \"argument\": [\n            //      3,\n            //      \"ACOIN.\"\n            //    ],\n            //    \"extensions\": []\n            //  },\n            //  {\n            //    \"member_index\": 2,\n            //    \"restriction_type\": 3,\n            //    \"argument\": [\n            //      3,\n            //      \"ACOIN.ZZZZZZZZZZZZZZZZ\"\n            //    ],\n            //    \"extensions\": []\n            //  }\n            //]\n\n            trx.clear();\n            trx.operations = {authorize_uia_creation};\n            sign(trx, alice_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Bob attempts to create a UIA with a symbol name below the authorized textual range\n         // This should fail because it violates Restriction 1\n         //////\n         {\n            asset_create_operation create_uia_op = create_uia(\"ABCOIN\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // Bob attempts to create a UIA with a symobl name above the authorized textual range\n         // This should fail because it violates Restriction 2\n         //////\n         {\n            asset_create_operation create_uia_op = create_uia(\"BOB\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // [0,1]: 0 is the rejection_indicator for an index to a sub-restriction; 1 is the index value for Restriction 2\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // Bob attempts to create a sub-token of ACOIN\n         // This should succeed because this satisfies the sub-token restriction by Alice\n         //////\n         {\n            asset_create_operation create_uia_op = create_uia(\"ACOIN.BOB\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n            create_uia_op = create_uia(\"ACOIN.CHARLIE\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n\n            create_uia_op = create_uia(\"ACOIN.DIANA\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Bob creates his own UIA that is similar to ACOIN\n         //////\n         {\n            upgrade_to_lifetime_member(bob);\n\n            asset_create_operation create_uia_op = create_uia(\"AACOIN\", bob, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n\n            PUSH_TX(db, trx);\n\n\n            create_uia_op = create_uia(\"AACOIN.TEST\", bob, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // Bob attempts to create a sub-token of AACOIN but with Alice as the issuer\n         // This should fail because it violates Restriction 1\n         //////\n         {\n            asset_create_operation create_uia_op = create_uia(\"AACOIN.BOB\", alice, white_list);\n            trx.clear();\n            trx.operations = {create_uia_op};\n            sign(trx, bob_private_key);\n\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for Restriction 1\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n   /**\n    * Test of CAA for account_update_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing a key\n    * to ONLY update the voting slate of an account\n    */\n   BOOST_AUTO_TEST_CASE(authorized_voting_key) {\n      try {\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         ACTORS((alice));\n         fund(alice, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n         upgrade_to_lifetime_member(alice);\n\n         // Arbitrarily identify one of the active witnesses\n         flat_set<witness_id_type> witnesses = db.get_global_properties().active_witnesses;\n         auto itr_witnesses = witnesses.begin();\n         witness_id_type witness0_id = itr_witnesses[0];\n         const auto& idx = db.get_index_type<witness_index>().indices().get<by_id>();\n         witness_object witness0_obj = *idx.find(witness0_id);\n\n\n         //////\n         // Define a key that can be authorized\n         // This can be a new key or an existing key. The existing key may even be the active key of an account.\n         //////\n         fc::ecc::private_key some_private_key = generate_private_key(\"some key\");\n         public_key_type some_public_key = public_key_type(some_private_key.get_public_key());\n\n\n         //////\n         // The key attempts to update the voting slate of Alice\n         // This should fail because the key is not authorized by Alice to update any part of her account\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n            account_options alice_options = alice.options;\n            auto insert_result = alice_options.votes.insert(witness0_obj.vote_id);\n            if (!insert_result.second)\n               FC_THROW(\"Account ${account} was already voting for witness ${witness}\",\n                        (\"account\", alice)(\"witness\", \"init0\"));\n            uop.new_options = alice_options;\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for Bob's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] { PUSH_TX(db, trx); });\n         }\n\n\n         //////\n         // Alice authorizes the key to update her voting slate\n         // by authorizing account updates EXCEPT for\n         // updating the owner key\n         // updating the active key\n         // updating the memo key\n         // updating the special owner authority\n         // updating the special active authority\n         //////\n         {\n            custom_authority_create_operation authorize_account_update;\n            authorize_account_update.account = alice.get_id();\n            authorize_account_update.auth.add_authority(some_public_key, 1);\n            authorize_account_update.auth.weight_threshold = 1;\n            authorize_account_update.enabled = true;\n            authorize_account_update.valid_to = db.head_block_time() + 86400;\n            authorize_account_update.operation_type = operation::tag<account_update_operation>::value;\n\n            // Shall not update the owner key member\n            auto owner_index = member_index<account_update_operation>(\"owner\");\n            restriction no_owner = restriction(owner_index, FUNC(eq), void_t());\n\n            // Shall not update the active key member\n            auto active_index = member_index<account_update_operation>(\"active\");\n            restriction no_active = restriction(active_index, FUNC(eq), void_t());\n\n            // Shall not update the memo key member of the new_options member\n            auto new_options_index = member_index<account_update_operation>(\"new_options\");\n            auto memo_index = member_index<account_options>(\"memo_key\");\n            restriction same_memo = restriction(new_options_index, FUNC(attr),\n                                                vector<restriction>{\n                                                        restriction(memo_index, FUNC(eq), alice.options.memo_key)});\n\n            // Shall not update the extensions member\n            auto ext_index = member_index<account_update_operation>(\"extensions\");\n            restriction no_ext = restriction(ext_index, FUNC(eq), void_t());\n\n            auto owner_special_index = member_index<account_update_operation::ext>(\"owner_special_authority\");\n            restriction no_special_owner = restriction(ext_index, FUNC(attr),\n                                                       vector<restriction>{\n                                                               restriction(owner_special_index, FUNC(eq), void_t())});\n\n            auto active_special_index = member_index<account_update_operation::ext>(\"active_special_authority\");\n            restriction no_special_active = restriction(ext_index, FUNC(attr),\n                                                        vector<restriction>{\n                                                                restriction(active_special_index, FUNC(eq), void_t())});\n\n            // Shall not update the extensions member of the new_options member\n            auto new_options_ext_index = member_index<account_options>(\"extensions\");\n            restriction no_new_options_ext = restriction(new_options_index, FUNC(attr), vector<restriction>{\n                    restriction(new_options_ext_index, FUNC(eq), void_t())});\n\n            // Combine all of the shall not restrictions\n            vector<restriction> shall_not_restrictions = {no_owner, no_active, no_special_owner, no_special_active,\n                                                          same_memo};\n            authorize_account_update.restrictions = shall_not_restrictions;\n            //[\n            //  {\n            //    \"member_index\": 2,\n            //    \"restriction_type\": 0,\n            //    \"argument\": [\n            //      0,\n            //      {}\n            //    ],\n            //    \"extensions\": []\n            //  },\n            //  {\n            //    \"member_index\": 3,\n            //    \"restriction_type\": 0,\n            //    \"argument\": [\n            //      0,\n            //      {}\n            //    ],\n            //    \"extensions\": []\n            //  },\n            //  {\n            //    \"member_index\": 5,\n            //    \"restriction_type\": 10,\n            //    \"argument\": [\n            //      39,\n            //      [\n            //        {\n            //          \"member_index\": 1,\n            //          \"restriction_type\": 0,\n            //          \"argument\": [\n            //            0,\n            //            {}\n            //          ],\n            //          \"extensions\": []\n            //        }\n            //      ]\n            //    ],\n            //    \"extensions\": []\n            //  },\n            //  {\n            //    \"member_index\": 5,\n            //    \"restriction_type\": 10,\n            //    \"argument\": [\n            //      39,\n            //      [\n            //        {\n            //          \"member_index\": 2,\n            //          \"restriction_type\": 0,\n            //          \"argument\": [\n            //            0,\n            //            {}\n            //          ],\n            //          \"extensions\": []\n            //        }\n            //      ]\n            //    ],\n            //    \"extensions\": []\n            //  },\n            //  {\n            //    \"member_index\": 4,\n            //    \"restriction_type\": 10,\n            //    \"argument\": [\n            //      39,\n            //      [\n            //        {\n            //          \"member_index\": 0,\n            //          \"restriction_type\": 0,\n            //          \"argument\": [\n            //            5,\n            //            \"BTS7zsqi7QUAjTAdyynd6DVe8uv4K8gCTRHnAoMN9w9CA1xLCTDVv\"\n            //          ],\n            //          \"extensions\": []\n            //        }\n            //      ]\n            //    ],\n            //    \"extensions\": []\n            //  }\n            //]\n\n            // Broadcast the transaction\n            trx.clear();\n            trx.operations = {authorize_account_update};\n            sign(trx, alice_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // The key attempts to update the owner key for alice\n         // This should fail because it is NOT authorized by alice\n         // It violates Restriction 1 (index-0)\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n\n            uop.owner = authority(1, some_public_key, 1);\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_owner_auth);\n            // The failure should indicate the rejection path\n            // {\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // The key attempts to update the active key for alice\n         // This should fail because it is NOT authorized by alice\n         // It violates Restriction 2 (index-1)\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n\n            uop.active = authority(1, some_public_key, 1);\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // {\"success\":false,\"rejection_path\":[[0,1],[2,\"predicate_was_false\"]]}\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,1],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // The key attempts to update the special owner key for alice\n         // This should fail because it is NOT authorized by alice\n         // It violates Restriction 3 (index-2)\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n\n            uop.extensions.value.owner_special_authority = no_special_authority();\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_owner_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,2],[0,0],[2,\"predicate_was_false\"]\n            // [0,2]: 0 is the rejection_indicator for an index to a sub-restriction; 2 is the index value for Restriction 3\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,2],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // The key attempts to update the special active key for alice\n         // This should fail because it is NOT authorized by alice\n         // It violates Restriction 4 (index-3)\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n\n            uop.extensions.value.active_special_authority = no_special_authority();\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,3],[0,0],[2,\"predicate_was_false\"]\n            // [0,3]: 0 is the rejection_indicator for an index to a sub-restriction; 3 is the index value for Restriction 4\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,3],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // The key attempts to update the memo key for alice\n         // This should fail because it is NOT authorized by alice\n         // It violates Restriction 5 (index-4)\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n\n            account_options alice_options = alice.options;\n            alice_options.memo_key = some_public_key;\n            uop.new_options = alice_options;\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // \"rejection_path\":[[0,4],[0,0],[2,\"predicate_was_false\"]\n            // [0,4]: 0 is the rejection_indicator for an index to a sub-restriction; 4 is the index value for Restriction 5\n            // [0,0]: 0 is the rejection_indicator for an index to a sub-restriction; 0 is the index value for the only argument\n            // [2,\"predicate_was_false\"]: 0 is the rejection_indicator for rejection_reason; \"predicate_was_false\" is the reason\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,4],[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // The key attempts to update the voting slate for alice\n         // This should succeed because the key is authorized by alice\n         //////\n         {\n            account_update_operation uop;\n            uop.account = alice.get_id();\n            account_options alice_options = alice.options;\n            auto insert_result = alice_options.votes.insert(witness0_obj.vote_id);\n            if (!insert_result.second)\n               FC_THROW(\"Account ${account} was already voting for witness ${witness}\",\n                        (\"account\", alice)(\"witness\", \"init0\"));\n            uop.new_options = alice_options;\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(uop));\n            sign(trx, some_private_key);\n            PUSH_TX(db, trx);\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of CAA for witness_update_operation\n    *\n    * Scenario: Test of authorization of one account (alice) authorizing a key\n    * to ONLY change the signing key of a witness account\n    */\n   BOOST_AUTO_TEST_CASE(authorized_change_witness_signing_key) {\n      try {\n\n         //////\n         // Initialize the blockchain\n         //////\n         generate_blocks(HARDFORK_BSIP_40_TIME);\n         generate_blocks(5);\n         db.modify(global_property_id_type()(db), [](global_property_object &gpo) {\n            gpo.parameters.extensions.value.custom_authority_options = custom_authority_options_type();\n         });\n         set_expiration(db, trx);\n\n\n         //////\n         // Initialize: Accounts\n         //////\n         // Create a new witness account (witness0)\n         ACTORS((witness0));\n         // Upgrade witness account to LTM\n         upgrade_to_lifetime_member(witness0.id);\n         generate_block();\n\n         // Create the witnesses\n         // Get the witness0 identifier after a block has been generated\n         // to be sure of using the most up-to-date identifier for the account\n         const account_id_type witness0_identifier = get_account(\"witness0\").id;\n         create_witness(witness0_identifier, witness0_private_key);\n\n         generate_block();\n\n         // Find the witness ID for witness0\n         const auto& idx = db.get_index_type<witness_index>().indices().get<by_account>();\n         witness_object witness0_obj = *idx.find(witness0_identifier);\n         BOOST_CHECK(witness0_obj.witness_account == witness0_identifier);\n\n\n         //////\n         // Define a key that can be authorized\n         // This can be a new key or an existing key. The existing key may even be the active key of an account.\n         //////\n         fc::ecc::private_key some_private_key = generate_private_key(\"some key\");\n         public_key_type some_public_key = public_key_type(some_private_key.get_public_key());\n\n\n         //////\n         // Define an alternate witness signing key\n         //////\n         fc::ecc::private_key alternate_signing_private_key = generate_private_key(\"some signing key\");\n         public_key_type alternate_signing_public_key = public_key_type(alternate_signing_private_key.get_public_key());\n         // The current signing key should be different than the alternate signing public key\n         BOOST_CHECK(witness0_obj.signing_key != alternate_signing_public_key);\n\n\n         //////\n         // The key attempts to update the signing key of witness0\n         // This should fail because the key is NOT authorized by witness0 to update the signing key\n         //////\n         {\n            witness_update_operation wop;\n            wop.witness = witness0_obj.id;\n            wop.witness_account = witness0_obj.witness_account;\n\n            wop.new_signing_key = alternate_signing_public_key;\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(wop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should not indicate any rejected custom auths because no CAA applies for the key's attempt\n            // \"rejected_custom_auths\":[]\n            EXPECT_EXCEPTION_STRING(\"\\\"rejected_custom_auths\\\":[]\", [&] { PUSH_TX(db, trx); });\n         }\n\n\n         //////\n         // Alice authorizes the key to only update the witness signing key\n         //////\n         {\n            custom_authority_create_operation authorize_update_signing_key;\n            authorize_update_signing_key.account = witness0.get_id();\n            authorize_update_signing_key.auth.add_authority(some_public_key, 1);\n            authorize_update_signing_key.auth.weight_threshold = 1;\n            authorize_update_signing_key.enabled = true;\n            authorize_update_signing_key.valid_to = db.head_block_time() + 86400;\n            authorize_update_signing_key.operation_type = operation::tag<witness_update_operation>::value;\n            auto url_index = member_index<witness_update_operation>(\"new_url\");\n            restriction no_url = restriction(url_index, FUNC(eq), void_t());\n            authorize_update_signing_key.restrictions = {no_url};\n            //[\n            //  {\n            //    \"member_index\": 3,\n            //    \"restriction_type\": 0,\n            //    \"argument\": [\n            //      0,\n            //      {}\n            //    ]\n            //  }\n            //]\n\n            // Broadcast the transaction\n            trx.clear();\n            trx.operations = {authorize_update_signing_key};\n            sign(trx, witness0_private_key);\n            PUSH_TX(db, trx);\n         }\n\n\n         //////\n         // The key attempts to update the URL of witness0\n         // This should fail because the key is NOT authorized by witness0 to update the URL\n         //////\n         {\n            witness_update_operation wop;\n            wop.witness = witness0_obj.id;\n            wop.witness_account = witness0_obj.witness_account;\n\n            wop.new_url = \"NEW_URL\";\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(wop));\n            sign(trx, some_private_key);\n            BOOST_CHECK_THROW(PUSH_TX(db, trx), tx_missing_active_auth);\n            // The failure should indicate the rejection path\n            // {\"success\":false,\"rejection_path\":[[0,0],[2,\"predicate_was_false\"]]}\n            EXPECT_EXCEPTION_STRING(\"\\\"rejection_path\\\":[[0,0],[2,\\\"predicate_was_false\\\"]]\", [&] {PUSH_TX(db, trx);});\n         }\n\n\n         //////\n         // The key attempts to update the signing key of witness0\n         // This should succeed because the key is authorized by witness0 to update the signing key\n         //////\n         {\n            witness_update_operation wop;\n            wop.witness = witness0_obj.id;\n            wop.witness_account = witness0_obj.witness_account;\n\n            wop.new_signing_key = alternate_signing_public_key;\n\n            trx.clear();\n            trx.operations.emplace_back(std::move(wop));\n            sign(trx, some_private_key);\n            PUSH_TX(db, trx);\n\n            // Check the current signing key for witness0\n            witness_object updated_witness0_obj = *idx.find(witness0_obj.witness_account);\n            BOOST_CHECK(updated_witness0_obj.witness_account == witness0_obj.witness_account);\n            BOOST_CHECK(updated_witness0_obj.signing_key == alternate_signing_public_key);\n         }\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/custom_operations.cpp",
    "content": "/*\n * Copyright (c) 2019 oxarbitrage and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/api.hpp>\n#include <graphene/utilities/tempdir.hpp>\n#include <fc/crypto/digest.hpp>\n\n#include <graphene/custom_operations/custom_operations_plugin.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n#define BOOST_TEST_MODULE Custom operations plugin tests\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::app;\nusing namespace graphene::custom_operations;\n\nBOOST_FIXTURE_TEST_SUITE( custom_operation_tests, database_fixture )\n\nvoid map_operation(flat_map<string, optional<string>>& pairs, bool remove, string& catalog, account_id_type& account,\n      private_key& pk, database& db)\n{\n   signed_transaction trx;\n   set_expiration(db, trx);\n\n   custom_operation op;\n   account_storage_map store;\n\n   store.key_values = pairs;\n   store.remove = remove;\n   store.catalog = catalog;\n\n   auto packed = fc::raw::pack(store);\n   packed.insert(packed.begin(), types::account_map);\n\n   op.payer = account;\n   op.data = packed;\n   op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op);\n   trx.operations.push_back(op);\n   trx.sign(pk, db.get_chain_id());\n   PUSH_TX(db, trx, ~0);\n   trx.clear();\n}\n\nBOOST_AUTO_TEST_CASE(custom_operations_account_storage_map_test)\n{\ntry {\n   ACTORS((nathan)(alice)(robert)(patty));\n\n   app.enable_plugin(\"custom_operations\");\n   custom_operations_api custom_operations_api(app);\n\n   generate_block();\n   enable_fees();\n\n   int64_t init_balance(10000 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   transfer(committee_account, nathan_id, asset(init_balance));\n   transfer(committee_account, alice_id, asset(init_balance));\n\n   // catalog is indexed so cant be too big(greater than CUSTOM_OPERATIONS_MAX_KEY_SIZE(200) is not allowed)\n   std::string catalog(201, 'a');\n   flat_map<string, optional<string>> pairs;\n   pairs[\"key\"] = fc::json::to_string(\"value\");\n   map_operation(pairs, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   auto storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", catalog);\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 0 );\n\n   // keys are indexed so they cant be too big(greater than CUSTOM_OPERATIONS_MAX_KEY_SIZE(200) is not allowed)\n   catalog = \"whatever\";\n   std::string key(201, 'a');\n   pairs.clear();\n   pairs[key] = fc::json::to_string(\"value\");\n   map_operation(pairs, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", catalog);\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 0 );\n\n   // creating a map with bad json as value is not allowed\n   catalog = \"whatever\";\n   pairs.clear();\n   pairs[\"key\"] = \"value\";\n   map_operation(pairs, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", catalog);\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 0 );\n\n   // nathan adds key-value data via custom operation to a settings catalog\n   catalog = \"settings\";\n   pairs.clear();\n   pairs[\"language\"] = fc::json::to_string(\"en\");\n   pairs[\"image_url\"] = fc::json::to_string(\"http://some.image.url/img.jpg\");\n   map_operation(pairs, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // check nathan stored data with the api\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"settings\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 2 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, \"image_url\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].value->as_string(), \"http://some.image.url/img.jpg\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].key, \"language\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].value->as_string(), \"en\");\n\n   // edit some stuff and add new stuff\n   pairs.clear();\n   pairs[\"image_url\"] = fc::json::to_string(\"http://new.image.url/newimg.jpg\");\n   pairs[\"theme\"] = fc::json::to_string(\"dark\");\n   map_operation(pairs, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // check old and new stuff\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"settings\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 3 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, \"image_url\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].value->as_string(), \"http://new.image.url/newimg.jpg\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].key, \"language\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].value->as_string(), \"en\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[2].key, \"theme\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[2].value->as_string(), \"dark\");\n\n   // delete stuff from the storage\n   pairs.clear();\n   pairs[\"theme\"] = fc::json::to_string(\"dark\");\n   map_operation(pairs, true, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // theme is removed from the storage\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"settings\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 2 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, \"image_url\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].value->as_string(), \"http://new.image.url/newimg.jpg\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].key, \"language\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].value->as_string(), \"en\");\n\n   // delete stuff that it is not there\n   pairs.clear();\n   pairs[\"nothere\"] = fc::json::to_string(\"nothere\");\n   map_operation(pairs, true, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // nothing changes\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"settings\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 2 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, \"image_url\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].value->as_string(), \"http://new.image.url/newimg.jpg\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].key, \"language\");\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].value->as_string(), \"en\");\n\n   // alice, duplicated keys in storage, only second value will be added\n   pairs.clear();\n   catalog = \"random\";\n   pairs[\"key1\"] = fc::json::to_string(\"value1\");\n   pairs[\"key1\"] = fc::json::to_string(\"value2\");\n   map_operation(pairs, false, catalog, alice_id, alice_private_key, db);\n   generate_block();\n\n   vector<account_storage_object> storage_results_alice = custom_operations_api.get_storage_info(\"alice\", \"random\");\n   BOOST_CHECK_EQUAL(storage_results_alice.size(), 1 );\n   BOOST_CHECK_EQUAL(storage_results_alice[0].account.instance.value, 17 );\n   BOOST_CHECK_EQUAL(storage_results_alice[0].key, \"key1\");\n   BOOST_CHECK_EQUAL(storage_results_alice[0].value->as_string(), \"value2\");\n\n   // add an object\n   pairs.clear();\n   catalog = \"account_object\";\n   pairs[\"nathan\"] = fc::json::to_string(nathan);\n   map_operation(pairs, false, catalog, alice_id, alice_private_key, db);\n   generate_block();\n\n   storage_results_alice = custom_operations_api.get_storage_info(\"alice\", \"account_object\");\n   BOOST_CHECK_EQUAL(storage_results_alice.size(), 1);\n   BOOST_CHECK_EQUAL(storage_results_alice[0].account.instance.value, 17);\n   BOOST_CHECK_EQUAL(storage_results_alice[0].key, \"nathan\");\n   BOOST_CHECK_EQUAL(storage_results_alice[0].value->as<account_object>(20).name, \"nathan\");\n\n   // add 2 more objects\n   pairs.clear();\n   catalog = \"account_object\";\n   pairs[\"robert\"] = fc::json::to_string(robert);\n   pairs[\"patty\"] = fc::json::to_string(patty);\n   map_operation(pairs, false, catalog, alice_id, alice_private_key, db);\n   generate_block();\n\n   storage_results_alice = custom_operations_api.get_storage_info(\"alice\", \"account_object\");\n   BOOST_CHECK_EQUAL(storage_results_alice.size(), 3);\n   BOOST_CHECK_EQUAL(storage_results_alice[0].account.instance.value, 17);\n   BOOST_CHECK_EQUAL(storage_results_alice[0].key, \"nathan\");\n   BOOST_CHECK_EQUAL(storage_results_alice[0].value->as<account_object>(20).name, \"nathan\");\n   BOOST_CHECK_EQUAL(storage_results_alice[1].account.instance.value, 17);\n   BOOST_CHECK_EQUAL(storage_results_alice[1].key, \"patty\");\n   BOOST_CHECK_EQUAL(storage_results_alice[1].value->as<account_object>(20).name, \"patty\");\n   BOOST_CHECK_EQUAL(storage_results_alice[2].key, \"robert\");\n   BOOST_CHECK_EQUAL(storage_results_alice[2].value->as<account_object>(20).name, \"robert\");\n}\ncatch (fc::exception &e) {\n   edump((e.to_detail_string()));\n   throw;\n} }\n\nBOOST_AUTO_TEST_CASE(custom_operations_account_storage_list_test)\n{\ntry {\n   ACTORS((nathan)(alice)(robert)(patty));\n\n   app.enable_plugin(\"custom_operations\");\n   custom_operations_api custom_operations_api(app);\n\n   generate_block();\n   enable_fees();\n\n   int64_t init_balance(10000 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   transfer(committee_account, nathan_id, asset(init_balance));\n   transfer(committee_account, alice_id, asset(init_balance));\n\n   // catalog is indexed so cant be too big(greater than CUSTOM_OPERATIONS_MAX_KEY_SIZE(200) is not allowed)\n   std::string catalog(201, 'a');\n   flat_map<string, optional<string>> accounts;\n   accounts[robert.name];\n   map_operation(accounts, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   auto storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", catalog);\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 0 );\n\n   // keys are indexed so they cant be too big(greater than CUSTOM_OPERATIONS_MAX_KEY_SIZE(200) is not allowed)\n   catalog = \"whatever\";\n   std::string value(201, 'a');\n   accounts.clear();\n   accounts[value];\n   map_operation(accounts, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", catalog);\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 0 );\n\n   // nathan add a list of accounts to storage\n   accounts.clear();\n   accounts[alice.name];\n   accounts[robert.name];\n   catalog = \"contact_list\";\n   map_operation(accounts, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // get the account list for nathan, check alice and robert are there\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"contact_list\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 2 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, alice.name);\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].key, robert.name);\n\n   // add a value into account list already there\n   accounts.clear();\n   accounts[alice.name];\n   map_operation(accounts, false, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // nothing changes\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"contact_list\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 2 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, alice.name);\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[1].key, robert.name);\n\n   // delete alice from the list\n   accounts.clear();\n   accounts[alice.name];\n   map_operation(accounts, true, catalog, nathan_id, nathan_private_key, db);\n   generate_block();\n\n   // alice gone\n   storage_results_nathan = custom_operations_api.get_storage_info(\"nathan\", \"contact_list\");\n   BOOST_CHECK_EQUAL(storage_results_nathan.size(), 1 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].account.instance.value, 16 );\n   BOOST_CHECK_EQUAL(storage_results_nathan[0].key, robert.name);\n\n   // duplicated accounts in the list, only 1 will be inserted\n   accounts.clear();\n   accounts[robert.name];\n   accounts[robert.name];\n   map_operation(accounts, false, catalog, alice_id, alice_private_key, db);\n   generate_block();\n\n   auto storage_results_alice = custom_operations_api.get_storage_info(\"alice\", \"contact_list\");\n   BOOST_CHECK_EQUAL(storage_results_alice.size(), 1 );\n   BOOST_CHECK_EQUAL(storage_results_alice[0].account.instance.value, 17 );\n   BOOST_CHECK_EQUAL(storage_results_alice[0].key, robert.name);\n}\ncatch (fc::exception &e) {\n   edump((e.to_detail_string()));\n   throw;\n} }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/database_api_tests.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/database_api.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/crypto/digest.hpp>\n#include <fc/crypto/hex.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n#include <random>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture)\n\nBOOST_AUTO_TEST_CASE(is_registered)\n{\n   try {\n      /***\n       * Arrange\n       */\n      auto nathan_private_key = generate_private_key(\"nathan\");\n      public_key_type nathan_public = nathan_private_key.get_public_key();\n\n      auto dan_private_key = generate_private_key(\"dan\");\n      public_key_type dan_public = dan_private_key.get_public_key();\n\n      auto unregistered_private_key = generate_private_key(\"unregistered\");\n      public_key_type unregistered_public = unregistered_private_key.get_public_key();\n\n\n      /***\n       * Act\n       */\n      create_account(\"dan\", dan_private_key.get_public_key());\n      create_account(\"nathan\", nathan_private_key.get_public_key());\n      // Unregistered key will not be registered with any account\n\n\n      /***\n       * Assert\n       */\n      graphene::app::database_api db_api1(db);\n      BOOST_CHECK_THROW( db_api1.is_public_key_registered((string) nathan_public), fc::exception );\n\n      graphene::app::application_options opt = app.get_options();\n      opt.has_api_helper_indexes_plugin = true;\n      graphene::app::database_api db_api( db, &opt );\n\n      BOOST_CHECK(db_api.is_public_key_registered((string) nathan_public));\n      BOOST_CHECK(db_api.is_public_key_registered((string) dan_public));\n      BOOST_CHECK(!db_api.is_public_key_registered((string) unregistered_public));\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( get_potential_signatures_owner_and_active )\n{\n   try {\n      fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      public_key_type pub_key_active( nathan_key1.get_public_key() );\n      public_key_type pub_key_owner( nathan_key2.get_public_key() );\n      const account_object& nathan = create_account(\"nathan\", nathan_key1.get_public_key() );\n\n      try {\n         account_update_operation op;\n         op.account = nathan.id;\n         op.active = authority(1, pub_key_active, 1);\n         op.owner = authority(1, pub_key_owner, 1);\n         trx.operations.push_back(op);\n         sign(trx, nathan_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n      } FC_CAPTURE_AND_RETHROW ((nathan.active))\n\n      // this op requires active\n      transfer_operation op;\n      op.from = nathan.id;\n      op.to = account_id_type();\n      trx.operations.push_back(op);\n\n      graphene::app::database_api db_api(db);\n      set<public_key_type> pub_keys = db_api.get_potential_signatures( trx );\n\n      BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n\n      trx.operations.clear();\n\n      // this op requires owner\n      account_update_operation auop;\n      auop.account = nathan.id;\n      auop.owner = authority(1, pub_key_owner, 1);\n      trx.operations.push_back(auop);\n\n      pub_keys = db_api.get_potential_signatures( trx );\n\n      BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() ); // active key doesn't help in this case\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\n/// Testing get_potential_signatures and get_required_signatures for non-immediate owner authority issue.\n/// https://github.com/bitshares/bitshares-core/issues/584\nBOOST_AUTO_TEST_CASE( get_signatures_non_immediate_owner )\n{\n   try {\n      fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      fc::ecc::private_key ashley_key1 = fc::ecc::private_key::regenerate(fc::digest(\"akey1\"));\n      fc::ecc::private_key ashley_key2 = fc::ecc::private_key::regenerate(fc::digest(\"akey2\"));\n      fc::ecc::private_key oliver_key1 = fc::ecc::private_key::regenerate(fc::digest(\"okey1\"));\n      fc::ecc::private_key oliver_key2 = fc::ecc::private_key::regenerate(fc::digest(\"okey2\"));\n      public_key_type pub_key_active( nathan_key1.get_public_key() );\n      public_key_type pub_key_owner( nathan_key2.get_public_key() );\n      public_key_type a_pub_key_active( ashley_key1.get_public_key() );\n      public_key_type a_pub_key_owner( ashley_key2.get_public_key() );\n      public_key_type o_pub_key_active( oliver_key1.get_public_key() );\n      public_key_type o_pub_key_owner( oliver_key2.get_public_key() );\n      const account_object& nathan = create_account(\"nathan\", nathan_key1.get_public_key() );\n      const account_object& ashley = create_account(\"ashley\", ashley_key1.get_public_key() );\n      const account_object& oliver = create_account(\"oliver\", oliver_key1.get_public_key() );\n      account_id_type nathan_id = nathan.id;\n      account_id_type ashley_id = ashley.id;\n      account_id_type oliver_id = oliver.id;\n\n      try {\n         account_update_operation op;\n         op.account = nathan_id;\n         op.active = authority(1, pub_key_active, 1, ashley_id, 1);\n         op.owner = authority(1, pub_key_owner, 1, oliver_id, 1);\n         trx.operations.push_back(op);\n         sign(trx, nathan_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n\n         op.account = ashley_id;\n         op.active = authority(1, a_pub_key_active, 1);\n         op.owner = authority(1, a_pub_key_owner, 1);\n         trx.operations.push_back(op);\n         sign(trx, ashley_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n\n         op.account = oliver_id;\n         op.active = authority(1, o_pub_key_active, 1);\n         op.owner = authority(1, o_pub_key_owner, 1);\n         trx.operations.push_back(op);\n         sign(trx, oliver_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n      } FC_CAPTURE_AND_RETHROW ((nathan.active))\n\n      // this transaction requires active\n      signed_transaction trx_a;\n      transfer_operation op;\n      op.from = nathan_id;\n      op.to = account_id_type();\n      trx_a.operations.push_back(op);\n\n      // get potential signatures\n      graphene::app::database_api db_api(db);\n      set<public_key_type> pub_keys = db_api.get_potential_signatures( trx_a );\n\n      BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_active ) != pub_keys.end() );\n      // doesn't work due to https://github.com/bitshares/bitshares-core/issues/584\n      BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );\n      // doesn't work due to https://github.com/bitshares/bitshares-core/issues/584\n      BOOST_CHECK( pub_keys.find( o_pub_key_owner ) == pub_keys.end() );\n\n      // get required signatures\n      pub_keys = db_api.get_required_signatures( trx_a, { a_pub_key_owner, o_pub_key_owner } );\n      BOOST_CHECK( pub_keys.empty() );\n\n      // this op requires owner\n      signed_transaction trx_o;\n      account_update_operation auop;\n      auop.account = nathan_id;\n      auop.owner = authority(1, pub_key_owner, 1);\n      trx_o.operations.push_back(auop);\n\n      // get potential signatures\n      pub_keys = db_api.get_potential_signatures( trx_o );\n\n      // active authorities doesn't help in this case\n      BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_active ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );\n\n      // owner authorities should be ok\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );\n      // doesn't work due to https://github.com/bitshares/bitshares-core/issues/584\n      BOOST_CHECK( pub_keys.find( o_pub_key_owner ) == pub_keys.end() );\n\n      // get required signatures\n      pub_keys = db_api.get_required_signatures( trx_o, { a_pub_key_owner, o_pub_key_owner } );\n      BOOST_CHECK( pub_keys.empty() );\n\n      // go beyond hard fork\n      generate_blocks( HARDFORK_CORE_584_TIME, true );\n\n      // for the transaction that requires active\n      // get potential signatures\n      pub_keys = db_api.get_potential_signatures( trx_a );\n\n      // all authorities should be ok\n      BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_active ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_owner ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );\n\n      // get required signatures\n      pub_keys = db_api.get_required_signatures( trx_a, { a_pub_key_owner } );\n      BOOST_CHECK( pub_keys.find( a_pub_key_owner ) != pub_keys.end() );\n      pub_keys = db_api.get_required_signatures( trx_a, { o_pub_key_owner } );\n      BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );\n\n      // for the transaction that requires owner\n      // get potential signatures\n      pub_keys = db_api.get_potential_signatures( trx_o );\n\n      // active authorities doesn't help in this case\n      BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_active ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );\n\n      // owner authorities should help\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );\n\n      // get required signatures\n      pub_keys = db_api.get_required_signatures( trx_o, { a_pub_key_owner, o_pub_key_owner } );\n      BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( get_potential_signatures_other )\n{\n   try {\n      fc::ecc::private_key priv_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      public_key_type pub_key1( priv_key1.get_public_key() );\n\n      const account_object& nathan = create_account( \"nathan\" );\n\n      balance_claim_operation op;\n      op.deposit_to_account = nathan.id;\n      op.balance_owner_key = pub_key1;\n      trx.operations.push_back(op);\n\n      graphene::app::database_api db_api(db);\n      set<public_key_type> pub_keys = db_api.get_potential_signatures( trx );\n\n      BOOST_CHECK( pub_keys.find( pub_key1 ) != pub_keys.end() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( get_required_signatures_owner_or_active )\n{\n   try {\n      fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      public_key_type pub_key_active( nathan_key1.get_public_key() );\n      public_key_type pub_key_owner( nathan_key2.get_public_key() );\n      const account_object& nathan = create_account(\"nathan\", nathan_key1.get_public_key() );\n\n      try {\n         account_update_operation op;\n         op.account = nathan.id;\n         op.active = authority(1, pub_key_active, 1);\n         op.owner = authority(1, pub_key_owner, 1);\n         trx.operations.push_back(op);\n         sign(trx, nathan_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n      } FC_CAPTURE_AND_RETHROW ((nathan.active))\n\n      graphene::app::database_api db_api(db);\n\n      // prepare available keys sets\n      flat_set<public_key_type> avail_keys1, avail_keys2, avail_keys3;\n      avail_keys1.insert( pub_key_active );\n      avail_keys2.insert( pub_key_owner );\n      avail_keys3.insert( pub_key_active );\n      avail_keys3.insert( pub_key_owner );\n\n      set<public_key_type> pub_keys;\n\n      // this op requires active\n      transfer_operation op;\n      op.from = nathan.id;\n      op.to = account_id_type();\n      trx.operations.push_back(op);\n\n      // provides active, should be ok\n      pub_keys = db_api.get_required_signatures( trx, avail_keys1 );\n      BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );\n\n      // provides owner, should be ok\n      pub_keys = db_api.get_required_signatures( trx, avail_keys2 );\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n\n      // provides both active and owner, should return one of them\n      pub_keys = db_api.get_required_signatures( trx, avail_keys3 );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() || pub_keys.find( pub_key_owner ) != pub_keys.end() );\n\n      trx.operations.clear();\n\n      // this op requires owner\n      account_update_operation auop;\n      auop.account = nathan.id;\n      auop.owner = authority(1, pub_key_owner, 1);\n      trx.operations.push_back(auop);\n\n      // provides active, should return an empty set\n      pub_keys = db_api.get_required_signatures( trx, avail_keys1 );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides owner, should return it\n      pub_keys = db_api.get_required_signatures( trx, avail_keys2 );\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n\n      // provides both active and owner, should return owner only\n      pub_keys = db_api.get_required_signatures( trx, avail_keys3 );\n      BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( get_required_signatures_partially_signed_or_not )\n{\n   try {\n      fc::ecc::private_key morgan_key = fc::ecc::private_key::regenerate(fc::digest(\"morgan_key\"));\n      fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::digest(\"nathan_key\"));\n      fc::ecc::private_key oliver_key = fc::ecc::private_key::regenerate(fc::digest(\"oliver_key\"));\n      public_key_type pub_key_morgan( morgan_key.get_public_key() );\n      public_key_type pub_key_nathan( nathan_key.get_public_key() );\n      public_key_type pub_key_oliver( oliver_key.get_public_key() );\n      const account_object& morgan = create_account(\"morgan\", morgan_key.get_public_key() );\n      const account_object& nathan = create_account(\"nathan\", nathan_key.get_public_key() );\n      const account_object& oliver = create_account(\"oliver\", oliver_key.get_public_key() );\n\n      graphene::app::database_api db_api(db);\n\n      // prepare available keys sets\n      flat_set<public_key_type> avail_keys_empty, avail_keys_m, avail_keys_n, avail_keys_o;\n      flat_set<public_key_type> avail_keys_mn, avail_keys_mo, avail_keys_no, avail_keys_mno;\n      avail_keys_m.insert( pub_key_morgan );\n      avail_keys_mn.insert( pub_key_morgan );\n      avail_keys_mo.insert( pub_key_morgan );\n      avail_keys_mno.insert( pub_key_morgan );\n      avail_keys_n.insert( pub_key_nathan );\n      avail_keys_mn.insert( pub_key_nathan );\n      avail_keys_no.insert( pub_key_nathan );\n      avail_keys_mno.insert( pub_key_nathan );\n      avail_keys_o.insert( pub_key_oliver );\n      avail_keys_mo.insert( pub_key_oliver );\n      avail_keys_no.insert( pub_key_oliver );\n      avail_keys_mno.insert( pub_key_oliver );\n\n      // result set\n      set<public_key_type> pub_keys;\n\n      // make a transaction that require 1 signature (m)\n      transfer_operation op;\n      op.from = morgan.id;\n      op.to = oliver.id;\n      trx.operations.push_back(op);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n     // provides [m], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n       // provides [m,n], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // sign with n, but actually need m\n      sign(trx, nathan_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [m,o], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n,o], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // sign with m, should be enough\n      trx.clear_signatures();\n      sign(trx, morgan_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // sign with m+n, although m only should be enough, this API won't complain\n      sign(trx, nathan_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // make a transaction that require 2 signatures (m+n)\n      trx.clear_signatures();\n      op.from = nathan.id;\n      trx.operations.push_back(op);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return [m,n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 2 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,o], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n,o], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,n,o], should return [m,n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 2 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // sign with o, but actually need m+n\n      sign(trx, oliver_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return [m,n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 2 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,o], should return [m]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n\n      // provides [n,o], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,n,o], should return [m,n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 2 );\n      BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // sign with m+o, but actually need m+n\n      sign(trx, morgan_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n,o], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,n,o], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // sign with m, but actually need m+n\n      trx.clear_signatures();\n      sign(trx, morgan_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n,o], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // provides [m,n,o], should return [n]\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 1 );\n      BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );\n\n      // sign with m+n, should be enough\n      sign(trx, nathan_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // sign with m+n+o, should be enough as well\n      sign(trx, oliver_key);\n\n      // provides [], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_m );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_n );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_o );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_no );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n      // provides [m,n,o], should return []\n      pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );\n      BOOST_CHECK( pub_keys.size() == 0 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( subscription_key_collision_test )\n{\n   object_id_type uia_object_id = create_user_issued_asset( \"UIATEST\" ).get_id();\n\n   uint32_t objects_changed = 0;\n   auto callback = [&]( const variant& v )\n   {\n      ++objects_changed;\n   };\n\n   graphene::app::database_api db_api(db);\n   db_api.set_subscribe_callback( callback, false );\n\n   // subscribe to an account which has same instance ID as UIATEST\n   vector<string> collision_ids;\n   collision_ids.push_back( string( object_id_type( account_id_type( uia_object_id ) ) ) );\n   db_api.get_accounts( collision_ids );\n\n   generate_block();\n   fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n   BOOST_CHECK_EQUAL( objects_changed, 0 ); // did not subscribe to UIATEST, so no notification\n\n   vector<string> asset_names;\n   asset_names.push_back( \"UIATEST\" );\n   db_api.get_assets( asset_names );\n\n   generate_block();\n   fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n   BOOST_CHECK_EQUAL( objects_changed, 0 ); // UIATEST did not change in this block, so no notification\n}\n\nBOOST_AUTO_TEST_CASE( subscription_notification_test )\n{\n   try {\n\n      generate_blocks(HARDFORK_CORE_1468_TIME);\n      set_expiration( db, trx );\n      set_htlc_committee_parameters();\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS( (alice)(bob) );\n\n      create_user_issued_asset( \"UIATEST\" );\n\n      // prepare data for get_htlc\n      {\n         int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n         transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n\n         uint16_t preimage_size = 256;\n         std::vector<char> pre_image(256);\n         std::independent_bits_engine<std::default_random_engine, sizeof(unsigned), unsigned int> rbe;\n         std::generate(begin(pre_image), end(pre_image), std::ref(rbe));\n\n         // alice puts a htlc contract to bob\n         graphene::chain::htlc_create_operation create_operation;\n         BOOST_TEST_MESSAGE(\"Alice, who has 100 coins, is transferring 3 coins to Bob\");\n         create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         create_operation.to = bob_id;\n         create_operation.claim_period_seconds = 60;\n         create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n         create_operation.preimage_size = preimage_size;\n         create_operation.from = alice_id;\n         create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);\n         trx.operations.push_back(create_operation);\n         sign(trx, alice_private_key);\n         PUSH_TX(db, trx, ~0);\n         trx.clear();\n      }\n\n// declare db_api1 ~ db_api60\n#define SUB_NOTIF_TEST_NUM_CALLBACKS_PLUS_ONE 61\n// db_api1  ~ db_api30 auto-subscribe in the beginning,\n// db_api31 ~ db_api60 don't auto-subscribe\n#define SUB_NOTIF_TEST_START_ID_DISABLE_AUTO_SUB 31\n\n// create callback functions\n#define SUB_NOTIF_TEST_INIT_CALLBACKS(z, i, data) \\\n      uint32_t objects_changed ## i = 0; \\\n      auto callback ## i = [&]( const variant& v ) \\\n      { \\\n         idump((i)(v)); \\\n         ++objects_changed ## i; \\\n      }; \\\n      uint32_t expected_objects_changed ## i = 0;\n\n// create function to check results\n#define SUB_NOTIF_TEST_CHECK(z, i, data) \\\n      if( expected_objects_changed ## i > 0 ) { \\\n         BOOST_CHECK_LE( expected_objects_changed ## i, objects_changed ## i ); \\\n      } else { \\\n         BOOST_CHECK_EQUAL( expected_objects_changed ## i, objects_changed ## i ); \\\n      } \\\n      expected_objects_changed ## i = 0; \\\n      objects_changed ## i = 0;\n\n      BOOST_PP_REPEAT_FROM_TO( 1, SUB_NOTIF_TEST_NUM_CALLBACKS_PLUS_ONE, SUB_NOTIF_TEST_INIT_CALLBACKS, unused );\n\n      auto check_results = [&]()\n      {\n         BOOST_PP_REPEAT_FROM_TO( 1, SUB_NOTIF_TEST_NUM_CALLBACKS_PLUS_ONE, SUB_NOTIF_TEST_CHECK, unused );\n      };\n\n#undef SUB_NOTIF_TEST_CHECK\n#undef SUB_NOTIF_TEST_INIT_CALLBACKS\n\n      graphene::app::database_api db_api1(db);\n\n      // subscribing to all should fail\n      BOOST_CHECK_THROW( db_api1.set_subscribe_callback( callback1, true ), fc::exception );\n\n      db_api1.set_subscribe_callback( callback1, false );\n\n      graphene::app::application_options opt;\n      opt.enable_subscribe_to_all = true;\n\n      graphene::app::database_api db_api2( db, &opt );\n      db_api2.set_subscribe_callback( callback2, true ); // subscribing to all should succeed\n\n// declare the rest of API callers and initialize callbacks\n#define SUB_NOTIF_TEST_INIT_APIS(z, i, data) \\\n      graphene::app::database_api db_api ## i( db, &opt ); \\\n      db_api ## i.set_subscribe_callback( callback ## i, false );\n\n      BOOST_PP_REPEAT_FROM_TO( 3, SUB_NOTIF_TEST_NUM_CALLBACKS_PLUS_ONE, SUB_NOTIF_TEST_INIT_APIS, unused );\n\n#undef SUB_NOTIF_TEST_INIT_APIS\n\n// disable auto-subscription for some API callers\n#define SUB_NOTIF_TEST_DISABLE_AUTO_SUB(z, i, data) \\\n      db_api ## i.set_auto_subscription( false );\n\n      BOOST_PP_REPEAT_FROM_TO( SUB_NOTIF_TEST_START_ID_DISABLE_AUTO_SUB, SUB_NOTIF_TEST_NUM_CALLBACKS_PLUS_ONE,\n                               SUB_NOTIF_TEST_DISABLE_AUTO_SUB, unused );\n\n#undef SUB_NOTIF_TEST_DISABLE_AUTO_SUB\n#undef SUB_NOTIF_TEST_NUM_CALLBACKS_PLUS_ONE\n#undef SUB_NOTIF_TEST_START_ID_DISABLE_AUTO_SUB\n\n      vector<object_id_type> account_ids;\n      account_ids.push_back( alice_id );\n      db_api1.get_objects( account_ids );         // db_api1  subscribe to Alice\n      db_api11.get_objects( account_ids, true );  // db_api11 subscribe to Alice\n      db_api21.get_objects( account_ids, false ); // db_api21 doesn't subscribe to Alice\n      db_api31.get_objects( account_ids );        // db_api31 doesn't subscribe to Alice\n      db_api41.get_objects( account_ids, true );  // db_api41 subscribe to Alice\n      db_api51.get_objects( account_ids, false ); // db_api51 doesn't subscribe to Alice\n\n      vector<string> account_names;\n      account_names.push_back( \"alice\" );\n      db_api4.get_accounts( account_names );         // db_api4  subscribe to Alice\n      db_api14.get_accounts( account_names, true );  // db_api14 subscribe to Alice\n      db_api24.get_accounts( account_names, false ); // db_api24 doesn't subscribe to Alice\n      db_api34.get_accounts( account_names );        // db_api34 doesn't subscribe to Alice\n      db_api44.get_accounts( account_names, true );  // db_api44 subscribe to Alice\n      db_api54.get_accounts( account_names, false ); // db_api54 doesn't subscribe to Alice\n\n      db_api5.lookup_accounts( \"ali\", 1 );         // db_api5  subscribe to Alice\n      db_api15.lookup_accounts( \"ali\", 1, true );  // db_api15 subscribe to Alice\n      db_api25.lookup_accounts( \"ali\", 1, false ); // db_api25 doesn't subscribe to Alice\n      db_api35.lookup_accounts( \"ali\", 1 );        // db_api35 doesn't subscribe to Alice\n      db_api45.lookup_accounts( \"ali\", 1, true );  // db_api45 subscribe to Alice\n      db_api55.lookup_accounts( \"ali\", 1, false ); // db_api55 doesn't subscribe to Alice\n\n      db_api6.lookup_accounts( \"alice\", 3 );         // db_api6  does not subscribe to Alice\n      db_api16.lookup_accounts( \"alice\", 3, true );  // db_api16 does not subscribe to Alice\n      db_api26.lookup_accounts( \"alice\", 3, false ); // db_api26 does not subscribe to Alice\n      db_api36.lookup_accounts( \"alice\", 3 );        // db_api36 does not subscribe to Alice\n      db_api46.lookup_accounts( \"alice\", 3, true );  // db_api46 does not subscribe to Alice\n      db_api56.lookup_accounts( \"alice\", 3, false ); // db_api56 does not subscribe to Alice\n\n      vector<string> asset_names;\n      asset_names.push_back( \"UIATEST\" );\n      db_api7.get_assets( asset_names );         // db_api7  subscribe to UIA\n      db_api17.get_assets( asset_names, true );  // db_api17 subscribe to UIA\n      db_api27.get_assets( asset_names, false ); // db_api27 doesn't subscribe to UIA\n      db_api37.get_assets( asset_names );        // db_api37 doesn't subscribe to UIA\n      db_api47.get_assets( asset_names, true );  // db_api47 subscribe to UIA\n      db_api57.get_assets( asset_names, false ); // db_api57 doesn't subscribe to UIA\n\n      graphene::chain::htlc_id_type alice_htlc_id_bob; // assuming ID of the first htlc object is 0\n      db_api8.get_htlc( alice_htlc_id_bob );         // db_api8  subscribe to the HTLC object\n      db_api18.get_htlc( alice_htlc_id_bob, true );  // db_api18 subscribe to the HTLC object\n      db_api28.get_htlc( alice_htlc_id_bob, false ); // db_api28 doesn't subscribe to the HTLC object\n      db_api38.get_htlc( alice_htlc_id_bob );        // db_api38 doesn't subscribe to the HTLC object\n      db_api48.get_htlc( alice_htlc_id_bob, true );  // db_api48 subscribe to the HTLC object\n      db_api58.get_htlc( alice_htlc_id_bob, false ); // db_api58 doesn't subscribe to the HTLC object\n\n      generate_block();\n      ++expected_objects_changed1; // db_api1 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed11; // db_api11 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed41; // db_api41 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed2; // db_api2 subscribed to all, notify new objects\n      // db_api3 didn't subscribe to anything, nothing would be notified\n      ++expected_objects_changed4; // db_api4 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed14; // db_api14 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed44; // db_api44 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed5; // db_api5 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed15; // db_api15 subscribed to Alice, notify Alice account creation\n      ++expected_objects_changed45; // db_api45 subscribed to Alice, notify Alice account creation\n      // db_api*6 didn't subscribe to anything, nothing would be notified\n      ++expected_objects_changed7; // db_api7 subscribed to UIA, notify asset creation\n      ++expected_objects_changed17; // db_api17 subscribed to UIA, notify asset creation\n      ++expected_objects_changed47; // db_api47 subscribed to UIA, notify asset creation\n      ++expected_objects_changed8; // db_api8 subscribed to HTLC object, notify object creation\n      ++expected_objects_changed18; // db_api18 subscribed to HTLC object, notify object creation\n      ++expected_objects_changed48; // db_api48 subscribed to HTLC object, notify object creation\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n      transfer( account_id_type(), alice_id, asset(1) );\n      generate_block();\n      // db_api1 didn't subscribe to Alice with get_full_accounts but only subscribed to the account object,\n      //   nothing would be notified\n      ++expected_objects_changed2; // db_api2 subscribed to all, notify new balance object and etc\n      // db_api3 didn't subscribe to anything, nothing would be notified\n      // db_api4 only subscribed to the account object of Alice, nothing notified\n      // db_api5 only subscribed to the account object of Alice, nothing notified\n      // db_api6 didn't subscribe to anything, nothing would be notified\n      // db_api7: no change on UIA, nothing would be notified\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n      vector<object_id_type> obj_ids;\n      obj_ids.push_back( db.get_dynamic_global_properties().id );\n      db_api3.get_objects( obj_ids ); // db_api3 subscribe to dynamic global properties\n\n      db_api4.get_full_accounts( account_names, true );   // db_api4 subscribe to Alice with get_full_accounts\n      db_api14.get_full_accounts( account_names, false ); // db_api14 doesn't subscribe\n      db_api24.get_full_accounts( account_names );        // db_api24 subscribe to Alice with get_full_accounts\n      db_api34.get_full_accounts( account_names, true );  // db_api34 subscribe to Alice with get_full_accounts\n      db_api44.get_full_accounts( account_names, false ); // db_api44 doesn't subscribe\n      db_api54.get_full_accounts( account_names );        // db_api54 doesn't subscribe\n\n      db_api5.get_full_accounts( account_names, false ); // db_api5 doesn't subscribe\n\n      transfer( account_id_type(), alice_id, asset(1) );\n      generate_block();\n      // db_api1 didn't subscribe to Alice with get_full_accounts but only subscribed to the account object,\n      //   nothing would be notified\n      ++expected_objects_changed2; // db_api2 subscribed to all, notify new history records and etc\n      ++expected_objects_changed3; // db_api3 subscribed to dynamic global properties, would be notified\n      ++expected_objects_changed4; // db_api4 subscribed to full account data of Alice, would be notified\n      ++expected_objects_changed24; // db_api24 subscribed to full account data of Alice, would be notified\n      ++expected_objects_changed34; // db_api34 subscribed to full account data of Alice, would be notified\n      // db_api5 only subscribed to the account object of Alice, nothing notified\n      // db_api6 didn't subscribe to anything, nothing would be notified\n      // db_api7: no change on UIA, nothing would be notified\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n      db_api6.set_auto_subscription( false );\n      db_api6.get_objects( obj_ids ); // db_api6 doesn't auto-subscribe to dynamic global properties\n\n      generate_block();\n      // db_api1 only subscribed to the account object of Alice, nothing notified\n      // db_api2 subscribed to all, but no object is created or removed in this block, so nothing notified\n      ++expected_objects_changed3; // db_api3 subscribed to dynamic global properties, would be notified\n      // db_api4 subscribed to full account data of Alice, nothing would be notified\n      // db_api5 only subscribed to the account object of Alice, nothing notified\n      // db_api6 didn't subscribe to anything, nothing would be notified\n      // db_api7: no change on UIA, nothing would be notified\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n      account_names.clear();\n      account_names.push_back( \"bob\" );\n      db_api5.set_auto_subscription( false );\n      db_api5.get_full_accounts( account_names, true ); // db_api5 subscribe to full account data of Bob\n\n      db_api6.get_full_accounts( account_names, false ); // db_api6 doesn't subscribe\n\n      transfer( account_id_type(), bob_id, asset(1) );\n\n      generate_block();\n      // db_api1 only subscribed to the account object of Alice, nothing notified\n      ++expected_objects_changed2; // db_api2 subscribed to all, notify new history records and etc\n      ++expected_objects_changed3; // db_api3 subscribed to dynamic global properties, would be notified\n      // db_api4 subscribed to full account data of Alice, nothing would be notified\n      ++expected_objects_changed5; // db_api5 subscribed to full account data of Bob, would be notified\n      // db_api6 didn't subscribe to anything, nothing would be notified\n      // db_api7: no change on UIA, nothing would be notified\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n      db_api6.set_auto_subscription( true );\n      db_api6.get_objects( obj_ids ); // db_api6 auto-subscribe to dynamic global properties\n\n      generate_block();\n      // db_api1 only subscribed to the account object of Alice, nothing notified\n      // db_api2 subscribed to all, but no object is created or removed in this block, so nothing notified\n      ++expected_objects_changed3; // db_api3 subscribed to dynamic global properties, would be notified\n      // db_api4 subscribed to full account data of Alice, nothing would be notified\n      // db_api5 subscribed to full account data of Bob, nothing notified\n      ++expected_objects_changed6; // db_api6 subscribed to dynamic global properties, would be notified\n      // db_api7: no change on UIA, nothing would be notified\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n      db_api5.set_subscribe_callback( callback5, false ); // reset subscription\n\n      db_api6.cancel_all_subscriptions();\n      db_api6.get_objects( obj_ids ); // db_api6 doesn't auto-subscribe to dynamic global properties\n\n      transfer( alice_id, bob_id, asset(1) );\n\n      generate_block();\n      // db_api1 only subscribed to the account object of Alice, nothing notified\n      ++expected_objects_changed2; // db_api2 subscribed to all, notify new history records and etc\n      ++expected_objects_changed3; // db_api3 subscribed to dynamic global properties, would be notified\n      ++expected_objects_changed4; // db_api4 subscribed to full account data of Alice, would be notified\n      ++expected_objects_changed24; // db_api24 subscribed to full account data of Alice, would be notified\n      ++expected_objects_changed34; // db_api34 subscribed to full account data of Alice, would be notified\n      // db_api5 subscribed to anything, nothing notified\n      // db_api6 subscribed to anything, nothing notified\n      // db_api7: no change on UIA, nothing would be notified\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n      check_results();\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( get_all_workers )\n{ try {\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   ACTORS( (connie)(whitney)(wolverine) );\n\n   fund(connie);\n   upgrade_to_lifetime_member(connie);\n   fund(whitney);\n   upgrade_to_lifetime_member(whitney);\n   fund(wolverine);\n   upgrade_to_lifetime_member(wolverine);\n\n   vector<worker_object> results;\n\n   const auto& worker1 = create_worker( connie_id, 1000, fc::days(10) );\n   worker_id_type worker1_id = worker1.id;\n\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 1 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 0 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 1 );\n   BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers(false).front().id == worker1_id );\n\n   generate_blocks( db.head_block_time() + fc::days(11) );\n   set_expiration( db, trx );\n\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 1 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 1 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 0 );\n   BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id );\n\n   const auto& worker2 = create_worker( whitney_id, 1000, fc::days(50) );\n   worker_id_type worker2_id = worker2.id;\n\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 2 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 1 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 1 );\n   BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers().back().id == worker2_id );\n   BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers(false).front().id == worker2_id );\n\n   const auto& worker3 = create_worker( wolverine_id, 1000, fc::days(100) );\n   worker_id_type worker3_id = worker3.id;\n\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 3 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 1 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 2 );\n   BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers().back().id == worker3_id );\n   BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers(false).front().id == worker2_id );\n   BOOST_CHECK( db_api.get_all_workers(false).back().id == worker3_id );\n\n   generate_blocks( db.head_block_time() + fc::days(55) );\n   set_expiration( db, trx );\n\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 3 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 2 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 1 );\n   BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers().back().id == worker3_id );\n   BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers(true).back().id == worker2_id );\n   BOOST_CHECK( db_api.get_all_workers(false).front().id == worker3_id );\n\n   generate_blocks( db.head_block_time() + fc::days(55) );\n   set_expiration( db, trx );\n\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 3 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 3 );\n   BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 0 );\n   BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers().back().id == worker3_id );\n   BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id );\n   BOOST_CHECK( db_api.get_all_workers(true).back().id == worker3_id );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( get_workers_by_account )\n{ try {\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   ACTORS( (connie)(whitney)(wolverine) );\n\n   fund(connie);\n   upgrade_to_lifetime_member(connie);\n   fund(whitney);\n   upgrade_to_lifetime_member(whitney);\n   fund(wolverine);\n   upgrade_to_lifetime_member(wolverine);\n\n   vector<worker_object> results;\n\n   const auto& worker1 = create_worker( connie_id );\n   worker_id_type worker1_id = worker1.id;\n\n   const auto& worker2 = create_worker( whitney_id, 1000, fc::days(50) );\n   worker_id_type worker2_id = worker2.id;\n\n   const auto& worker3 = create_worker( whitney_id, 1000, fc::days(100) );\n   worker_id_type worker3_id = worker3.id;\n\n   BOOST_REQUIRE_EQUAL( db_api.get_workers_by_account(\"connie\").size(), 1 );\n   BOOST_CHECK( db_api.get_workers_by_account(\"connie\").front().id == worker1_id );\n\n   BOOST_REQUIRE_EQUAL( db_api.get_workers_by_account(string(whitney.id)).size(), 2 );\n   BOOST_CHECK( db_api.get_workers_by_account(string(whitney.id)).front().id == worker2_id );\n   BOOST_CHECK( db_api.get_workers_by_account(string(whitney.id)).back().id == worker3_id );\n\n   BOOST_REQUIRE_EQUAL( db_api.get_workers_by_account(\"wolverine\").size(), 0 );\n\n   BOOST_REQUIRE_THROW( db_api.get_workers_by_account(\"not-a-user\"), fc::exception );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( lookup_vote_ids )\n{ try {\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   ACTORS( (connie)(whitney)(wolverine) );\n\n   fund(connie);\n   upgrade_to_lifetime_member(connie);\n   fund(whitney);\n   upgrade_to_lifetime_member(whitney);\n   fund(wolverine);\n   upgrade_to_lifetime_member(wolverine);\n\n   const auto& committee = create_committee_member( connie );\n   const auto& witness = create_witness( whitney );\n   const auto& worker = create_worker( wolverine_id );\n\n   std::vector<vote_id_type> votes;\n   votes.push_back( committee.vote_id );\n   votes.push_back( witness.vote_id );\n   votes.push_back( worker.vote_for );\n\n   const auto results = db_api.lookup_vote_ids( votes );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(get_limit_orders_by_account)\n{ try {\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   ACTORS((seller)(buyer)(watcher));\n\n   const auto& bitcny = create_user_issued_asset(\"CNY\");\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(10000000);\n   transfer( committee_account, seller_id, asset(init_balance) );\n   issue_uia( buyer_id, bitcny.amount(init_balance) );\n   BOOST_CHECK_EQUAL( 10000000, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 10000000, get_balance(buyer, bitcny) );\n\n   std::vector<limit_order_object> results, results2;\n   limit_order_object o;\n\n   // limit too large\n   BOOST_CHECK_THROW( db_api.get_limit_orders_by_account( seller.name, 102 ), fc::exception );\n\n   // The order book is empty\n   results = db_api.get_limit_orders_by_account( seller.name );\n   BOOST_CHECK_EQUAL( results.size(), 0 );\n\n   // Seller create 50 orders\n   for (size_t i = 0 ; i < 50 ; ++i)\n   {\n      BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250)));\n   }\n\n   // Get all orders\n   results = db_api.get_limit_orders_by_account( seller.name );\n   BOOST_CHECK_EQUAL( results.size(), 50 );\n\n   // Seller create 200 orders\n   for (size_t i = 1 ; i < 101 ; ++i)\n   {\n      BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 + i)));\n      BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 - i)));\n   }\n\n   // Buyer create 20 orders\n   for (size_t i = 0 ; i < 70 ; ++i)\n   {\n      BOOST_CHECK(create_sell_order(buyer, bitcny.amount(100), core.amount(5000 + i)));\n   }\n\n   // Get the first 101 orders\n   results = db_api.get_limit_orders_by_account( seller.name );\n   BOOST_CHECK_EQUAL( results.size(), 101 );\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].id < results[i+1].id);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(250)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(276)));\n   o = results.back();\n\n   // Get the No. 101-201 orders\n   results = db_api.get_limit_orders_by_account( seller.name, {}, o.id );\n   BOOST_CHECK_EQUAL( results.size(), 101 );\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].id < results[i+1].id);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(276)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(326)));\n   o = results.back();\n\n   // Get the No. 201- orders\n   results = db_api.get_limit_orders_by_account( seller.name, {}, o.id );\n   BOOST_CHECK_EQUAL( results.size(), 50 );\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].id < results[i+1].id);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(326)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(150)));\n\n   // Get the No. 201-210 orders\n   results2 = db_api.get_limit_orders_by_account( seller.name, 10, o.id );\n   BOOST_CHECK_EQUAL( results2.size(), 10 );\n   for (size_t i = 0 ; i < results2.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results2[i].id < results2[i+1].id);\n      BOOST_CHECK(results[i].id == results2[i].id);\n   }\n   BOOST_CHECK(results2.front().sell_price == price(core.amount(100), bitcny.amount(326)));\n   BOOST_CHECK(results2.back().sell_price == price(core.amount(100), bitcny.amount(170)));\n\n   // Buyer has 70 orders, all IDs are greater than sellers\n   results = db_api.get_limit_orders_by_account( buyer.name, 90, o.id );\n   BOOST_CHECK_EQUAL( results.size(), 70 );\n   o = results.back();\n\n   // All seller's order IDs are smaller, so querying with a buyer's ID will get nothing\n   results = db_api.get_limit_orders_by_account( seller.name, 90, o.id );\n   BOOST_CHECK_EQUAL( results.size(), 0 );\n\n   // Watcher has no order\n   results = db_api.get_limit_orders_by_account( watcher.name );\n   BOOST_CHECK_EQUAL( results.size(), 0 );\n\n   // unregistered account, throws exception\n   BOOST_CHECK_THROW( db_api.get_limit_orders_by_account( \"not-a-user\", 10, limit_order_id_type() ),\n                      fc::exception );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(get_account_limit_orders)\n{ try {\n   graphene::app::database_api db_api( db, &( app.get_options() ));\n   ACTORS((seller));\n\n   const auto& bitcny = create_bitasset(\"CNY\");\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(10000000);\n   transfer(committee_account, seller_id, asset(init_balance));\n   BOOST_CHECK_EQUAL( 10000000, get_balance(seller, core) );\n\n   /// Create 250 versatile orders\n   for (size_t i = 0 ; i < 50 ; ++i)\n   {\n      BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250)));\n   }\n\n   for (size_t i = 1 ; i < 101 ; ++i)\n   {\n      BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 + i)));\n      BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 - i)));\n   }\n\n   std::vector<limit_order_object> results;\n   limit_order_object o;\n\n   // query with no constraint, expected:\n   // 1. up to 101 orders returned\n   // 2. orders were sorted by price desendingly\n   results = db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\");\n   BOOST_CHECK(results.size() == 101);\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(150)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(250)));\n   results.clear();\n\n   // query with specified limit, expected:\n   // 1. up to specified amount of orders returned\n   // 2. orders were sorted by price desendingly\n   results = db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\", 50);\n   BOOST_CHECK(results.size() == 50);\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(150)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(199)));\n\n   o = results.back();\n   results.clear();\n\n   // query with specified order id and limit, expected:\n   // same as before, but also the first order's id equal to specified\n   results = db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\", 80,\n       limit_order_id_type(o.id));\n   BOOST_CHECK(results.size() == 80);\n   BOOST_CHECK(results.front().id == o.id);\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(199)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(250)));\n\n   o = results.back();\n   results.clear();\n\n   // query with specified price and an not exists order id, expected:\n   // 1. the canceled order should not exists in returned orders and first order's\n   //    id should greater than specified\n   // 2. returned orders sorted by price desendingly\n   // 3. the first order's sell price equal to specified\n   cancel_limit_order(o); // NOTE 1: this canceled order was in scope of the\n                          // first created 50 orders, so with price 2.5 BTS/CNY\n   results = db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\", 50,\n       limit_order_id_type(o.id), o.sell_price);\n   BOOST_CHECK(results.size() == 50);\n   BOOST_CHECK(results.front().id > o.id);\n   // NOTE 2: because of NOTE 1, here should be equal\n   BOOST_CHECK(results.front().sell_price == o.sell_price);\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(250)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(279)));\n\n   o = results.back();\n   results.clear();\n\n   cancel_limit_order(o); // NOTE 3: this time the canceled order was in scope\n                          // of the lowest price 150 orders\n   results = db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\", 101,\n       limit_order_id_type(o.id), o.sell_price);\n   BOOST_CHECK(results.size() == 71);\n   BOOST_CHECK(results.front().id > o.id);\n   // NOTE 3: because of NOTE 1, here should be little than\n   BOOST_CHECK(results.front().sell_price < o.sell_price);\n   for (size_t i = 0 ; i < results.size() - 1 ; ++i)\n   {\n      BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);\n   }\n   BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(280)));\n   BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(350)));\n\n   BOOST_CHECK_THROW(db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, \"CNY\", 101,\n               limit_order_id_type(o.id)), fc::exception);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( get_transaction_hex )\n{ try {\n   graphene::app::database_api db_api(db);\n   auto test_private_key = generate_private_key(\"testaccount\");\n   public_key_type test_public = test_private_key.get_public_key();\n\n   trx.operations.push_back(make_account(\"testaccount\", test_public));\n   trx.validate();\n\n   // case1: not signed, get hex\n   std::string hex_str = fc::to_hex( fc::raw::pack( trx ) );\n\n   BOOST_CHECK( db_api.get_transaction_hex( trx ) == hex_str );\n   BOOST_CHECK( db_api.get_transaction_hex_without_sig( trx ) + \"00\" == hex_str );\n\n   // case2: signed, get hex\n   sign( trx, test_private_key );\n   hex_str = fc::to_hex( fc::raw::pack( trx ) );\n\n   BOOST_CHECK( db_api.get_transaction_hex( trx ) == hex_str );\n   BOOST_CHECK( db_api.get_transaction_hex_without_sig( trx ) +\n                   fc::to_hex( fc::raw::pack( trx.signatures ) ) == hex_str );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(verify_account_authority)\n{\n      try {\n\n         ACTORS( (nathan) );\n         graphene::app::database_api db_api(db);\n\n         // good keys\n         flat_set<public_key_type> public_keys;\n         public_keys.emplace(nathan_public_key);\n         BOOST_CHECK(db_api.verify_account_authority( \"nathan\", public_keys));\n\n         // bad keys\n         flat_set<public_key_type> bad_public_keys;\n         bad_public_keys.emplace(public_key_type(\"BTS6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy\"));\n         BOOST_CHECK(!db_api.verify_account_authority( \"nathan\", bad_public_keys));\n\n      } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( any_two_of_three )\n{\n   try {\n      fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest(\"key3\"));\n      const account_object& nathan = create_account(\"nathan\", nathan_key1.get_public_key() );\n      fund(nathan);\n      graphene::app::database_api db_api(db);\n\n      try {\n         account_update_operation op;\n         op.account = nathan.id;\n         op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1,\n               public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         sign(trx, nathan_key1);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n      } FC_CAPTURE_AND_RETHROW ((nathan.active))\n\n      // two keys should work\n      {\n      \tflat_set<public_key_type> public_keys;\n      \tpublic_keys.emplace(nathan_key1.get_public_key());\n      \tpublic_keys.emplace(nathan_key2.get_public_key());\n      \tBOOST_CHECK(db_api.verify_account_authority(\"nathan\", public_keys));\n      }\n\n      // the other two keys should work\n      {\n     \t   flat_set<public_key_type> public_keys;\n      \tpublic_keys.emplace(nathan_key2.get_public_key());\n      \tpublic_keys.emplace(nathan_key3.get_public_key());\n     \t   BOOST_CHECK(db_api.verify_account_authority(\"nathan\", public_keys));\n      }\n\n      // just one key should not work\n      {\n     \t   flat_set<public_key_type> public_keys;\n         public_keys.emplace(nathan_key1.get_public_key());\n     \t   BOOST_CHECK(!db_api.verify_account_authority(\"nathan\", public_keys));\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts )\n{\n   try {\n      ACTORS( (nathan) (alice) (bob) );\n\n      graphene::app::database_api db_api(db);\n\n      try {\n         account_update_operation op;\n         op.account = nathan.id;\n         op.active = authority(3, nathan_public_key, 1, alice.id, 1, bob.id, 1);\n         op.owner = *op.active;\n         trx.operations.push_back(op);\n         sign(trx, nathan_private_key);\n         PUSH_TX( db, trx, database::skip_transaction_dupe_check );\n         trx.clear();\n      } FC_CAPTURE_AND_RETHROW ((nathan.active))\n\n      // requires 3 signatures\n      {\n      \tflat_set<public_key_type> public_keys;\n      \tpublic_keys.emplace(nathan_public_key);\n      \tpublic_keys.emplace(alice_public_key);\n      \tpublic_keys.emplace(bob_public_key);\n      \tBOOST_CHECK(db_api.verify_account_authority(\"nathan\", public_keys));\n      }\n\n      // only 2 signatures given\n      {\n      \tflat_set<public_key_type> public_keys;\n      \tpublic_keys.emplace(nathan_public_key);\n      \tpublic_keys.emplace(bob_public_key);\n      \tBOOST_CHECK(!db_api.verify_account_authority(\"nathan\", public_keys));\n      }\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( get_assets_by_issuer ) {\n   try {\n      graphene::app::database_api db_api(db, &(this->app.get_options()));\n\n      create_bitasset(\"CNY\");\n      create_bitasset(\"EUR\");\n      create_bitasset(\"USD\");\n\n      generate_block();\n\n      auto assets = db_api.get_assets_by_issuer(\"witness-account\", asset_id_type(), 10);\n\n      BOOST_CHECK(assets.size() == 3);\n      BOOST_CHECK(assets[0].symbol == \"CNY\");\n      BOOST_CHECK(assets[1].symbol == \"EUR\");\n      BOOST_CHECK(assets[2].symbol == \"USD\");\n\n      assets = db_api.get_assets_by_issuer(\"witness-account\", asset_id_type(200), 100);\n      BOOST_CHECK(assets.size() == 0);\n\n      GRAPHENE_CHECK_THROW(db_api.get_assets_by_issuer(\"nosuchaccount\", asset_id_type(), 100), fc::exception);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( get_call_orders_by_account ) {\n\n   try {\n      ACTORS((caller)(feedproducer));\n\n      graphene::app::database_api db_api(db, &(this->app.get_options()));\n\n      const auto &usd = create_bitasset(\"USD\", feedproducer_id);\n      const auto &cny = create_bitasset(\"CNY\", feedproducer_id);\n      const auto &core = asset_id_type()(db);\n\n      int64_t init_balance(1000000);\n      transfer(committee_account, caller_id, asset(init_balance));\n\n      update_feed_producers(usd, {feedproducer.id});\n      update_feed_producers(cny, {feedproducer.id});\n\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = usd.amount(1) / core.amount(5);\n      publish_feed(usd, feedproducer, current_feed);\n\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = cny.amount(1) / core.amount(5);\n      publish_feed(cny, feedproducer, current_feed);\n\n      auto call1 = borrow(caller, usd.amount(1000), asset(15000));\n      auto call2 = borrow(caller, cny.amount(1000), asset(15000));\n\n      auto calls = db_api.get_call_orders_by_account(\"caller\", asset_id_type(), 100);\n\n      BOOST_CHECK(calls.size() == 2);\n      BOOST_CHECK(calls[0].id == call1->id);\n      BOOST_CHECK(calls[1].id == call2->id);\n\n      GRAPHENE_CHECK_THROW(db_api.get_call_orders_by_account(\"nosuchaccount\", asset_id_type(), 100), fc::exception);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( get_settle_orders_by_account ) {\n   try {\n      ACTORS((creator)(settler)(caller)(feedproducer));\n\n      graphene::app::database_api db_api(db, &(this->app.get_options()));\n\n      const auto &usd = create_bitasset(\"USD\", creator_id);\n      const auto &core = asset_id_type()(db);\n      asset_id_type usd_id = usd.id;\n\n      int64_t init_balance(1000000);\n      transfer(committee_account, settler_id, asset(init_balance));\n      transfer(committee_account, caller_id, asset(init_balance));\n\n      update_feed_producers(usd, {feedproducer.id});\n\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = usd.amount(1) / core.amount(5);\n      publish_feed(usd, feedproducer, current_feed);\n\n      borrow(caller, usd.amount(1000), asset(15000));\n      generate_block();\n\n      transfer(caller.id, settler.id, asset(200, usd_id));\n\n      auto result = force_settle( settler, usd_id(db).amount(4));\n      generate_block();\n\n      auto settlements = db_api.get_settle_orders_by_account(\"settler\", force_settlement_id_type(), 100);\n\n      BOOST_CHECK(settlements.size() == 1);\n      BOOST_CHECK(settlements[0].id == result.get<object_id_type>());\n\n      GRAPHENE_CHECK_THROW(db_api.get_settle_orders_by_account(\"nosuchaccount\", force_settlement_id_type(), 100), fc::exception);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( asset_in_collateral )\n{ try {\n   ACTORS( (dan)(nathan) );\n   fund( nathan );\n   fund( dan );\n\n   graphene::app::database_api db_api( db, &( app.get_options() ) );\n\n   auto oassets = db_api.get_assets( { GRAPHENE_SYMBOL } );\n   BOOST_REQUIRE( !oassets.empty() );\n   BOOST_REQUIRE( oassets[0].valid() );\n   BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() );\n   BOOST_CHECK_EQUAL( 0, oassets[0]->total_in_collateral->value );\n   BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() );\n\n   asset_id_type bitusd_id = create_bitasset( \"USDBIT\", nathan_id, 100, charge_market_fee ).id;\n   update_feed_producers( bitusd_id, { nathan_id } );\n   asset_id_type bitdan_id = create_bitasset( \"DANBIT\", dan_id, 100, charge_market_fee ).id;\n   update_feed_producers( bitdan_id, { nathan_id } );\n   asset_id_type btc_id = create_bitasset( \"BTC\", nathan_id, 100, charge_market_fee, 8, bitusd_id ).id;\n   update_feed_producers( btc_id, { nathan_id } );\n\n   oassets = db_api.get_assets( { GRAPHENE_SYMBOL, \"USDBIT\", \"DANBIT\", \"BTC\" } );\n   BOOST_REQUIRE_EQUAL( 4, oassets.size() );\n   BOOST_REQUIRE( oassets[0].valid() );\n   BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() );\n   BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[1].valid() );\n   BOOST_REQUIRE( oassets[1]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[1]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[2].valid() );\n   BOOST_REQUIRE( oassets[2]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[2]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[3].valid() );\n   BOOST_REQUIRE( oassets[3]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[3]->total_backing_collateral.valid() );\n   BOOST_CHECK_EQUAL( 0, oassets[0]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[1]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[1]->total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[2]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[2]->total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[3]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[3]->total_backing_collateral->value );\n\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n\n   const auto& bitusd = bitusd_id( db );\n   const auto& bitdan = bitdan_id( db );\n   const auto& btc = btc_id( db );\n\n   {\n      const auto& core = asset_id_type()( db );\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd.amount(1) / core.amount(5);\n      publish_feed( bitusd_id, nathan_id, current_feed );\n      current_feed.settlement_price = bitdan.amount(1) / core.amount(5);\n      publish_feed( bitdan_id, nathan_id, current_feed );\n      current_feed.settlement_price = btc.amount(1) / bitusd.amount(100);\n      publish_feed( btc_id, nathan_id, current_feed );\n   }\n\n   borrow( nathan_id, bitusd.amount(1000), asset(15000) );\n   borrow( dan_id, bitusd.amount(100), asset(2000) );\n\n   oassets = db_api.get_assets( { GRAPHENE_SYMBOL, \"USDBIT\", \"DANBIT\", \"BTC\" } );\n   BOOST_REQUIRE_EQUAL( 4, oassets.size() );\n   BOOST_REQUIRE( oassets[0].valid() );\n   BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() );\n   BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[1].valid() );\n   BOOST_REQUIRE( oassets[1]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[1]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[2].valid() );\n   BOOST_REQUIRE( oassets[2]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[2]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[3].valid() );\n   BOOST_REQUIRE( oassets[3]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[3]->total_backing_collateral.valid() );\n   BOOST_CHECK_EQUAL( 17000, oassets[0]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[1]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 17000, oassets[1]->total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[2]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[2]->total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[3]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[3]->total_backing_collateral->value );\n\n   borrow( nathan_id, bitdan.amount(1000), asset(15000) );\n   borrow( nathan_id, btc.amount(5), bitusd.amount(1000) );\n\n   oassets = db_api.lookup_asset_symbols( { GRAPHENE_SYMBOL, \"USDBIT\", \"DANBIT\", \"BTC\" } );\n   BOOST_REQUIRE_EQUAL( 4, oassets.size() );\n   BOOST_REQUIRE( oassets[0].valid() );\n   BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() );\n   BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[1].valid() );\n   BOOST_REQUIRE( oassets[1]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[1]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[2].valid() );\n   BOOST_REQUIRE( oassets[2]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[2]->total_backing_collateral.valid() );\n   BOOST_REQUIRE( oassets[3].valid() );\n   BOOST_REQUIRE( oassets[3]->total_in_collateral.valid() );\n   BOOST_REQUIRE( oassets[3]->total_backing_collateral.valid() );\n   BOOST_CHECK_EQUAL( 32000, oassets[0]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 1000, oassets[1]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 17000, oassets[1]->total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[2]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 15000, oassets[2]->total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, oassets[3]->total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 1000, oassets[3]->total_backing_collateral->value );\n\n   force_settle( dan_id(db), bitusd.amount(100) ); // settles against nathan, receives 500 CORE collateral\n   generate_blocks( db.head_block_time() + fc::days(2) );\n   fc::usleep(fc::milliseconds(100));\n\n   auto assets = db_api.list_assets( GRAPHENE_SYMBOL, 1 );\n   BOOST_REQUIRE( !assets.empty() );\n   BOOST_REQUIRE( assets[0].total_in_collateral.valid() );\n   BOOST_CHECK( !assets[0].total_backing_collateral.valid() );\n   BOOST_CHECK_EQUAL( 31500, assets[0].total_in_collateral->value );\n\n   assets = db_api.get_assets_by_issuer( \"nathan\", asset_id_type(1), 2 );\n   BOOST_REQUIRE_EQUAL( 2, assets.size() );\n   BOOST_REQUIRE( assets[0].total_in_collateral.valid() );\n   BOOST_REQUIRE( assets[0].total_backing_collateral.valid() );\n   BOOST_REQUIRE( assets[1].total_in_collateral.valid() );\n   BOOST_REQUIRE( assets[1].total_backing_collateral.valid() );\n   BOOST_CHECK_EQUAL( 1000, assets[0].total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 16500, assets[0].total_backing_collateral->value );\n   BOOST_CHECK_EQUAL( 0, assets[1].total_in_collateral->value );\n   BOOST_CHECK_EQUAL( 1000, assets[1].total_backing_collateral->value );\n\n} FC_LOG_AND_RETHROW() }\n\n\nBOOST_AUTO_TEST_CASE( get_trade_history )\n{ try {\n\n   app.enable_plugin(\"market_history\");\n   graphene::app::application_options opt=app.get_options();\n   opt.has_market_history_plugin = true;\n   graphene::app::database_api db_api( db, &opt);\n\n   ACTORS((bob)(alice));\n\n   const auto& eur = create_user_issued_asset(\"EUR\");\n   const auto& usd = create_user_issued_asset(\"USD\");\n\n   issue_uia( bob_id, usd.amount(1000000) );\n   issue_uia( alice_id, eur.amount(1000000) );\n\n   // maker create an order\n   create_sell_order(bob, usd.amount(200), eur.amount(210));\n\n   // taker match it\n   create_sell_order(alice, eur.amount(210), usd.amount(200));\n\n   generate_block();\n\n   // taker is selling\n   auto history = db_api.get_trade_history( \"EUR\", \"USD\", db.head_block_time(), db.head_block_time() - fc::days(1) );\n   BOOST_REQUIRE_EQUAL( 1, history.size() );\n   BOOST_CHECK_EQUAL( \"1.05\", history[0].price );\n   BOOST_CHECK_EQUAL( \"2\", history[0].amount );\n   BOOST_CHECK_EQUAL( \"2.10\", history[0].value );\n   BOOST_CHECK_EQUAL( \"sell\", history[0].type );\n   BOOST_CHECK_EQUAL( bob_id.instance.value, history[0].side1_account_id.instance.value );\n   BOOST_CHECK_EQUAL( alice_id.instance.value, history[0].side2_account_id.instance.value );\n\n   // opposite side, taker is buying\n   history = db_api.get_trade_history( \"USD\", \"EUR\", db.head_block_time(), db.head_block_time() - fc::days(1) );\n   BOOST_REQUIRE_EQUAL( 1, history.size() );\n   BOOST_CHECK_EQUAL( \"0.9523809523809523809\", history[0].price );\n   BOOST_CHECK_EQUAL( \"2.10\", history[0].amount );\n   BOOST_CHECK_EQUAL( \"2\", history[0].value );\n   BOOST_CHECK_EQUAL( \"buy\", history[0].type );\n   BOOST_CHECK_EQUAL( bob_id.instance.value, history[0].side1_account_id.instance.value );\n   BOOST_CHECK_EQUAL( alice_id.instance.value, history[0].side2_account_id.instance.value );\n\n   // by sequence\n   history = db_api.get_trade_history_by_sequence( \"EUR\", \"USD\", 2, db.head_block_time() - fc::days(1) );\n   BOOST_CHECK_EQUAL( \"1.05\", history[0].price );\n   BOOST_CHECK_EQUAL( \"2\", history[0].amount );\n   BOOST_CHECK_EQUAL( \"2.10\", history[0].value );\n   BOOST_CHECK_EQUAL( \"sell\", history[0].type );\n   BOOST_CHECK_EQUAL( bob_id.instance.value, history[0].side1_account_id.instance.value );\n   BOOST_CHECK_EQUAL( alice_id.instance.value, history[0].side2_account_id.instance.value );\n\n   // opposite side\n   history = db_api.get_trade_history_by_sequence( \"USD\", \"EUR\", 2, db.head_block_time() - fc::days(1) );\n   BOOST_REQUIRE_EQUAL( 1, history.size() );\n   BOOST_CHECK_EQUAL( \"0.9523809523809523809\", history[0].price );\n   BOOST_CHECK_EQUAL( \"2.10\", history[0].amount );\n   BOOST_CHECK_EQUAL( \"2\", history[0].value );\n   BOOST_CHECK_EQUAL( \"buy\", history[0].type );\n   BOOST_CHECK_EQUAL( bob_id.instance.value, history[0].side1_account_id.instance.value );\n   BOOST_CHECK_EQUAL( alice_id.instance.value, history[0].side2_account_id.instance.value );\n\n} FC_LOG_AND_RETHROW() }\n\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/database_tests.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\n\nBOOST_FIXTURE_TEST_SUITE( database_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( undo_test )\n{\n   try {\n      database db;\n      auto ses = db._undo_db.start_undo_session();\n      const auto& bal_obj1 = db.create<account_balance_object>( [&]( account_balance_object& obj ){\n               /* no balances right now */\n      });\n      auto id1 = bal_obj1.id;\n      // abandon changes\n      ses.undo();\n      // start a new session\n      ses = db._undo_db.start_undo_session();\n\n      const auto& bal_obj2 = db.create<account_balance_object>( [&]( account_balance_object& obj ){\n               /* no balances right now */\n      });\n      auto id2 = bal_obj2.id;\n      BOOST_CHECK( id1 == id2 );\n   } catch ( const fc::exception& e )\n   {\n      edump( (e.to_detail_string()) );\n      throw;\n   }\n}\n\n/**\n * Check that database modify() functors that throw do not get caught by boost, which will remove the object\n */\nBOOST_AUTO_TEST_CASE(failed_modify_test)\n{ try {\n   database db;\n   // Create dummy object\n   const auto& obj = db.create<account_balance_object>([](account_balance_object& obj) {\n                     obj.owner = account_id_type(123);\n                  });\n   account_balance_id_type obj_id = obj.id;\n   BOOST_CHECK_EQUAL(obj.owner.instance.value, 123u);\n\n   // Modify dummy object, check that changes stick\n   db.modify(obj, [](account_balance_object& obj) {\n      obj.owner = account_id_type(234);\n   });\n   BOOST_CHECK_EQUAL(obj_id(db).owner.instance.value, 234u);\n\n   // Throw exception when modifying object, check that object still exists after\n   BOOST_CHECK_THROW(db.modify(obj, [](account_balance_object& obj) {\n      throw 5;\n   }), int);\n   BOOST_CHECK(db.find_object(obj_id));\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( flat_index_test )\n{ try {\n   ACTORS((sam));\n   const auto& bitusd = create_bitasset(\"USDBIT\", sam.id);\n   const asset_id_type bitusd_id = bitusd.id;\n   update_feed_producers(bitusd, {sam.id});\n   price_feed current_feed;\n   current_feed.settlement_price = bitusd.amount(100) / asset(100);\n   publish_feed(bitusd, sam, current_feed);\n   BOOST_CHECK_EQUAL( (int)bitusd.bitasset_data_id->instance, 1 );\n   BOOST_CHECK( !(*bitusd.bitasset_data_id)(db).current_feed.settlement_price.is_null() );\n   try {\n      auto ses = db._undo_db.start_undo_session();\n      const auto& obj1 = db.create<asset_bitasset_data_object>( [&]( asset_bitasset_data_object& obj ){\n          obj.settlement_fund = 17;\n      });\n      BOOST_REQUIRE_EQUAL( obj1.settlement_fund.value, 17 );\n      throw std::string(\"Expected\");\n      // With flat_index, obj1 will not really be removed from the index\n   } catch ( const std::string& e )\n   { // ignore\n   }\n\n   // force maintenance\n   const auto& dynamic_global_props = db.get<dynamic_global_property_object>(dynamic_global_property_id_type());\n   generate_blocks(dynamic_global_props.next_maintenance_time, true);\n\n   BOOST_CHECK( !(*bitusd_id(db).bitasset_data_id)(db).current_feed.settlement_price.is_null() );\n} FC_CAPTURE_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( merge_test )\n{\n   try {\n      database db;\n      auto ses = db._undo_db.start_undo_session();\n      db.create<account_balance_object>( [&]( account_balance_object& obj ){\n          obj.balance = 42;\n      });\n      ses.merge();\n\n      auto balance = db.get_balance( account_id_type(), asset_id_type() );\n      BOOST_CHECK_EQUAL( 42, balance.amount.value );\n   } catch ( const fc::exception& e )\n   {\n      edump( (e.to_detail_string()) );\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( direct_index_test )\n{ try {\n   try {\n      const graphene::db::primary_index< account_index, 6 > small_chunkbits( db );\n      BOOST_FAIL( \"Expected assertion failure!\" );\n   } catch( const fc::assert_exception& expected ) {}\n\n   graphene::db::primary_index< account_index, 8 > my_accounts( db );\n   const auto& direct = my_accounts.get_secondary_index<graphene::db::direct_index< account_object, 8 >>();\n   BOOST_CHECK_EQUAL( 0u, my_accounts.indices().size() );\n   BOOST_CHECK( nullptr == direct.find( account_id_type( 1 ) ) );\n   // BOOST_CHECK_THROW( direct.find( asset_id_type( 1 ) ), fc::assert_exception ); // compile-time error\n   BOOST_CHECK_THROW( direct.find( object_id_type( asset_id_type( 1 ) ) ), fc::assert_exception );\n   BOOST_CHECK_THROW( direct.get( account_id_type( 1 ) ), fc::assert_exception );\n\n   account_object test_account;\n   test_account.id = account_id_type(1);\n   test_account.name = \"account1\";\n\n   my_accounts.load( fc::raw::pack( test_account ) );\n\n   BOOST_CHECK_EQUAL( 1u, my_accounts.indices().size() );\n   BOOST_CHECK( nullptr == direct.find( account_id_type( 0 ) ) );\n   BOOST_CHECK( nullptr == direct.find( account_id_type( 2 ) ) );\n   BOOST_CHECK( nullptr != direct.find( account_id_type( 1 ) ) );\n   BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name );\n\n   // The following assumes that MAX_HOLE = 100\n   test_account.id = account_id_type(102);\n   test_account.name = \"account102\";\n   // highest insert was 1, direct.next is 2 => 102 is highest allowed instance\n   my_accounts.load( fc::raw::pack( test_account ) );\n   BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name );\n\n   // direct.next is now 103, but index sequence counter is 0\n   my_accounts.create( [] ( object& o ) {\n       account_object& acct = dynamic_cast< account_object& >( o );\n       BOOST_CHECK_EQUAL( 0u, acct.id.instance() );\n       acct.name = \"account0\";\n   } );\n\n   test_account.id = account_id_type(50);\n   test_account.name = \"account50\";\n   my_accounts.load( fc::raw::pack( test_account ) );\n\n   // can handle nested modification\n   my_accounts.modify( direct.get( account_id_type(0) ), [&direct,&my_accounts] ( object& outer ) {\n      account_object& _outer = dynamic_cast< account_object& >( outer );\n      my_accounts.modify( direct.get( account_id_type(50) ), [] ( object& inner ) {\n         account_object& _inner = dynamic_cast< account_object& >( inner );\n         _inner.referrer = account_id_type(102);\n      });\n      _outer.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n   });\n\n   // direct.next is still 103, so 204 is not allowed\n   test_account.id = account_id_type(204);\n   test_account.name = \"account204\";\n   GRAPHENE_REQUIRE_THROW( my_accounts.load( fc::raw::pack( test_account ) ), fc::assert_exception );\n   // This is actually undefined behaviour. The object has been inserted into\n   // the primary index, but the secondary has refused to insert it!\n   BOOST_CHECK_EQUAL( 5u, my_accounts.indices().size() );\n\n   uint32_t count = 0;\n   for( uint32_t i = 0; i < 250; i++ )\n   {\n      const account_object* aptr = dynamic_cast< const account_object* >( my_accounts.find( account_id_type( i ) ) );\n      if( aptr )\n      {\n         count++;\n         BOOST_CHECK( aptr->id.instance() == 0 || aptr->id.instance() == 1\n                      || aptr->id.instance() == 50 || aptr->id.instance() == 102 );\n         BOOST_CHECK_EQUAL( i, aptr->id.instance() );\n         BOOST_CHECK_EQUAL( \"account\" + std::to_string( i ), aptr->name );\n      }\n   }\n   BOOST_CHECK_EQUAL( count, my_accounts.indices().size() - 1 );\n\n   GRAPHENE_REQUIRE_THROW( my_accounts.modify( direct.get( account_id_type( 1 ) ), [] ( object& acct ) {\n      acct.id = account_id_type(2);\n   }), fc::assert_exception );\n   // This is actually undefined behaviour. The object has been modified, but\n   // but the secondary has not updated its representation\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( required_approval_index_test ) // see https://github.com/bitshares/bitshares-core/issues/1719\n{ try {\n   ACTORS( (alice)(bob)(charlie)(agnetha)(benny)(carlos) );\n\n   database db1;\n   db1.initialize_indexes();\n   const auto& required_approvals = db1.add_secondary_index<primary_index<proposal_index>, required_approval_index>()\n                                       ->_account_to_proposals;\n\n   // Create a proposal\n   const auto& prop = db1.create<proposal_object>( [this,alice_id,agnetha_id]( object& o ) {\n      proposal_object& prop = static_cast<proposal_object&>(o);\n      prop.proposer = committee_account;\n      prop.required_active_approvals.insert( alice_id );\n      prop.required_owner_approvals.insert( agnetha_id );\n   });\n\n   BOOST_CHECK_EQUAL( 2u, required_approvals.size() );\n   BOOST_REQUIRE( required_approvals.find( alice_id )   != required_approvals.end() );\n   BOOST_REQUIRE( required_approvals.find( agnetha_id ) != required_approvals.end() );\n   BOOST_CHECK_EQUAL( 1u, required_approvals.find(alice_id)->second.size() );\n   BOOST_CHECK_EQUAL( 1u, required_approvals.find(agnetha_id)->second.size() );\n\n   // add approvals\n   db1.modify( prop, [bob_id,benny_id]( object& o ) {\n      proposal_object& prop = static_cast<proposal_object&>(o);\n      prop.available_active_approvals.insert( bob_id );\n      prop.available_owner_approvals.insert( benny_id );\n   });\n\n   BOOST_CHECK_EQUAL( 4u, required_approvals.size() );\n   BOOST_REQUIRE( required_approvals.find( bob_id )   != required_approvals.end() );\n   BOOST_REQUIRE( required_approvals.find( benny_id ) != required_approvals.end() );\n   BOOST_CHECK_EQUAL( 1u, required_approvals.find(bob_id)->second.size() );\n   BOOST_CHECK_EQUAL( 1u, required_approvals.find(benny_id)->second.size() );\n\n   // remove approvals + add others\n   db1.modify( prop, [bob_id,charlie_id,benny_id,carlos_id]( object& o ) {\n      proposal_object& prop = static_cast<proposal_object&>(o);\n      prop.available_active_approvals.insert( charlie_id );\n      prop.available_owner_approvals.insert( carlos_id );\n      prop.available_active_approvals.erase( bob_id );\n      prop.available_owner_approvals.erase( benny_id );\n   });\n\n   BOOST_CHECK_EQUAL( 4u, required_approvals.size() );\n   BOOST_REQUIRE( required_approvals.find( charlie_id ) != required_approvals.end() );\n   BOOST_REQUIRE( required_approvals.find( carlos_id )  != required_approvals.end() );\n   BOOST_CHECK_EQUAL( 1u, required_approvals.find(charlie_id)->second.size() );\n   BOOST_CHECK_EQUAL( 1u, required_approvals.find(carlos_id)->second.size() );\n\n   // simulate save/restore\n   std::vector<char> serialized = fc::raw::pack( prop );\n   database db2;\n   db2.initialize_indexes();\n   const auto& reloaded_proposals = db2.get_index_type< primary_index< proposal_index > >();\n   const auto& reloaded_approvals = db2.add_secondary_index<primary_index<proposal_index>, required_approval_index>()\n                                       ->_account_to_proposals;\n   const_cast< primary_index< proposal_index >& >( reloaded_proposals ).load( serialized );\n   const auto& prop2 = *reloaded_proposals.indices().begin();\n\n   BOOST_CHECK_EQUAL( 4u, reloaded_approvals.size() );\n   BOOST_REQUIRE( reloaded_approvals.find( charlie_id ) != reloaded_approvals.end() );\n   BOOST_REQUIRE( reloaded_approvals.find( carlos_id )  != reloaded_approvals.end() );\n   BOOST_CHECK_EQUAL( 1u, reloaded_approvals.find(charlie_id)->second.size() );\n   BOOST_CHECK_EQUAL( 1u, reloaded_approvals.find(carlos_id)->second.size() );\n\n   db2.modify( prop2, []( object& o ) {\n      proposal_object& prop = static_cast<proposal_object&>(o);\n      prop.available_active_approvals.clear();\n      prop.available_owner_approvals.clear();\n   });\n\n   BOOST_CHECK_EQUAL( 2u, reloaded_approvals.size() );\n   BOOST_REQUIRE( reloaded_approvals.find( alice_id )   != reloaded_approvals.end() );\n   BOOST_REQUIRE( reloaded_approvals.find( agnetha_id ) != reloaded_approvals.end() );\n\n   db2.remove( prop2 );\n\n   BOOST_CHECK_EQUAL( 0u, reloaded_approvals.size() );\n\n   db1.remove( prop );\n\n   BOOST_CHECK_EQUAL( 0u, required_approvals.size() );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/fee_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/fba_accumulator_id.hpp>\n\n#include <graphene/chain/fba_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/exceptions.hpp>\n\n#include <fc/uint128.hpp>\n\n#include <boost/test/unit_test.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( fee_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( nonzero_fee_test )\n{\n   try\n   {\n      ACTORS((alice)(bob));\n\n      const share_type prec = asset::scaled_precision( asset_id_type()(db).precision );\n\n      // Return number of core shares (times precision)\n      auto _core = [&]( int64_t x ) -> asset\n      {  return asset( x*prec );    };\n\n      transfer( committee_account, alice_id, _core(1000000) );\n\n      // make sure the database requires our fee to be nonzero\n      enable_fees();\n\n      signed_transaction tx;\n      transfer_operation xfer_op;\n      xfer_op.from = alice_id;\n      xfer_op.to = bob_id;\n      xfer_op.amount = _core(1000);\n      xfer_op.fee = _core(0);\n      tx.operations.push_back( xfer_op );\n      set_expiration( db, tx );\n      sign( tx, alice_private_key );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), insufficient_fee );\n   }\n   catch( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(asset_claim_fees_test)\n{\n   try\n   {\n      ACTORS((alice)(bob)(izzy)(jill));\n      // Izzy issues asset to Alice\n      // Jill issues asset to Bob\n      // Alice and Bob trade in the market and pay fees\n      // Verify that Izzy and Jill can claim the fees\n\n      const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision );\n\n      // Return number of core shares (times precision)\n      auto _core = [&]( int64_t x ) -> asset\n      {  return asset( x*core_prec );    };\n\n      transfer( committee_account, alice_id, _core(1000000) );\n      transfer( committee_account,   bob_id, _core(1000000) );\n      transfer( committee_account,  izzy_id, _core(1000000) );\n      transfer( committee_account,  jill_id, _core(1000000) );\n\n      asset_id_type izzycoin_id = create_bitasset( \"IZZYCOIN\", izzy_id,   GRAPHENE_1_PERCENT, charge_market_fee ).id;\n      asset_id_type jillcoin_id = create_bitasset( \"JILLCOIN\", jill_id, 2*GRAPHENE_1_PERCENT, charge_market_fee ).id;\n\n      const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision );\n      const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision );\n\n      auto _izzy = [&]( int64_t x ) -> asset\n      {   return asset( x*izzy_prec, izzycoin_id );   };\n      auto _jill = [&]( int64_t x ) -> asset\n      {   return asset( x*jill_prec, jillcoin_id );   };\n\n      update_feed_producers( izzycoin_id(db), { izzy_id } );\n      update_feed_producers( jillcoin_id(db), { jill_id } );\n\n      const asset izzy_satoshi = asset(1, izzycoin_id);\n      const asset jill_satoshi = asset(1, jillcoin_id);\n\n      // Izzycoin is worth 100 BTS\n      price_feed feed;\n      feed.settlement_price = price( _izzy(1), _core(100) );\n      feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      publish_feed( izzycoin_id(db), izzy, feed );\n\n      // Jillcoin is worth 30 BTS\n      feed.settlement_price = price( _jill(1), _core(30) );\n      feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      publish_feed( jillcoin_id(db), jill, feed );\n\n      enable_fees();\n\n      // Alice and Bob create some coins\n      borrow( alice_id, _izzy( 200), _core( 60000) );\n      borrow(   bob_id, _jill(2000), _core(180000) );\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice_id, _izzy(100), _jill(300) );   // Alice is willing to sell her Izzy's for 3 Jill\n      create_sell_order(   bob_id, _jill(700), _izzy(200) );   // Bob is buying up to 200 Izzy's for up to 3.5 Jill\n\n      // 100 Izzys and 300 Jills are matched, so the fees should be\n      //   1 Izzy (1%) and 6 Jill (2%).\n\n      auto claim_fees = [&]( account_id_type issuer, asset amount_to_claim )\n      {\n         asset_claim_fees_operation claim_op;\n         claim_op.issuer = issuer;\n         claim_op.amount_to_claim = amount_to_claim;\n         signed_transaction tx;\n         tx.operations.push_back( claim_op );\n         db.current_fee_schedule().set_fee( tx.operations.back() );\n         set_expiration( db, tx );\n         fc::ecc::private_key   my_pk = (issuer == izzy_id) ? izzy_private_key : jill_private_key;\n         fc::ecc::private_key your_pk = (issuer == izzy_id) ? jill_private_key : izzy_private_key;\n         sign( tx, your_pk );\n         GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception );\n         tx.clear_signatures();\n         sign( tx, my_pk );\n         PUSH_TX( db, tx );\n      };\n\n      {\n         const asset_object& izzycoin = izzycoin_id(db);\n         const asset_object& jillcoin = jillcoin_id(db);\n\n         //wdump( (izzycoin)(izzycoin.dynamic_asset_data_id(db))((*izzycoin.bitasset_data_id)(db)) );\n         //wdump( (jillcoin)(jillcoin.dynamic_asset_data_id(db))((*jillcoin.bitasset_data_id)(db)) );\n\n         // check the correct amount of fees has been awarded\n         BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(1).amount );\n         BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(6).amount );\n\n      }\n\n      {\n         const asset_object& izzycoin = izzycoin_id(db);\n         const asset_object& jillcoin = jillcoin_id(db);\n\n         // can't claim more than balance\n         GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) + izzy_satoshi ), fc::exception );\n         GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(6) + jill_satoshi ), fc::exception );\n\n         // can't claim asset that doesn't belong to you\n         GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, izzy_satoshi ), fc::exception );\n         GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, jill_satoshi ), fc::exception );\n\n         // can claim asset in one go\n         claim_fees( izzy_id, _izzy(1) );\n         GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, izzy_satoshi ), fc::exception );\n         BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(0).amount );\n\n         // can claim in multiple goes\n         claim_fees( jill_id, _jill(4) );\n         BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(2).amount );\n         GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(2) + jill_satoshi ), fc::exception );\n         claim_fees( jill_id, _jill(2) );\n         BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(0).amount );\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(asset_claim_pool_test)\n{\n    try\n    {\n        ACTORS((alice)(bob));\n        // Alice and Bob create some user issued assets\n        // Alice deposits BTS to the fee pool\n        // Alice claimes fee pool of her asset and can't claim pool of Bob's asset\n\n        const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision );\n\n        // return number of core shares (times precision)\n        auto _core = [&core_prec]( int64_t x ) -> asset\n        {  return asset( x*core_prec );    };\n\n        const asset_object& alicecoin = create_user_issued_asset( \"ALICECOIN\", alice,  0 );\n        const asset_object& aliceusd = create_user_issued_asset( \"ALICEUSD\", alice, 0 );\n\n        asset_id_type alicecoin_id = alicecoin.id;\n        asset_id_type aliceusd_id = aliceusd.id;\n        asset_id_type bobcoin_id = create_user_issued_asset( \"BOBCOIN\", bob, 0).id;\n\n        // prepare users' balance\n        issue_uia( alice, aliceusd.amount( 20000000 ) );\n        issue_uia( alice, alicecoin.amount( 10000000 ) );\n\n        transfer( committee_account, alice_id, _core(1000) );\n        transfer( committee_account, bob_id, _core(1000) );\n\n        enable_fees();\n\n        auto claim_pool = [&]( const account_id_type issuer, const asset_id_type asset_to_claim,\n                              const asset& amount_to_fund, const asset_object& fee_asset  )\n        {\n            asset_claim_pool_operation claim_op;\n            claim_op.issuer = issuer;\n            claim_op.asset_id = asset_to_claim;\n            claim_op.amount_to_claim = amount_to_fund;\n\n            signed_transaction tx;\n            tx.operations.push_back( claim_op );\n            db.current_fee_schedule().set_fee( tx.operations.back(), fee_asset.options.core_exchange_rate );\n            set_expiration( db, tx );\n            sign( tx, alice_private_key );\n            PUSH_TX( db, tx );\n\n        };\n\n        auto claim_pool_proposal = [&]( const account_id_type issuer, const asset_id_type asset_to_claim,\n                                        const asset& amount_to_fund, const asset_object& fee_asset  )\n        {\n            asset_claim_pool_operation claim_op;\n            claim_op.issuer = issuer;\n            claim_op.asset_id = asset_to_claim;\n            claim_op.amount_to_claim = amount_to_fund;\n\n            const auto& curfees = db.get_global_properties().parameters.get_current_fees();\n            const auto& proposal_create_fees = curfees.get<proposal_create_operation>();\n            proposal_create_operation prop;\n            prop.fee_paying_account = alice_id;\n            prop.proposed_ops.emplace_back( claim_op );\n            prop.expiration_time =  db.head_block_time() + fc::days(1);\n            prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n\n            signed_transaction tx;\n            tx.operations.push_back( prop );\n            db.current_fee_schedule().set_fee( tx.operations.back(), fee_asset.options.core_exchange_rate );\n            set_expiration( db, tx );\n            sign( tx, alice_private_key );\n            PUSH_TX( db, tx );\n\n        };\n\n        // deposit 100 BTS to the fee pool of ALICEUSD asset\n        fund_fee_pool( alice_id(db), aliceusd_id(db), _core(100).amount );\n\n        // New reference for core_asset after having produced blocks\n        const asset_object& core_asset_hf = asset_id_type()(db);\n\n        // can't claim pool because it is empty\n        GRAPHENE_REQUIRE_THROW( claim_pool( alice_id, alicecoin_id, _core(1), core_asset_hf), fc::exception );\n\n        // deposit 300 BTS to the fee pool of ALICECOIN asset\n        fund_fee_pool( alice_id(db), alicecoin_id(db), _core(300).amount );\n\n        // Test amount of CORE in fee pools\n        BOOST_CHECK( alicecoin_id(db).dynamic_asset_data_id(db).fee_pool == _core(300).amount );\n        BOOST_CHECK( aliceusd_id(db).dynamic_asset_data_id(db).fee_pool == _core(100).amount );\n\n        // can't claim pool of an asset that doesn't belong to you\n        GRAPHENE_REQUIRE_THROW( claim_pool( alice_id, bobcoin_id, _core(200), core_asset_hf), fc::exception );\n\n        // can't claim more than is available in the fee pool\n        GRAPHENE_REQUIRE_THROW( claim_pool( alice_id, alicecoin_id, _core(400), core_asset_hf ), fc::exception );\n\n        // can't pay fee in the same asset whose pool is being drained\n        GRAPHENE_REQUIRE_THROW( claim_pool( alice_id, alicecoin_id, _core(200), alicecoin_id(db) ), fc::exception );\n\n        // can claim BTS back from the fee pool\n        claim_pool( alice_id, alicecoin_id, _core(200), core_asset_hf );\n        BOOST_CHECK( alicecoin_id(db).dynamic_asset_data_id(db).fee_pool == _core(100).amount );\n\n        // can pay fee in the asset other than the one whose pool is being drained\n        share_type balance_before_claim = get_balance( alice_id, asset_id_type() );\n        claim_pool( alice_id, alicecoin_id, _core(100), aliceusd_id(db) );\n        BOOST_CHECK( alicecoin_id(db).dynamic_asset_data_id(db).fee_pool == _core(0).amount );\n\n        //check balance after claiming pool\n        share_type current_balance = get_balance( alice_id, asset_id_type() );\n        BOOST_CHECK( balance_before_claim + _core(100).amount == current_balance );\n\n        // can create a proposal to claim claim pool after hard fork\n        claim_pool_proposal( alice_id, aliceusd_id, _core(1), core_asset_hf);\n    }\n    FC_LOG_AND_RETHROW()\n}\n\n///////////////////////////////////////////////////////////////\n// cashback_test infrastructure                              //\n///////////////////////////////////////////////////////////////\n\n#define CHECK_BALANCE( actor_name, amount ) \\\n   BOOST_CHECK_EQUAL( get_balance( actor_name ## _id, asset_id_type() ), amount )\n\n#define CHECK_VESTED_CASHBACK( actor_name, amount ) \\\n   BOOST_CHECK_EQUAL( actor_name ## _id(db).statistics(db).pending_vested_fees.value, amount )\n\n#define CHECK_UNVESTED_CASHBACK( actor_name, amount ) \\\n   BOOST_CHECK_EQUAL( actor_name ## _id(db).statistics(db).pending_fees.value, amount )\n\n#define GET_CASHBACK_BALANCE( account ) \\\n   ( (account.cashback_vb.valid()) \\\n   ? account.cashback_balance(db).balance.amount.value \\\n   : 0 )\n\n#define CHECK_CASHBACK_VBO( actor_name, _amount ) \\\n   BOOST_CHECK_EQUAL( GET_CASHBACK_BALANCE( actor_name ## _id(db) ), _amount )\n\n#define P100 GRAPHENE_100_PERCENT\n#define P1 GRAPHENE_1_PERCENT\n\nuint64_t pct( uint64_t percentage, uint64_t val )\n{\n   fc::uint128_t x = percentage;\n   x *= val;\n   x /= GRAPHENE_100_PERCENT;\n   return static_cast<uint64_t>(x);\n}\n\nuint64_t pct( uint64_t percentage0, uint64_t percentage1, uint64_t val )\n{\n   return pct( percentage1, pct( percentage0, val ) );\n}\n\nuint64_t pct( uint64_t percentage0, uint64_t percentage1, uint64_t percentage2, uint64_t val )\n{\n   return pct( percentage2, pct( percentage1, pct( percentage0, val ) ) );\n}\n\nstruct actor_audit\n{\n   int64_t b0 = 0;      // starting balance parameter\n   int64_t bal = 0;     // balance should be this\n   int64_t ubal = 0;    // unvested balance (in VBO) should be this\n   int64_t ucb = 0;     // unvested cashback in account_statistics should be this\n   int64_t vcb = 0;     // vested cashback in account_statistics should be this\n   int64_t ref_pct = 0; // referrer percentage should be this\n};\n\nBOOST_AUTO_TEST_CASE( cashback_test )\n{ try {\n   /*                        Account Structure used in this test                         *\n    *                                                                                    *\n    *               /-----------------\\       /-------------------\\                      *\n    *               | life (Lifetime) |       |  rog (Lifetime)   |                      *\n    *               \\-----------------/       \\-------------------/                      *\n    *                  | Ref&Reg    | Refers     | Registers  | Registers                *\n    *                  |            | 75         | 25         |                          *\n    *                  v            v            v            |                          *\n    *  /----------------\\         /----------------\\          |                          *\n    *  |  ann (Annual)  |         |  dumy (basic)  |          |                          *\n    *  \\----------------/         \\----------------/          |-------------.            *\n    * 80 | Refers      L--------------------------------.     |             |            *\n    *    v                     Refers                80 v     v 20          |            *\n    *  /----------------\\                         /----------------\\        |            *\n    *  |  scud (basic)  |<------------------------|  stud (basic)  |        |            *\n    *  \\----------------/ 20   Registers          | (Upgrades to   |        | 5          *\n    *                                             |   Lifetime)    |        v            *\n    *                                             \\----------------/   /--------------\\  *\n    *                                                         L------->| pleb (Basic) |  *\n    *                                                       95 Refers  \\--------------/  *\n    *                                                                                    *\n    * Fee distribution chains (80-20 referral/net split, 50-30 referrer/LTM split)       *\n    * life : 80% -> life, 20% -> net                                                     *\n    * rog: 80% -> rog, 20% -> net                                                        *\n    * ann (before upg): 80% -> life, 20% -> net                                          *\n    * ann (after upg): 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% -> net                   *\n    * stud (before upg): 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% * 80% -> rog,          *\n    *                    20% -> net                                                      *\n    * stud (after upg): 80% -> stud, 20% -> net                                          *\n    * dumy : 75% * 80% -> life, 25% * 80% -> rog, 20% -> net                             *\n    * scud : 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% * 80% -> stud, 20% -> net          *\n    * pleb : 95% * 80% -> stud, 5% * 80% -> rog, 20% -> net                              *\n    */\n\n   BOOST_TEST_MESSAGE(\"Creating actors\");\n\n   ACTOR(life);\n   ACTOR(rog);\n   PREP_ACTOR(ann);\n   PREP_ACTOR(scud);\n   PREP_ACTOR(dumy);\n   PREP_ACTOR(stud);\n   PREP_ACTOR(pleb);\n   // use ##_public_key vars to silence unused variable warning\n   BOOST_CHECK_GT(ann_public_key.key_data.size(), 0u);\n   BOOST_CHECK_GT(scud_public_key.key_data.size(), 0u);\n   BOOST_CHECK_GT(dumy_public_key.key_data.size(), 0u);\n   BOOST_CHECK_GT(stud_public_key.key_data.size(), 0u);\n   BOOST_CHECK_GT(pleb_public_key.key_data.size(), 0u);\n\n   account_id_type ann_id, scud_id, dumy_id, stud_id, pleb_id;\n   actor_audit alife, arog, aann, ascud, adumy, astud, apleb;\n\n   alife.b0 = 100000000;\n   arog.b0 = 100000000;\n   aann.b0 = 1000000;\n   astud.b0 = 1000000;\n   astud.ref_pct = 80 * GRAPHENE_1_PERCENT;\n   ascud.ref_pct = 80 * GRAPHENE_1_PERCENT;\n   adumy.ref_pct = 75 * GRAPHENE_1_PERCENT;\n   apleb.ref_pct = 95 * GRAPHENE_1_PERCENT;\n\n   transfer(account_id_type(), life_id, asset(alife.b0));\n   alife.bal += alife.b0;\n   transfer(account_id_type(), rog_id, asset(arog.b0));\n   arog.bal += arog.b0;\n   upgrade_to_lifetime_member(life_id);\n   upgrade_to_lifetime_member(rog_id);\n\n   BOOST_TEST_MESSAGE(\"Enable fees\");\n   const auto& fees = db.get_global_properties().parameters.get_current_fees();\n\n#define CustomRegisterActor(actor_name, registrar_name, referrer_name, referrer_rate) \\\n   { \\\n      account_create_operation op; \\\n      op.registrar = registrar_name ## _id; \\\n      op.referrer = referrer_name ## _id; \\\n      op.referrer_percent = referrer_rate*GRAPHENE_1_PERCENT; \\\n      op.name = BOOST_PP_STRINGIZE(actor_name); \\\n      op.options.memo_key = actor_name ## _private_key.get_public_key(); \\\n      op.active = authority(1, public_key_type(actor_name ## _private_key.get_public_key()), 1); \\\n      op.owner = op.active; \\\n      op.fee = fees.calculate_fee(op); \\\n      trx.operations = {op}; \\\n      sign( trx,  registrar_name ## _private_key ); \\\n      actor_name ## _id = PUSH_TX( db, trx ).operation_results.front().get<object_id_type>(); \\\n      trx.clear(); \\\n   }\n#define CustomAuditActor(actor_name)                                \\\n   if( actor_name ## _id != account_id_type() )                     \\\n   {                                                                \\\n      CHECK_BALANCE( actor_name, a ## actor_name.bal );             \\\n      CHECK_VESTED_CASHBACK( actor_name, a ## actor_name.vcb );     \\\n      CHECK_UNVESTED_CASHBACK( actor_name, a ## actor_name.ucb );   \\\n      CHECK_CASHBACK_VBO( actor_name, a ## actor_name.ubal );       \\\n   }\n\n#define CustomAudit()                                \\\n   {                                                 \\\n      CustomAuditActor( life );                      \\\n      CustomAuditActor( rog );                       \\\n      CustomAuditActor( ann );                       \\\n      CustomAuditActor( stud );                      \\\n      CustomAuditActor( dumy );                      \\\n      CustomAuditActor( scud );                      \\\n      CustomAuditActor( pleb );                      \\\n   }\n\n   int64_t reg_fee    = fees.get< account_create_operation >().premium_fee;\n   int64_t xfer_fee   = fees.get< transfer_operation >().fee;\n   int64_t upg_an_fee = fees.get< account_upgrade_operation >().membership_annual_fee;\n   int64_t upg_lt_fee = fees.get< account_upgrade_operation >().membership_lifetime_fee;\n   // all percentages here are cut from whole pie!\n   uint64_t network_pct = 20 * P1;\n   uint64_t lt_pct = 375 * P100 / 1000;\n\n   BOOST_TEST_MESSAGE(\"Register and upgrade Ann\");\n   {\n      CustomRegisterActor(ann, life, life, 75);\n      alife.vcb += reg_fee; alife.bal += -reg_fee;\n      CustomAudit();\n\n      transfer(life_id, ann_id, asset(aann.b0));\n      alife.vcb += xfer_fee; alife.bal += -xfer_fee -aann.b0; aann.bal += aann.b0;\n      CustomAudit();\n\n      upgrade_to_annual_member(ann_id);\n      aann.ucb += upg_an_fee; aann.bal += -upg_an_fee;\n\n      // audit distribution of fees from Ann\n      alife.ubal += pct( P100-network_pct, aann.ucb );\n      alife.bal  += pct( P100-network_pct, aann.vcb );\n      aann.ucb = 0; aann.vcb = 0;\n      CustomAudit();\n   }\n\n   BOOST_TEST_MESSAGE(\"Register dumy and stud\");\n   CustomRegisterActor(dumy, rog, life, 75);\n   arog.vcb += reg_fee; arog.bal += -reg_fee;\n   CustomAudit();\n\n   CustomRegisterActor(stud, rog, ann, 80);\n   arog.vcb += reg_fee; arog.bal += -reg_fee;\n   CustomAudit();\n\n   BOOST_TEST_MESSAGE(\"Upgrade stud to lifetime member\");\n\n   transfer(life_id, stud_id, asset(astud.b0));\n   alife.vcb += xfer_fee; alife.bal += -astud.b0 -xfer_fee; astud.bal += astud.b0;\n   CustomAudit();\n\n   upgrade_to_lifetime_member(stud_id);\n   astud.ucb += upg_lt_fee; astud.bal -= upg_lt_fee;\n\n/*\nnetwork_cut:   20000\nreferrer_cut:  40000 -> ann\nregistrar_cut: 10000 -> rog\nlifetime_cut:  30000 -> life\n\nNET : net\nLTM : net' ltm\nREF : net' ltm' ref\nREG : net' ltm' ref'\n*/\n\n   // audit distribution of fees from stud\n   alife.ubal += pct( P100-network_pct,      lt_pct,                     astud.ucb );\n   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      astud.ref_pct, astud.ucb );\n   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-astud.ref_pct, astud.ucb );\n   astud.ucb  = 0;\n   CustomAudit();\n\n   BOOST_TEST_MESSAGE(\"Register pleb and scud\");\n\n   CustomRegisterActor(pleb, rog, stud, 95);\n   arog.vcb += reg_fee; arog.bal += -reg_fee;\n   CustomAudit();\n\n   CustomRegisterActor(scud, stud, ann, 80);\n   astud.vcb += reg_fee; astud.bal += -reg_fee;\n   CustomAudit();\n\n   generate_block();\n\n   BOOST_TEST_MESSAGE(\"Wait for maintenance interval\");\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   // audit distribution of fees from life\n   alife.ubal += pct( P100-network_pct, alife.ucb +alife.vcb );\n   alife.ucb = 0; alife.vcb = 0;\n\n   // audit distribution of fees from rog\n   arog.ubal += pct( P100-network_pct, arog.ucb + arog.vcb );\n   arog.ucb = 0; arog.vcb = 0;\n\n   // audit distribution of fees from ann\n   alife.ubal += pct( P100-network_pct,      lt_pct,                    aann.ucb+aann.vcb );\n   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      aann.ref_pct, aann.ucb+aann.vcb );\n   alife.ubal += pct( P100-network_pct, P100-lt_pct, P100-aann.ref_pct, aann.ucb+aann.vcb );\n   aann.ucb = 0; aann.vcb = 0;\n\n   // audit distribution of fees from stud\n   astud.ubal += pct( P100-network_pct,                                  astud.ucb+astud.vcb );\n   astud.ucb = 0; astud.vcb = 0;\n\n   // audit distribution of fees from dumy\n   alife.ubal += pct( P100-network_pct,      lt_pct,                     adumy.ucb+adumy.vcb );\n   alife.ubal += pct( P100-network_pct, P100-lt_pct,      adumy.ref_pct, adumy.ucb+adumy.vcb );\n   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-adumy.ref_pct, adumy.ucb+adumy.vcb );\n   adumy.ucb = 0; adumy.vcb = 0;\n\n   // audit distribution of fees from scud\n   alife.ubal += pct( P100-network_pct,      lt_pct,                     ascud.ucb+ascud.vcb );\n   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      ascud.ref_pct, ascud.ucb+ascud.vcb );\n   astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb );\n   ascud.ucb = 0; ascud.vcb = 0;\n\n   // audit distribution of fees from pleb\n   astud.ubal += pct( P100-network_pct,      lt_pct,                     apleb.ucb+apleb.vcb );\n   astud.ubal += pct( P100-network_pct, P100-lt_pct,      apleb.ref_pct, apleb.ucb+apleb.vcb );\n   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-apleb.ref_pct, apleb.ucb+apleb.vcb );\n   apleb.ucb = 0; apleb.vcb = 0;\n\n   CustomAudit();\n\n   BOOST_TEST_MESSAGE(\"Doing some transfers\");\n\n   transfer(stud_id, scud_id, asset(500000));\n   astud.bal += -500000-xfer_fee; astud.vcb += xfer_fee; ascud.bal += 500000;\n   CustomAudit();\n\n   transfer(scud_id, pleb_id, asset(400000));\n   ascud.bal += -400000-xfer_fee; ascud.vcb += xfer_fee; apleb.bal += 400000;\n   CustomAudit();\n\n   transfer(pleb_id, dumy_id, asset(300000));\n   apleb.bal += -300000-xfer_fee; apleb.vcb += xfer_fee; adumy.bal += 300000;\n   CustomAudit();\n\n   transfer(dumy_id, rog_id, asset(200000));\n   adumy.bal += -200000-xfer_fee; adumy.vcb += xfer_fee; arog.bal += 200000;\n   CustomAudit();\n\n   BOOST_TEST_MESSAGE(\"Waiting for maintenance time\");\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   // audit distribution of fees from life\n   alife.ubal += pct( P100-network_pct, alife.ucb +alife.vcb );\n   alife.ucb = 0; alife.vcb = 0;\n\n   // audit distribution of fees from rog\n   arog.ubal += pct( P100-network_pct, arog.ucb + arog.vcb );\n   arog.ucb = 0; arog.vcb = 0;\n\n   // audit distribution of fees from ann\n   alife.ubal += pct( P100-network_pct,      lt_pct,                    aann.ucb+aann.vcb );\n   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      aann.ref_pct, aann.ucb+aann.vcb );\n   alife.ubal += pct( P100-network_pct, P100-lt_pct, P100-aann.ref_pct, aann.ucb+aann.vcb );\n   aann.ucb = 0; aann.vcb = 0;\n\n   // audit distribution of fees from stud\n   astud.ubal += pct( P100-network_pct,                                  astud.ucb+astud.vcb );\n   astud.ucb = 0; astud.vcb = 0;\n\n   // audit distribution of fees from dumy\n   alife.ubal += pct( P100-network_pct,      lt_pct,                     adumy.ucb+adumy.vcb );\n   alife.ubal += pct( P100-network_pct, P100-lt_pct,      adumy.ref_pct, adumy.ucb+adumy.vcb );\n   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-adumy.ref_pct, adumy.ucb+adumy.vcb );\n   adumy.ucb = 0; adumy.vcb = 0;\n\n   // audit distribution of fees from scud\n   alife.ubal += pct( P100-network_pct,      lt_pct,                     ascud.ucb+ascud.vcb );\n   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      ascud.ref_pct, ascud.ucb+ascud.vcb );\n   astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb );\n   ascud.ucb = 0; ascud.vcb = 0;\n\n   // audit distribution of fees from pleb\n   astud.ubal += pct( P100-network_pct,      lt_pct,                     apleb.ucb+apleb.vcb );\n   astud.ubal += pct( P100-network_pct, P100-lt_pct,      apleb.ref_pct, apleb.ucb+apleb.vcb );\n   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-apleb.ref_pct, apleb.ucb+apleb.vcb );\n   apleb.ucb = 0; apleb.vcb = 0;\n\n   CustomAudit();\n\n   BOOST_TEST_MESSAGE(\"Waiting for annual membership to expire\");\n\n   generate_blocks(ann_id(db).membership_expiration_date);\n   generate_block();\n\n   BOOST_TEST_MESSAGE(\"Transferring from scud to pleb\");\n\n   //ann's membership has expired, so scud's fee should go up to life instead.\n   transfer(scud_id, pleb_id, asset(10));\n   ascud.bal += -10-xfer_fee; ascud.vcb += xfer_fee; apleb.bal += 10;\n   CustomAudit();\n\n   BOOST_TEST_MESSAGE(\"Waiting for maint interval\");\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   // audit distribution of fees from scud\n   alife.ubal += pct( P100-network_pct,      lt_pct,                     ascud.ucb+ascud.vcb );\n   alife.ubal += pct( P100-network_pct, P100-lt_pct,      ascud.ref_pct, ascud.ucb+ascud.vcb );\n   astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb );\n   ascud.ucb = 0; ascud.vcb = 0;\n\n   CustomAudit();\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( account_create_fee_scaling )\n{ try {\n   auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale;\n   db.modify(global_property_id_type()(db), [](global_property_object& gpo)\n   {\n      gpo.parameters.get_mutable_fees() = fee_schedule::get_default();\n      gpo.parameters.get_mutable_fees().get<account_create_operation>().basic_fee = 1;\n   });\n\n   for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i )\n   {\n      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.get_current_fees().get<account_create_operation>().basic_fee, 1u);\n      create_account(\"shill\" + fc::to_string(i));\n   }\n   for( int i = 0; i < accounts_per_scale; ++i )\n   {\n      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.get_current_fees().get<account_create_operation>().basic_fee, 16u);\n      create_account(\"moreshills\" + fc::to_string(i));\n   }\n   for( int i = 0; i < accounts_per_scale; ++i )\n   {\n      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.get_current_fees().get<account_create_operation>().basic_fee, 256u);\n      create_account(\"moarshills\" + fc::to_string(i));\n   }\n   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.get_current_fees().get<account_create_operation>().basic_fee, 4096u);\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.get_current_fees().get<account_create_operation>().basic_fee, 1u);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( fee_refund_test )\n{\n   try\n   {\n      ACTORS((alice)(bob)(izzy));\n\n      int64_t alice_b0 = 1000000, bob_b0 = 1000000;\n\n      transfer( account_id_type(), alice_id, asset(alice_b0) );\n      transfer( account_id_type(), bob_id, asset(bob_b0) );\n\n      asset_id_type core_id = asset_id_type();\n      asset_id_type usd_id = create_user_issued_asset( \"IZZYUSD\", izzy_id(db), charge_market_fee ).id;\n      issue_uia( alice_id, asset( alice_b0, usd_id ) );\n      issue_uia( bob_id, asset( bob_b0, usd_id ) );\n\n      int64_t order_create_fee = 537;\n      int64_t order_cancel_fee = 129;\n\n      uint32_t skip = database::skip_witness_signature\n                    | database::skip_transaction_signatures\n                    | database::skip_transaction_dupe_check\n                    | database::skip_block_size_check\n                    | database::skip_tapos_check\n                    | database::skip_merkle_check\n                    ;\n\n      generate_block( skip );\n\n      for( int i=0; i<2; i++ )\n      {\n         if( i == 1 )\n         {\n            generate_blocks( HARDFORK_445_TIME, true, skip );\n            generate_block( skip );\n         }\n\n         // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation\n         // so we have to do it every time we stop generating/popping blocks and start doing tx's\n         enable_fees();\n         /*\n         change_fees({\n                       limit_order_create_operation::fee_parameters_type { order_create_fee },\n                       limit_order_cancel_operation::fee_parameters_type { order_cancel_fee }\n                     });\n         */\n         // C++ -- The above commented out statement doesn't work, I don't know why\n         // so we will use the following rather lengthy initialization instead\n         {\n            fee_parameters::flat_set_type new_fees;\n            {\n               limit_order_create_operation::fee_parameters_type create_fee_params;\n               create_fee_params.fee = order_create_fee;\n               new_fees.insert( create_fee_params );\n            }\n            {\n               limit_order_cancel_operation::fee_parameters_type cancel_fee_params;\n               cancel_fee_params.fee = order_cancel_fee;\n               new_fees.insert( cancel_fee_params );\n            }\n            change_fees( new_fees );\n         }\n\n         // Alice creates order\n         // Bob creates order which doesn't match\n\n         // AAAAGGHH create_sell_order reads trx.expiration #469\n         set_expiration( db, trx );\n\n         // Check non-overlapping\n\n         limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(1000, usd_id) )->id;\n         limit_order_id_type bo1_id = create_sell_order(   bob_id, asset(500, usd_id), asset(1000) )->id;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - 1000 - order_create_fee );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_b0 - order_create_fee );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_b0 - 500 );\n\n         // Bob cancels order\n         cancel_limit_order( bo1_id(db) );\n\n         int64_t cancel_net_fee;\n         if( db.head_block_time() > HARDFORK_445_TIME )\n            cancel_net_fee = order_cancel_fee;\n         else\n            cancel_net_fee = order_create_fee + order_cancel_fee;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - 1000 - order_create_fee );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_b0 - cancel_net_fee );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_b0 );\n\n         // Alice cancels order\n         cancel_limit_order( ao1_id(db) );\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_b0 - cancel_net_fee );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_b0 );\n\n         // Check partial fill\n         const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id) );\n         const limit_order_object* bo2 = create_sell_order(   bob_id, asset(100, usd_id), asset(500) );\n\n         BOOST_CHECK( ao2 != nullptr );\n         BOOST_CHECK( bo2 == nullptr );\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee - order_create_fee - 1000 );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 + 100 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ),   bob_b0 - cancel_net_fee - order_create_fee + 500 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ),   bob_b0 - 100 );\n\n         // cancel Alice order, show that entire deferred_fee was consumed by partial match\n         cancel_limit_order( *ao2 );\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee - order_create_fee - 500 - order_cancel_fee );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 + 100 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ),   bob_b0 - cancel_net_fee - order_create_fee + 500 );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ),   bob_b0 - 100 );\n\n         // TODO: Check multiple fill\n         // there really should be a test case involving Alice creating multiple orders matched by single Bob order\n         // but we'll save that for future cleanup\n\n         // undo above tx's and reset\n         generate_block( skip );\n         db.pop_block();\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( non_core_fee_refund_test )\n{\n   try\n   {\n      ACTORS((alice)(bob)(izzy));\n\n      int64_t alice_b0 = 1000000, bob_b0 = 1000000;\n      int64_t pool_0 = 100000, accum_0 = 0;\n\n      transfer( account_id_type(), alice_id, asset(alice_b0) );\n      transfer( account_id_type(), bob_id, asset(bob_b0) );\n\n      asset_id_type core_id = asset_id_type();\n      const auto& usd_obj = create_user_issued_asset( \"IZZYUSD\", izzy_id(db), charge_market_fee );\n      asset_id_type usd_id = usd_obj.id;\n      issue_uia( alice_id, asset( alice_b0, usd_id ) );\n      issue_uia( bob_id, asset( bob_b0, usd_id ) );\n\n      fund_fee_pool( committee_account( db ), usd_obj, pool_0 );\n\n      int64_t order_create_fee = 537;\n      int64_t order_cancel_fee = 129;\n\n      uint32_t skip = database::skip_witness_signature\n                    | database::skip_transaction_signatures\n                    | database::skip_transaction_dupe_check\n                    | database::skip_block_size_check\n                    | database::skip_tapos_check\n                    | database::skip_merkle_check\n                    ;\n\n      generate_block( skip );\n\n      fee_parameters::flat_set_type new_fees;\n      {\n         limit_order_create_operation::fee_parameters_type create_fee_params;\n         create_fee_params.fee = order_create_fee;\n         new_fees.insert( create_fee_params );\n      }\n      {\n         limit_order_cancel_operation::fee_parameters_type cancel_fee_params;\n         cancel_fee_params.fee = order_cancel_fee;\n         new_fees.insert( cancel_fee_params );\n      }\n      {\n         transfer_operation::fee_parameters_type transfer_fee_params;\n         transfer_fee_params.fee = 0;\n         transfer_fee_params.price_per_kbyte = 0;\n         new_fees.insert( transfer_fee_params );\n      }\n\n      for( int i=0; i<4; i++ )\n      {\n         bool expire_order = ( i % 2 != 0 );\n         bool before_hardfork_445 = ( i < 2 );\n         if( i == 2 )\n         {\n            generate_blocks( HARDFORK_445_TIME, true, skip );\n            generate_block( skip );\n         }\n\n         // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation\n         // so we have to do it every time we stop generating/popping blocks and start doing tx's\n         enable_fees();\n         change_fees( new_fees );\n\n         // AAAAGGHH create_sell_order reads trx.expiration #469\n         set_expiration( db, trx );\n\n         // prepare params\n         uint32_t blocks_generated = 0;\n         time_point_sec max_exp = time_point_sec::maximum();\n         time_point_sec exp = db.head_block_time(); // order will be accepted when pushing trx then expired at current block\n         price cer( asset(1), asset(1, usd_id) );\n         const auto* usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n\n         // balance data\n         int64_t alice_bc = alice_b0, bob_bc = bob_b0; // core balance\n         int64_t alice_bu = alice_b0, bob_bu = bob_b0; // usd balance\n         int64_t pool_b = pool_0, accum_b = accum_0;\n\n         // refund data\n         int64_t core_fee_refund_core;\n         int64_t core_fee_refund_usd;\n         int64_t usd_fee_refund_core;\n         int64_t usd_fee_refund_usd;\n         if( db.head_block_time() > HARDFORK_445_TIME )\n         {\n            core_fee_refund_core = order_create_fee;\n            core_fee_refund_usd = 0;\n            usd_fee_refund_core = order_create_fee;\n            usd_fee_refund_usd = 0;\n         }\n         else\n         {\n            core_fee_refund_core = 0;\n            core_fee_refund_usd = 0;\n            usd_fee_refund_core = 0;\n            usd_fee_refund_usd = 0;\n         }\n\n         // Check non-overlapping\n         // Alice creates order\n         // Bob creates order which doesn't match\n         limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(1000, usd_id) )->id;\n         limit_order_id_type bo1_id = create_sell_order(   bob_id, asset(500, usd_id), asset(1000), exp, cer )->id;\n\n         alice_bc -= order_create_fee;\n         alice_bc -= 1000;\n         bob_bu -= order_create_fee;\n         bob_bu -= 500;\n         pool_b -= order_create_fee;\n         accum_b += order_create_fee;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Bob cancels order\n         if( !expire_order )\n            cancel_limit_order( bo1_id(db) );\n         else\n         {\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n         if( !expire_order || !before_hardfork_445 )\n            bob_bc -= order_cancel_fee;\n         // else do nothing: before hard fork 445, no fee on expired order\n         bob_bc += usd_fee_refund_core;\n         bob_bu += 500;\n         bob_bu += usd_fee_refund_usd;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n\n         // Alice cancels order\n         cancel_limit_order( ao1_id(db) );\n\n         alice_bc -= order_cancel_fee;\n         alice_bc += 1000;\n         alice_bc += core_fee_refund_core;\n         alice_bu += core_fee_refund_usd;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Check partial fill\n         const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), exp, cer );\n         const limit_order_id_type ao2id = ao2->id;\n         const limit_order_object* bo2 = create_sell_order(   bob_id, asset(100, usd_id), asset(500) );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao2id ) != nullptr );\n         BOOST_CHECK( bo2 == nullptr );\n\n         // data after order created\n         alice_bc -= 1000;\n         alice_bu -= order_create_fee;\n         pool_b -= order_create_fee;\n         accum_b += order_create_fee;\n         bob_bc -= order_create_fee;\n         bob_bu -= 100;\n\n         // data after order filled\n         alice_bu += 100;\n         bob_bc += 500;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // cancel Alice order, show that entire deferred_fee was consumed by partial match\n         if( !expire_order )\n            cancel_limit_order( *ao2 );\n         else\n         {\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n\n         if( !expire_order )\n            alice_bc -= order_cancel_fee;\n         // else do nothing:\n         //         before hard fork 445, no fee when order is expired;\n         //         after hard fork 445, when partially filled order expired, order cancel fee is capped at 0\n         alice_bc += 500;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Check multiple fill\n         // Alice creating multiple orders\n         const limit_order_object* ao31 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_object* ao32 = create_sell_order( alice_id, asset(1000), asset(2000, usd_id), max_exp, cer );\n         const limit_order_object* ao33 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_object* ao34 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_object* ao35 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n\n         const limit_order_id_type ao31id = ao31->id;\n         const limit_order_id_type ao32id = ao32->id;\n         const limit_order_id_type ao33id = ao33->id;\n         const limit_order_id_type ao34id = ao34->id;\n         const limit_order_id_type ao35id = ao35->id;\n\n         alice_bc -= 1000 * 5;\n         alice_bu -= order_create_fee * 5;\n         pool_b -= order_create_fee * 5;\n         accum_b += order_create_fee * 5;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Bob creating an order matching multiple Alice's orders\n         const limit_order_object* bo31 = create_sell_order(   bob_id, asset(500, usd_id), asset(2500), exp );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao31id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao32id ) != nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao33id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao34id ) != nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao35id ) != nullptr );\n         BOOST_CHECK( bo31 == nullptr );\n\n         // data after order created\n         bob_bc -= order_create_fee;\n         bob_bu -= 500;\n\n         // data after order filled\n         alice_bu += 500;\n         bob_bc += 2500;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Bob creating an order matching multiple Alice's orders\n         const limit_order_object* bo32 = create_sell_order(   bob_id, asset(500, usd_id), asset(2500), exp );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao31id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao32id ) != nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao33id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao34id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao35id ) == nullptr );\n         BOOST_CHECK( bo32 != nullptr );\n\n         // data after order created\n         bob_bc -= order_create_fee;\n         bob_bu -= 500;\n\n         // data after order filled\n         alice_bu += 300;\n         bob_bc += 1500;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // cancel Bob order, show that entire deferred_fee was consumed by partial match\n         if( !expire_order )\n            cancel_limit_order( *bo32 );\n         else\n         {\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n         if( !expire_order )\n            bob_bc -= order_cancel_fee;\n         // else do nothing:\n         //         before hard fork 445, no fee when order is expired;\n         //         after hard fork 445, when partially filled order expired, order cancel fee is capped at 0\n         bob_bu += 200;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // cancel Alice order, will refund after hard fork 445\n         cancel_limit_order( ao32id( db ) );\n\n         alice_bc -= order_cancel_fee;\n         alice_bc += 1000;\n         alice_bc += usd_fee_refund_core;\n         alice_bu += usd_fee_refund_usd;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // undo above tx's and reset\n         generate_block( skip );\n         ++blocks_generated;\n         while( blocks_generated > 0 )\n         {\n            db.pop_block();\n            --blocks_generated;\n         }\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( hf445_fee_refund_cross_test )\n{ // create orders before hard fork, cancel them after hard fork\n   try\n   {\n      ACTORS((alice)(bob)(izzy));\n\n      int64_t alice_b0 = 1000000, bob_b0 = 1000000;\n      int64_t pool_0 = 100000, accum_0 = 0;\n\n      transfer( account_id_type(), alice_id, asset(alice_b0) );\n      transfer( account_id_type(), bob_id, asset(bob_b0) );\n\n      asset_id_type core_id = asset_id_type();\n      const auto& usd_obj = create_user_issued_asset( \"IZZYUSD\", izzy_id(db), charge_market_fee );\n      asset_id_type usd_id = usd_obj.id;\n      issue_uia( alice_id, asset( alice_b0, usd_id ) );\n      issue_uia( bob_id, asset( bob_b0, usd_id ) );\n\n      fund_fee_pool( committee_account( db ), usd_obj, pool_0 );\n\n      int64_t order_create_fee = 537;\n      int64_t order_cancel_fee = 129;\n\n      uint32_t skip = database::skip_witness_signature\n                    | database::skip_transaction_signatures\n                    | database::skip_transaction_dupe_check\n                    | database::skip_block_size_check\n                    | database::skip_tapos_check\n                    | database::skip_merkle_check\n                    ;\n\n      generate_block( skip );\n\n      fee_parameters::flat_set_type new_fees;\n      {\n         limit_order_create_operation::fee_parameters_type create_fee_params;\n         create_fee_params.fee = order_create_fee;\n         new_fees.insert( create_fee_params );\n      }\n      {\n         limit_order_cancel_operation::fee_parameters_type cancel_fee_params;\n         cancel_fee_params.fee = order_cancel_fee;\n         new_fees.insert( cancel_fee_params );\n      }\n      {\n         transfer_operation::fee_parameters_type transfer_fee_params;\n         transfer_fee_params.fee = 0;\n         transfer_fee_params.price_per_kbyte = 0;\n         new_fees.insert( transfer_fee_params );\n      }\n\n      // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation\n      // so we have to do it every time we stop generating/popping blocks and start doing tx's\n      enable_fees();\n      change_fees( new_fees );\n\n      // AAAAGGHH create_sell_order reads trx.expiration #469\n      set_expiration( db, trx );\n\n      // prepare params\n      const chain_parameters& params = db.get_global_properties().parameters;\n      time_point_sec max_exp = time_point_sec::maximum();\n      time_point_sec exp = HARDFORK_445_TIME + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3 );\n      time_point_sec exp2 = HARDFORK_445_TIME + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 13 );\n      price cer( asset(1), asset(1, usd_id) );\n      const auto* usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n\n      // balance data\n      int64_t alice_bc = alice_b0, bob_bc = bob_b0; // core balance\n      int64_t alice_bu = alice_b0, bob_bu = bob_b0; // usd balance\n      int64_t pool_b = pool_0, accum_b = accum_0;\n\n      // prepare orders\n      BOOST_TEST_MESSAGE( \"Creating orders those will never match: ao1, ao2, bo1, bo2 ..\" );\n      // ao1: won't expire, won't match, fee in core\n      limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(100000, usd_id) )->id;\n      BOOST_CHECK( db.find( ao1_id ) != nullptr );\n      // ao2: will expire, won't match, fee in core\n      limit_order_id_type ao2_id = create_sell_order( alice_id, asset(800), asset(100000, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao2_id ) != nullptr );\n      // bo1: won't expire, won't match, fee in usd\n      limit_order_id_type bo1_id = create_sell_order(   bob_id, asset(1000, usd_id), asset(100000), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo1_id ) != nullptr );\n      // bo2: will expire, won't match, fee in usd\n      limit_order_id_type bo2_id = create_sell_order(   bob_id, asset(800, usd_id), asset(100000), exp, cer )->id;\n      BOOST_CHECK( db.find( bo2_id ) != nullptr );\n\n      alice_bc -= order_create_fee * 2;\n      alice_bc -= 1000;\n      alice_bc -= 800;\n      bob_bu -= order_create_fee * 2;\n      bob_bu -= 1000;\n      bob_bu -= 800;\n      pool_b -= order_create_fee * 2;\n      accum_b += order_create_fee * 2;\n      int64_t ao1_remain = 1000;\n      int64_t ao2_remain = 800;\n      int64_t bo1_remain = 1000;\n      int64_t bo2_remain = 800;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao3: won't expire, partially match before hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao3 ..\" );\n      limit_order_id_type ao3_id = create_sell_order( alice_id, asset(900), asset(2700, usd_id) )->id;\n      BOOST_CHECK( db.find( ao3_id ) != nullptr );\n      create_sell_order( bob_id, asset(600, usd_id), asset(200) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 900;\n      alice_bu += 600;\n      bob_bc -= order_create_fee;\n      bob_bu -= 600;\n      bob_bc += 200;\n      int64_t ao3_remain = 900 - 200;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao4: will expire, will partially match before hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao4 ..\" );\n      limit_order_id_type ao4_id = create_sell_order( alice_id, asset(700), asset(1400, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao4_id ) != nullptr );\n      create_sell_order( bob_id, asset(200, usd_id), asset(100) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 700;\n      alice_bu += 200;\n      bob_bc -= order_create_fee;\n      bob_bu -= 200;\n      bob_bc += 100;\n      int64_t ao4_remain = 700 - 100;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo3: won't expire, will partially match before hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo3 ..\" );\n      limit_order_id_type bo3_id = create_sell_order(   bob_id, asset(500, usd_id), asset(1500), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo3_id ) != nullptr );\n      create_sell_order( alice_id, asset(450), asset(150, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 450;\n      alice_bu += 150;\n      bob_bu -= order_create_fee;\n      bob_bu -= 500;\n      bob_bc += 450;\n      pool_b -= order_create_fee;\n      accum_b += order_create_fee;\n      int64_t bo3_remain = 500 - 150;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo4: will expire, will partially match before hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo4 ..\" );\n      limit_order_id_type bo4_id = create_sell_order(   bob_id, asset(300, usd_id), asset(600), exp, cer )->id;\n      BOOST_CHECK( db.find( bo4_id ) != nullptr );\n      create_sell_order( alice_id, asset(140), asset(70, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 140;\n      alice_bu += 70;\n      bob_bu -= order_create_fee;\n      bob_bu -= 300;\n      bob_bc += 140;\n      pool_b -= order_create_fee;\n      accum_b += order_create_fee;\n      int64_t bo4_remain = 300 - 70;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao5: won't expire, partially match after hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao5 ..\" );\n      limit_order_id_type ao5_id = create_sell_order( alice_id, asset(606), asset(909, usd_id) )->id;\n      BOOST_CHECK( db.find( ao5_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 606;\n      int64_t ao5_remain = 606;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao6: will expire, partially match after hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao6 ..\" );\n      limit_order_id_type ao6_id = create_sell_order( alice_id, asset(333), asset(444, usd_id), exp2 )->id;\n      BOOST_CHECK( db.find( ao6_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 333;\n      int64_t ao6_remain = 333;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo5: won't expire, partially match after hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo5 ..\" );\n      limit_order_id_type bo5_id = create_sell_order(   bob_id, asset(255, usd_id), asset(408), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo5_id ) != nullptr );\n\n      bob_bu -= order_create_fee;\n      bob_bu -= 255;\n      pool_b -= order_create_fee;\n      accum_b += order_create_fee;\n      int64_t bo5_remain = 255;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo6: will expire, partially match after hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo6 ..\" );\n      limit_order_id_type bo6_id = create_sell_order(   bob_id, asset(127, usd_id), asset(127), exp2, cer )->id;\n      BOOST_CHECK( db.find( bo6_id ) != nullptr );\n\n      bob_bu -= order_create_fee;\n      bob_bu -= 127;\n      pool_b -= order_create_fee;\n      accum_b += order_create_fee;\n      int64_t bo6_remain = 127;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block so the orders will be in db before hard fork\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      // generate blocks util hard fork 445\n      generate_blocks( HARDFORK_445_TIME, true, skip );\n      generate_block( skip );\n\n      // nothing will change\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate more blocks, so some orders will expire\n      generate_blocks( exp, true, skip );\n\n      // no fee refund for orders created before hard fork 445, but remaining funds will be refunded\n      BOOST_TEST_MESSAGE( \"Checking expired orders: ao2, ao4, bo2, bo4 ..\" );\n      alice_bc += ao2_remain;\n      alice_bc += ao4_remain;\n      bob_bu += bo2_remain;\n      bob_bu += bo4_remain;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // cancel ao1\n      BOOST_TEST_MESSAGE( \"Cancel order ao1 ..\" );\n      cancel_limit_order( ao1_id(db) );\n\n      alice_bc += ao1_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel ao3\n      BOOST_TEST_MESSAGE( \"Cancel order ao3 ..\" );\n      cancel_limit_order( ao3_id(db) );\n\n      alice_bc += ao3_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo1\n      BOOST_TEST_MESSAGE( \"Cancel order bo1 ..\" );\n      cancel_limit_order( bo1_id(db) );\n\n      bob_bu += bo1_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo3\n      BOOST_TEST_MESSAGE( \"Cancel order bo3 ..\" );\n      cancel_limit_order( bo3_id(db) );\n\n      bob_bu += bo3_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill ao6\n      BOOST_TEST_MESSAGE( \"Partially fill ao6 ..\" );\n      create_sell_order( bob_id, asset(88, usd_id), asset(66) );\n\n      alice_bu += 88;\n      bob_bc -= order_create_fee;\n      bob_bu -= 88;\n      bob_bc += 66;\n      ao6_remain -= 66;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo6\n      BOOST_TEST_MESSAGE( \"Partially fill bo6 ..\" );\n      create_sell_order( alice_id, asset(59), asset(59, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 59;\n      alice_bu += 59;\n      bob_bc += 59;\n      bo6_remain -= 59;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block to save the changes\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate blocks util exp2, so some orders will expire\n      generate_blocks( exp2, true, skip );\n\n      // no fee refund for orders created before hard fork 445, but remaining funds will be refunded\n      BOOST_TEST_MESSAGE( \"Checking expired orders: ao6, bo6 ..\" );\n      alice_bc += ao6_remain;\n      bob_bu += bo6_remain;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // partially fill ao5\n      BOOST_TEST_MESSAGE( \"Partially fill ao5 ..\" );\n      create_sell_order( bob_id, asset(93, usd_id), asset(62) );\n\n      alice_bu += 93;\n      bob_bc -= order_create_fee;\n      bob_bu -= 93;\n      bob_bc += 62;\n      ao5_remain -= 62;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo5\n      BOOST_TEST_MESSAGE( \"Partially fill bo5 ..\" );\n      create_sell_order( alice_id, asset(24), asset(15, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 24;\n      alice_bu += 15;\n      bob_bc += 24;\n      bo5_remain -= 15;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel ao5\n      BOOST_TEST_MESSAGE( \"Cancel order ao5 ..\" );\n      cancel_limit_order( ao5_id(db) );\n\n      alice_bc += ao5_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo5\n      BOOST_TEST_MESSAGE( \"Cancel order bo5 ..\" );\n      cancel_limit_order( bo5_id(db) );\n\n      bob_bu += bo5_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block to save the changes\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( bsip26_fee_refund_test )\n{\n   try\n   {\n      ACTORS((alice)(bob)(izzy));\n\n      int64_t alice_b0 = 1000000, bob_b0 = 1000000;\n      int64_t pool_0 = 1000000, accum_0 = 0;\n\n      transfer( account_id_type(), alice_id, asset(alice_b0) );\n      transfer( account_id_type(), bob_id, asset(bob_b0) );\n\n      asset_id_type core_id = asset_id_type();\n      int64_t cer_core_amount = 1801;\n      int64_t cer_usd_amount = 3;\n      price tmp_cer( asset( cer_core_amount ), asset( cer_usd_amount, asset_id_type(1) ) );\n      const auto& usd_obj = create_user_issued_asset( \"IZZYUSD\", izzy_id(db), charge_market_fee, tmp_cer );\n      asset_id_type usd_id = usd_obj.id;\n      issue_uia( alice_id, asset( alice_b0, usd_id ) );\n      issue_uia( bob_id, asset( bob_b0, usd_id ) );\n\n      fund_fee_pool( committee_account( db ), usd_obj, pool_0 );\n\n      int64_t order_create_fee = 547;\n      int64_t order_cancel_fee;\n      int64_t order_cancel_fee1 = 139;\n      int64_t order_cancel_fee2 = 829;\n\n      uint32_t skip = database::skip_witness_signature\n                    | database::skip_transaction_signatures\n                    | database::skip_transaction_dupe_check\n                    | database::skip_block_size_check\n                    | database::skip_tapos_check\n                    | database::skip_merkle_check\n                    ;\n\n      generate_block( skip );\n\n      fee_parameters::flat_set_type new_fees;\n      fee_parameters::flat_set_type new_fees1;\n      fee_parameters::flat_set_type new_fees2;\n      {\n         limit_order_create_operation::fee_parameters_type create_fee_params;\n         create_fee_params.fee = order_create_fee;\n         new_fees1.insert( create_fee_params );\n         new_fees2.insert( create_fee_params );\n      }\n      {\n         limit_order_cancel_operation::fee_parameters_type cancel_fee_params;\n         cancel_fee_params.fee = order_cancel_fee1;\n         new_fees1.insert( cancel_fee_params );\n      }\n      {\n         limit_order_cancel_operation::fee_parameters_type cancel_fee_params;\n         cancel_fee_params.fee = order_cancel_fee2;\n         new_fees2.insert( cancel_fee_params );\n      }\n      {\n         transfer_operation::fee_parameters_type transfer_fee_params;\n         transfer_fee_params.fee = 0;\n         transfer_fee_params.price_per_kbyte = 0;\n         new_fees1.insert( transfer_fee_params );\n         new_fees2.insert( transfer_fee_params );\n      }\n\n      for( int i=0; i<12; i++ )\n      {\n         bool expire_order = ( i % 2 != 0 );\n         bool high_cancel_fee = ( i % 4 >= 2 );\n         bool before_hardfork_445 = ( i < 4 );\n         bool after_bsip26 = ( i >= 8 );\n         idump( (before_hardfork_445)(after_bsip26)(expire_order)(high_cancel_fee) );\n         if( i == 4 )\n         {\n            BOOST_TEST_MESSAGE( \"Hard fork 445\" );\n            generate_blocks( HARDFORK_445_TIME, true, skip );\n            generate_block( skip );\n         }\n         else if( i == 8 )\n         {\n            BOOST_TEST_MESSAGE( \"Hard fork core-604 (bsip26)\" );\n            generate_blocks( HARDFORK_CORE_604_TIME, true, skip );\n            generate_block( skip );\n         }\n\n         if( high_cancel_fee )\n         {\n            new_fees = new_fees2;\n            order_cancel_fee = order_cancel_fee2;\n         }\n         else\n         {\n            new_fees = new_fees1;\n            order_cancel_fee = order_cancel_fee1;\n         }\n\n         int64_t usd_create_fee = order_create_fee * cer_usd_amount / cer_core_amount;\n         if( usd_create_fee * cer_core_amount != order_create_fee * cer_usd_amount ) usd_create_fee += 1;\n         int64_t usd_cancel_fee = order_cancel_fee * cer_usd_amount / cer_core_amount;\n         if( usd_cancel_fee * cer_core_amount != order_cancel_fee * cer_usd_amount ) usd_cancel_fee += 1;\n         int64_t core_create_fee = usd_create_fee * cer_core_amount / cer_usd_amount;\n         int64_t core_cancel_fee = usd_cancel_fee * cer_core_amount / cer_usd_amount;\n         BOOST_CHECK( core_cancel_fee >= order_cancel_fee );\n\n         BOOST_TEST_MESSAGE( \"Start\" );\n\n         // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation\n         // so we have to do it every time we stop generating/popping blocks and start doing tx's\n         enable_fees();\n         change_fees( new_fees );\n\n         // AAAAGGHH create_sell_order reads trx.expiration #469\n         set_expiration( db, trx );\n\n         // prepare params\n         uint32_t blocks_generated = 0;\n         time_point_sec max_exp = time_point_sec::maximum();\n         time_point_sec exp = db.head_block_time(); // order will be accepted when pushing trx then expired at current block\n         price cer = usd_id( db ).options.core_exchange_rate;\n         const auto* usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n\n         // balance data\n         int64_t alice_bc = alice_b0, bob_bc = bob_b0; // core balance\n         int64_t alice_bu = alice_b0, bob_bu = bob_b0; // usd balance\n         int64_t pool_b = pool_0, accum_b = accum_0;\n\n         // refund data\n         int64_t core_fee_refund_core;\n         int64_t core_fee_refund_usd;\n         int64_t usd_fee_refund_core;\n         int64_t usd_fee_refund_usd;\n         int64_t accum_on_new;\n         int64_t accum_on_fill;\n         int64_t pool_refund;\n         if( db.head_block_time() > HARDFORK_CORE_604_TIME )\n         {\n            core_fee_refund_core = order_create_fee;\n            core_fee_refund_usd = 0;\n            usd_fee_refund_core = 0;\n            usd_fee_refund_usd = usd_create_fee;\n            accum_on_new = 0;\n            accum_on_fill = usd_create_fee;\n            pool_refund = core_create_fee;\n         }\n         else if( db.head_block_time() > HARDFORK_445_TIME )\n         {\n            core_fee_refund_core = order_create_fee;\n            core_fee_refund_usd = 0;\n            usd_fee_refund_core = core_create_fee;\n            usd_fee_refund_usd = 0;\n            accum_on_new = usd_create_fee;\n            accum_on_fill = 0;\n            pool_refund = 0;\n         }\n         else\n         {\n            core_fee_refund_core = 0;\n            core_fee_refund_usd = 0;\n            usd_fee_refund_core = 0;\n            usd_fee_refund_usd = 0;\n            accum_on_new = usd_create_fee;\n            accum_on_fill = 0;\n            pool_refund = 0;\n         }\n\n         // Check non-overlapping\n         // Alice creates order\n         // Bob creates order which doesn't match\n         BOOST_TEST_MESSAGE( \"Creating non-overlapping orders\" );\n         BOOST_TEST_MESSAGE( \"Creating ao1\" );\n         limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(1000, usd_id), exp )->id;\n\n         alice_bc -= order_create_fee;\n         alice_bc -= 1000;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Alice cancels order\n         if( !expire_order )\n         {\n            BOOST_TEST_MESSAGE( \"Cancel order ao1\" );\n            cancel_limit_order( ao1_id(db) );\n         }\n         else\n         {\n            BOOST_TEST_MESSAGE( \"Order ao1 expired\" );\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n\n         if( !expire_order )\n            alice_bc -= order_cancel_fee; // manual cancellation always need a fee\n         else if( before_hardfork_445 )\n         {  // do nothing: before hard fork 445, no fee on expired order\n         }\n         else if( !after_bsip26 )\n         {\n            // charge a cancellation fee in core, capped by deffered_fee which is order_create_fee\n            alice_bc -= std::min( order_cancel_fee, order_create_fee );\n         }\n         else // bsip26\n         {\n            // charge a cancellation fee in core, capped by deffered_fee which is order_create_fee\n            alice_bc -= std::min( order_cancel_fee, order_create_fee );\n         }\n         alice_bc += 1000;\n         alice_bc += core_fee_refund_core;\n         alice_bu += core_fee_refund_usd;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         BOOST_TEST_MESSAGE( \"Creating bo1\" );\n         limit_order_id_type bo1_id = create_sell_order(   bob_id, asset(500, usd_id), asset(1000), exp, cer )->id;\n\n         bob_bu -= usd_create_fee;\n         bob_bu -= 500;\n         pool_b -= core_create_fee;\n         accum_b += accum_on_new;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Bob cancels order\n         if( !expire_order )\n         {\n            BOOST_TEST_MESSAGE( \"Cancel order bo1\" );\n            cancel_limit_order( bo1_id(db) );\n         }\n         else\n         {\n            BOOST_TEST_MESSAGE( \"Order bo1 expired\" );\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n         if( !expire_order )\n            bob_bc -= order_cancel_fee; // manual cancellation always need a fee\n         else if( before_hardfork_445 )\n         {  // do nothing: before hard fork 445, no fee on expired order\n         }\n         else if( !after_bsip26 )\n         {\n            // charge a cancellation fee in core, capped by deffered_fee which is core_create_fee\n            bob_bc -= std::min( order_cancel_fee, core_create_fee );\n         }\n         else // bsip26\n         {\n            // when expired, should have core_create_fee in deferred, usd_create_fee in deferred_paid\n\n            // charge a cancellation fee in core from fee_pool, capped by deffered\n            int64_t capped_core_cancel_fee = std::min( order_cancel_fee, core_create_fee );\n            pool_b -= capped_core_cancel_fee;\n\n            // charge a coresponding cancellation fee in usd from deffered_paid, round up, capped\n            int64_t capped_usd_cancel_fee = capped_core_cancel_fee * usd_create_fee / core_create_fee;\n            if( capped_usd_cancel_fee * core_create_fee != capped_core_cancel_fee * usd_create_fee )\n               capped_usd_cancel_fee += 1;\n            if( capped_usd_cancel_fee > usd_create_fee )\n               capped_usd_cancel_fee = usd_create_fee;\n            bob_bu -= capped_usd_cancel_fee;\n\n            // cancellation fee goes to accumulated fees\n            accum_b += capped_usd_cancel_fee;\n         }\n         bob_bc += usd_fee_refund_core;\n         bob_bu += 500;\n         bob_bu += usd_fee_refund_usd;\n         pool_b += pool_refund; // bo1\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n\n         // Check partial fill\n         BOOST_TEST_MESSAGE( \"Creating ao2, then be partially filled by bo2\" );\n         const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), exp, cer );\n         const limit_order_id_type ao2id = ao2->id;\n         const limit_order_object* bo2 = create_sell_order(   bob_id, asset(100, usd_id), asset(500) );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao2id ) != nullptr );\n         BOOST_CHECK( bo2 == nullptr );\n\n         // data after order created\n         alice_bc -= 1000;\n         alice_bu -= usd_create_fee;\n         pool_b -= core_create_fee;\n         accum_b += accum_on_new;\n         bob_bc -= order_create_fee;\n         bob_bu -= 100;\n\n         // data after order filled\n         alice_bu += 100;\n         bob_bc += 500;\n         accum_b += accum_on_fill; // ao2\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // cancel Alice order, show that entire deferred_fee was consumed by partial match\n         if( !expire_order )\n         {\n            BOOST_TEST_MESSAGE( \"Cancel order ao2\" );\n            cancel_limit_order( *ao2 );\n         }\n         else\n         {\n            BOOST_TEST_MESSAGE( \"Order ao2 expired\" );\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n\n         if( !expire_order )\n            alice_bc -= order_cancel_fee;\n         // else do nothing:\n         //         before hard fork 445, no fee when order is expired;\n         //         after hard fork 445, when partially filled order expired, order cancel fee is capped at 0\n         alice_bc += 500;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Check multiple fill\n         // Alice creating multiple orders\n         BOOST_TEST_MESSAGE( \"Creating ao31-ao35\" );\n         const limit_order_object* ao31 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_object* ao32 = create_sell_order( alice_id, asset(1000), asset(2000, usd_id), max_exp, cer );\n         const limit_order_object* ao33 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_object* ao34 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n         const limit_order_object* ao35 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer );\n\n         const limit_order_id_type ao31id = ao31->id;\n         const limit_order_id_type ao32id = ao32->id;\n         const limit_order_id_type ao33id = ao33->id;\n         const limit_order_id_type ao34id = ao34->id;\n         const limit_order_id_type ao35id = ao35->id;\n\n         alice_bc -= 1000 * 5;\n         alice_bu -= usd_create_fee * 5;\n         pool_b -= core_create_fee * 5;\n         accum_b += accum_on_new * 5;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Bob creating an order matching multiple Alice's orders\n         BOOST_TEST_MESSAGE( \"Creating bo31, completely fill ao31 and ao33, partially fill ao34\" );\n         const limit_order_object* bo31 = create_sell_order(   bob_id, asset(500, usd_id), asset(2500), exp );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao31id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao32id ) != nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao33id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao34id ) != nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao35id ) != nullptr );\n         BOOST_CHECK( bo31 == nullptr );\n\n         // data after order created\n         bob_bc -= order_create_fee;\n         bob_bu -= 500;\n\n         // data after order filled\n         alice_bu += 500;\n         bob_bc += 2500;\n         accum_b += accum_on_fill * 3; // ao31, ao33, ao34\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // Bob creating an order matching multiple Alice's orders\n         BOOST_TEST_MESSAGE( \"Creating bo32, completely fill partially filled ao34 and new ao35, leave on market\" );\n         const limit_order_object* bo32 = create_sell_order(   bob_id, asset(500, usd_id), asset(2500), exp );\n\n         BOOST_CHECK( db.find<limit_order_object>( ao31id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao32id ) != nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao33id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao34id ) == nullptr );\n         BOOST_CHECK( db.find<limit_order_object>( ao35id ) == nullptr );\n         BOOST_CHECK( bo32 != nullptr );\n\n         // data after order created\n         bob_bc -= order_create_fee;\n         bob_bu -= 500;\n\n         // data after order filled\n         alice_bu += 300;\n         bob_bc += 1500;\n         accum_b += accum_on_fill; // ao35\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // cancel Bob order, show that entire deferred_fee was consumed by partial match\n         if( !expire_order )\n         {\n            BOOST_TEST_MESSAGE( \"Cancel order bo32\" );\n            cancel_limit_order( *bo32 );\n         }\n         else\n         {\n            BOOST_TEST_MESSAGE( \"Order bo32 expired\" );\n            // empty accounts before generate block, to test if it will fail when charging order cancel fee\n            transfer( alice_id, account_id_type(), asset(alice_bc, core_id) );\n            transfer( alice_id, account_id_type(), asset(alice_bu,  usd_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bc, core_id) );\n            transfer(   bob_id, account_id_type(), asset(  bob_bu,  usd_id) );\n            // generate a new block so one or more order will expire\n            generate_block( skip );\n            ++blocks_generated;\n            enable_fees();\n            change_fees( new_fees );\n            set_expiration( db, trx );\n            exp = db.head_block_time();\n            usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n            // restore account balances\n            transfer( account_id_type(), alice_id, asset(alice_bc, core_id) );\n            transfer( account_id_type(), alice_id, asset(alice_bu,  usd_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bc, core_id) );\n            transfer( account_id_type(),   bob_id, asset(  bob_bu,  usd_id) );\n         }\n\n         if( !expire_order )\n            bob_bc -= order_cancel_fee;\n         // else do nothing:\n         //         before hard fork 445, no fee when order is expired;\n         //         after hard fork 445, when partially filled order expired, order cancel fee is capped at 0\n         bob_bu += 200;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // cancel Alice order, will refund after hard fork 445\n         BOOST_TEST_MESSAGE( \"Cancel order ao32\" );\n         cancel_limit_order( ao32id( db ) );\n\n         alice_bc -= order_cancel_fee;\n         alice_bc += 1000;\n         alice_bc += usd_fee_refund_core;\n         alice_bu += usd_fee_refund_usd;\n         pool_b += pool_refund;\n\n         BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n         BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n         BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n         BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n         BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n         // undo above tx's and reset\n         BOOST_TEST_MESSAGE( \"Clean up\" );\n         generate_block( skip );\n         ++blocks_generated;\n         while( blocks_generated > 0 )\n         {\n            db.pop_block();\n            --blocks_generated;\n         }\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( bsip26_fee_refund_cross_test )\n{ // create orders before hard fork, cancel them after hard fork\n   try\n   {\n      ACTORS((alice)(bob)(izzy));\n\n      int64_t alice_b0 = 1000000, bob_b0 = 1000000;\n      int64_t pool_0 = 1000000, accum_0 = 0;\n\n      transfer( account_id_type(), alice_id, asset(alice_b0) );\n      transfer( account_id_type(), bob_id, asset(bob_b0) );\n\n      asset_id_type core_id = asset_id_type();\n      int64_t cer_core_amount = 1801;\n      int64_t cer_usd_amount = 3;\n      price tmp_cer( asset( cer_core_amount ), asset( cer_usd_amount, asset_id_type(1) ) );\n      const auto& usd_obj = create_user_issued_asset( \"IZZYUSD\", izzy_id(db), charge_market_fee, tmp_cer );\n      asset_id_type usd_id = usd_obj.id;\n      issue_uia( alice_id, asset( alice_b0, usd_id ) );\n      issue_uia( bob_id, asset( bob_b0, usd_id ) );\n\n      fund_fee_pool( committee_account( db ), usd_obj, pool_0 );\n\n      int64_t order_create_fee = 547;\n      int64_t order_cancel_fee = 139;\n      int64_t usd_create_fee = order_create_fee * cer_usd_amount / cer_core_amount;\n      if( usd_create_fee * cer_core_amount != order_create_fee * cer_usd_amount ) usd_create_fee += 1;\n      int64_t usd_cancel_fee = order_cancel_fee * cer_usd_amount / cer_core_amount;\n      if( usd_cancel_fee * cer_core_amount != order_cancel_fee * cer_usd_amount ) usd_cancel_fee += 1;\n      int64_t core_create_fee = usd_create_fee * cer_core_amount / cer_usd_amount;\n      int64_t core_cancel_fee = usd_cancel_fee * cer_core_amount / cer_usd_amount;\n      BOOST_CHECK( core_cancel_fee >= order_cancel_fee );\n\n      uint32_t skip = database::skip_witness_signature\n                    | database::skip_transaction_signatures\n                    | database::skip_transaction_dupe_check\n                    | database::skip_block_size_check\n                    | database::skip_tapos_check\n                    | database::skip_merkle_check\n                    ;\n\n      generate_block( skip );\n\n      fee_parameters::flat_set_type new_fees;\n      {\n         limit_order_create_operation::fee_parameters_type create_fee_params;\n         create_fee_params.fee = order_create_fee;\n         new_fees.insert( create_fee_params );\n      }\n      {\n         limit_order_cancel_operation::fee_parameters_type cancel_fee_params;\n         cancel_fee_params.fee = order_cancel_fee;\n         new_fees.insert( cancel_fee_params );\n      }\n      {\n         transfer_operation::fee_parameters_type transfer_fee_params;\n         transfer_fee_params.fee = 0;\n         transfer_fee_params.price_per_kbyte = 0;\n         new_fees.insert( transfer_fee_params );\n      }\n\n      // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation\n      // so we have to do it every time we stop generating/popping blocks and start doing tx's\n      enable_fees();\n      change_fees( new_fees );\n\n      // AAAAGGHH create_sell_order reads trx.expiration #469\n      set_expiration( db, trx );\n\n      // prepare params\n      const chain_parameters& params = db.get_global_properties().parameters;\n      time_point_sec max_exp = time_point_sec::maximum();\n      time_point_sec exp = HARDFORK_CORE_604_TIME + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3 );\n      time_point_sec exp1 = HARDFORK_CORE_604_TIME + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 13 );\n      time_point_sec exp2 = HARDFORK_CORE_604_TIME + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 23 );\n      price cer = usd_id( db ).options.core_exchange_rate;\n      const auto* usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n\n      // balance data\n      int64_t alice_bc = alice_b0, bob_bc = bob_b0; // core balance\n      int64_t alice_bu = alice_b0, bob_bu = bob_b0; // usd balance\n      int64_t pool_b = pool_0, accum_b = accum_0;\n\n      // prepare orders\n      BOOST_TEST_MESSAGE( \"Creating orders those will never match: ao1, ao2, bo1, bo2 ..\" );\n      // ao1: won't expire, won't match, fee in core\n      limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(100000, usd_id) )->id;\n      BOOST_CHECK( db.find( ao1_id ) != nullptr );\n      // ao2: will expire, won't match, fee in core\n      limit_order_id_type ao2_id = create_sell_order( alice_id, asset(800), asset(100000, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao2_id ) != nullptr );\n      // bo1: won't expire, won't match, fee in usd\n      limit_order_id_type bo1_id = create_sell_order(   bob_id, asset(1000, usd_id), asset(100000), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo1_id ) != nullptr );\n      // bo2: will expire, won't match, fee in usd\n      limit_order_id_type bo2_id = create_sell_order(   bob_id, asset(800, usd_id), asset(100000), exp, cer )->id;\n      BOOST_CHECK( db.find( bo2_id ) != nullptr );\n\n      alice_bc -= order_create_fee * 2;\n      alice_bc -= 1000;\n      alice_bc -= 800;\n      bob_bu -= usd_create_fee * 2;\n      bob_bu -= 1000;\n      bob_bu -= 800;\n      pool_b -= core_create_fee * 2;\n      accum_b += usd_create_fee * 2;\n      int64_t ao1_remain = 1000;\n      int64_t ao2_remain = 800;\n      int64_t bo1_remain = 1000;\n      int64_t bo2_remain = 800;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao3: won't expire, partially match before hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao3 ..\" ); // 1:30\n      limit_order_id_type ao3_id = create_sell_order( alice_id, asset(900), asset(27000, usd_id) )->id;\n      BOOST_CHECK( db.find( ao3_id ) != nullptr );\n      create_sell_order( bob_id, asset(6000, usd_id), asset(200) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 900;\n      alice_bu += 6000;\n      bob_bc -= order_create_fee;\n      bob_bu -= 6000;\n      bob_bc += 200;\n      int64_t ao3_remain = 900 - 200;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao4: will expire, will partially match before hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao4 ..\" ); // 1:20\n      limit_order_id_type ao4_id = create_sell_order( alice_id, asset(700), asset(14000, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao4_id ) != nullptr );\n      create_sell_order( bob_id, asset(2000, usd_id), asset(100) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 700;\n      alice_bu += 2000;\n      bob_bc -= order_create_fee;\n      bob_bu -= 2000;\n      bob_bc += 100;\n      int64_t ao4_remain = 700 - 100;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo3: won't expire, will partially match before hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo3 ..\" ); // 1:30\n      limit_order_id_type bo3_id = create_sell_order(   bob_id, asset(500, usd_id), asset(15000), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo3_id ) != nullptr );\n      create_sell_order( alice_id, asset(4500), asset(150, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 4500;\n      alice_bu += 150;\n      bob_bu -= usd_create_fee;\n      bob_bu -= 500;\n      bob_bc += 4500;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo3_remain = 500 - 150;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo4: will expire, will partially match before hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo4 ..\" ); // 1:20\n      limit_order_id_type bo4_id = create_sell_order(   bob_id, asset(300, usd_id), asset(6000), exp, cer )->id;\n      BOOST_CHECK( db.find( bo4_id ) != nullptr );\n      create_sell_order( alice_id, asset(1400), asset(70, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 1400;\n      alice_bu += 70;\n      bob_bu -= usd_create_fee;\n      bob_bu -= 300;\n      bob_bc += 1400;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo4_remain = 300 - 70;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n\n      // ao11: won't expire, partially match after hard fork core-604, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao11 ..\" ); // 1:18\n      limit_order_id_type ao11_id = create_sell_order( alice_id, asset(510), asset(9180, usd_id) )->id;\n      BOOST_CHECK( db.find( ao11_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 510;\n      int64_t ao11_remain = 510;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao12: will expire, partially match after hard fork core-604, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao12 ..\" ); // 1:16\n      limit_order_id_type ao12_id = create_sell_order( alice_id, asset(256), asset(4096, usd_id), exp2 )->id;\n      BOOST_CHECK( db.find( ao12_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 256;\n      int64_t ao12_remain = 256;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo11: won't expire, partially match after hard fork core-604, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo11 ..\" ); // 1:18\n      limit_order_id_type bo11_id = create_sell_order(   bob_id, asset(388, usd_id), asset(6984), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo11_id ) != nullptr );\n\n      bob_bu -= usd_create_fee;\n      bob_bu -= 388;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo11_remain = 388;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo12: will expire, partially match after hard fork core-604, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo12 ..\" ); // 1:17\n      limit_order_id_type bo12_id = create_sell_order(   bob_id, asset(213, usd_id), asset(3621), exp2, cer )->id;\n      BOOST_CHECK( db.find( bo12_id ) != nullptr );\n\n      bob_bu -= usd_create_fee;\n      bob_bu -= 213;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo12_remain = 213;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao5: won't expire, partially match after hard fork 445, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao5 ..\" ); // 1:15\n      limit_order_id_type ao5_id = create_sell_order( alice_id, asset(606), asset(9090, usd_id) )->id;\n      BOOST_CHECK( db.find( ao5_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 606;\n      int64_t ao5_remain = 606;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao6: will expire, partially match after hard fork 445, fee in core\n      if( false ) { // only can have either ao5 or ao6, can't have both\n      BOOST_TEST_MESSAGE( \"Creating order ao6 ..\" ); // 3:40 = 1:13.33333\n      limit_order_id_type ao6_id = create_sell_order( alice_id, asset(333), asset(4440, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao6_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 333;\n      // int64_t ao6_remain = 333; // only can have either ao5 or ao6, can't have both\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n      }\n\n      // bo5: won't expire, partially match after hard fork 445, fee in usd\n      if( false ) { // only can have either bo5 or bo6, can't have both\n      BOOST_TEST_MESSAGE( \"Creating order bo5 ..\" ); // 1:16\n      limit_order_id_type bo5_id = create_sell_order(   bob_id, asset(255, usd_id), asset(4080), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo5_id ) != nullptr );\n\n      bob_bu -= usd_create_fee;\n      bob_bu -= 255;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      //int64_t bo5_remain = 255; // only can have either bo5 or bo6, can't have both\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n      }\n\n      // bo6: will expire, partially match after hard fork 445, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo6 ..\" ); // 1:10\n      limit_order_id_type bo6_id = create_sell_order(   bob_id, asset(127, usd_id), asset(1270), exp, cer )->id;\n      BOOST_CHECK( db.find( bo6_id ) != nullptr );\n      BOOST_CHECK( db.find( bo6_id ) != nullptr );\n\n      bob_bu -= usd_create_fee;\n      bob_bu -= 127;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo6_remain = 127;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block so the orders will be in db before hard fork 445\n      BOOST_TEST_MESSAGE( \"Generating blocks, passing hard fork 445 ...\" );\n      generate_block( skip );\n\n      // generate blocks util hard fork 445\n      generate_blocks( HARDFORK_445_TIME, true, skip );\n      generate_block( skip );\n\n      // nothing will change\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // partially fill ao6\n      if( false ) { // only can have either ao5 or ao6, can't have both\n      BOOST_TEST_MESSAGE( \"Partially fill ao6 ..\" ); // 3:40\n      create_sell_order( bob_id, asset(880, usd_id), asset(66) );\n\n      alice_bu += 880;\n      bob_bc -= order_create_fee;\n      bob_bu -= 880;\n      bob_bc += 66;\n      //ao6_remain -= 66;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n      }\n\n      // partially fill bo6\n      BOOST_TEST_MESSAGE( \"Partially fill bo6 ..\" ); // 1:10\n      create_sell_order( alice_id, asset(590), asset(59, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 590;\n      alice_bu += 59;\n      bob_bc += 590;\n      bo6_remain -= 59;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill ao5\n      BOOST_TEST_MESSAGE( \"Partially fill ao5 ..\" ); // 1:15\n      create_sell_order( bob_id, asset(930, usd_id), asset(62) );\n\n      alice_bu += 930;\n      bob_bc -= order_create_fee;\n      bob_bu -= 930;\n      bob_bc += 62;\n      ao5_remain -= 62;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo5\n      if( false ) { // only can have either bo5 or bo6, can't have both\n      BOOST_TEST_MESSAGE( \"Partially fill bo5 ..\" ); // 1:16\n      create_sell_order( alice_id, asset(240), asset(15, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 240;\n      alice_bu += 15;\n      bob_bc += 240;\n      //bo5_remain -= 15;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n      }\n\n      // prepare more orders\n      BOOST_TEST_MESSAGE( \"Creating more orders those will never match: ao7, ao8, bo7, bo8 ..\" ); // ~ 1:100\n      // ao7: won't expire, won't match, fee in core\n      limit_order_id_type ao7_id = create_sell_order( alice_id, asset(1003), asset(100000, usd_id) )->id;\n      BOOST_CHECK( db.find( ao7_id ) != nullptr );\n      // ao8: will expire, won't match, fee in core\n      limit_order_id_type ao8_id = create_sell_order( alice_id, asset(803), asset(100000, usd_id), exp1 )->id;\n      BOOST_CHECK( db.find( ao8_id ) != nullptr );\n      // bo7: won't expire, won't match, fee in usd\n      limit_order_id_type bo7_id = create_sell_order(   bob_id, asset(1003, usd_id), asset(100000), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo7_id ) != nullptr );\n      // bo8: will expire, won't match, fee in usd\n      limit_order_id_type bo8_id = create_sell_order(   bob_id, asset(803, usd_id), asset(100000), exp1, cer )->id;\n      BOOST_CHECK( db.find( bo8_id ) != nullptr );\n\n      alice_bc -= order_create_fee * 2;\n      alice_bc -= 1003;\n      alice_bc -= 803;\n      bob_bu -= usd_create_fee * 2;\n      bob_bu -= 1003;\n      bob_bu -= 803;\n      pool_b -= core_create_fee * 2;\n      accum_b += usd_create_fee * 2;\n      int64_t ao7_remain = 1003;\n      int64_t ao8_remain = 803;\n      int64_t bo7_remain = 1003;\n      int64_t bo8_remain = 803;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao9: won't expire, partially match before hard fork core-604, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao9 ..\" ); // 1:3\n      limit_order_id_type ao9_id = create_sell_order( alice_id, asset(909), asset(2727, usd_id) )->id;\n      BOOST_CHECK( db.find( ao9_id ) != nullptr );\n      create_sell_order( bob_id, asset(606, usd_id), asset(202) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 909;\n      alice_bu += 606;\n      bob_bc -= order_create_fee;\n      bob_bu -= 606;\n      bob_bc += 202;\n      int64_t ao9_remain = 909 - 202;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao10: will expire, will partially match before hard fork core-604, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao10 ..\" ); // 1:2\n      limit_order_id_type ao10_id = create_sell_order( alice_id, asset(707), asset(1414, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao10_id ) != nullptr );\n      create_sell_order( bob_id, asset(202, usd_id), asset(101) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 707;\n      alice_bu += 202;\n      bob_bc -= order_create_fee;\n      bob_bu -= 202;\n      bob_bc += 101;\n      int64_t ao10_remain = 707 - 101;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo9: won't expire, will partially match before hard fork core-604, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo9 ..\" ); // 1:3\n      limit_order_id_type bo9_id = create_sell_order(   bob_id, asset(505, usd_id), asset(1515), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo9_id ) != nullptr );\n      create_sell_order( alice_id, asset(453), asset(151, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 453;\n      alice_bu += 151;\n      bob_bu -= usd_create_fee;\n      bob_bu -= 505;\n      bob_bc += 453;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo9_remain = 505 - 151;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo10: will expire, will partially match before hard fork core-604, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo10 ..\" ); // 1:2\n      limit_order_id_type bo10_id = create_sell_order(   bob_id, asset(302, usd_id), asset(604), exp, cer )->id;\n      BOOST_CHECK( db.find( bo10_id ) != nullptr );\n      create_sell_order( alice_id, asset(142), asset(71, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 142;\n      alice_bu += 71;\n      bob_bu -= usd_create_fee;\n      bob_bu -= 302;\n      bob_bc += 142;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo10_remain = 302 - 71;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao13: won't expire, partially match after hard fork core-604, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao13 ..\" ); // 1:1.5\n      limit_order_id_type ao13_id = create_sell_order( alice_id, asset(424), asset(636, usd_id) )->id;\n      BOOST_CHECK( db.find( ao13_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 424;\n      int64_t ao13_remain = 424;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // ao14: will expire, partially match after hard fork core-604, fee in core\n      BOOST_TEST_MESSAGE( \"Creating order ao14 ..\" ); // 1:1.2\n      limit_order_id_type ao14_id = create_sell_order( alice_id, asset(525), asset(630, usd_id), exp )->id;\n      BOOST_CHECK( db.find( ao14_id ) != nullptr );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 525;\n      int64_t ao14_remain = 525;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo13: won't expire, partially match after hard fork core-604, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo13 ..\" ); // 1:1.5\n      limit_order_id_type bo13_id = create_sell_order(   bob_id, asset(364, usd_id), asset(546), max_exp, cer )->id;\n      BOOST_CHECK( db.find( bo13_id ) != nullptr );\n\n      bob_bu -= usd_create_fee;\n      bob_bu -= 364;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo13_remain = 364;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // bo14: will expire, partially match after hard fork core-604, fee in usd\n      BOOST_TEST_MESSAGE( \"Creating order bo14 ..\" ); // 1:1.2\n      limit_order_id_type bo14_id = create_sell_order(   bob_id, asset(365, usd_id), asset(438), exp, cer )->id;\n      BOOST_CHECK( db.find( bo14_id ) != nullptr );\n\n      bob_bu -= usd_create_fee;\n      bob_bu -= 365;\n      pool_b -= core_create_fee;\n      accum_b += usd_create_fee;\n      int64_t bo14_remain = 365;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block so the orders will be in db before hard fork core-604\n      BOOST_TEST_MESSAGE( \"Generating blocks, passing hard fork core-604 ...\" );\n      generate_block( skip );\n\n      // generate blocks util hard fork core-604\n      generate_blocks( HARDFORK_CORE_604_TIME, true, skip );\n      generate_block( skip );\n\n      // nothing will change\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // partially fill ao14\n      BOOST_TEST_MESSAGE( \"Partially fill ao14 ..\" ); // 1:1.2\n      create_sell_order( bob_id, asset(72, usd_id), asset(60) );\n\n      alice_bu += 72;\n      bob_bc -= order_create_fee;\n      bob_bu -= 72;\n      bob_bc += 60;\n      ao14_remain -= 60;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo14\n      BOOST_TEST_MESSAGE( \"Partially fill bo14 ..\" ); // 1:1.2\n      create_sell_order( alice_id, asset(66), asset(55, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 66;\n      alice_bu += 55;\n      bob_bc += 66;\n      bo14_remain -= 55;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block to save the changes\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate more blocks, so some orders will expire\n      generate_blocks( exp, true, skip );\n\n      // don't refund fee, only refund remaining funds, for:\n      // * orders created before hard fork 445 : ao2, ao4, ao6, bo2, bo4, bo6\n      // * partially filled orders (cancellation fee capped at 0) : ao10, ao14, bo10, bo14\n      BOOST_TEST_MESSAGE( \"Checking expired orders: ao2, ao4, ao6, ao10, ao14, bo2, bo4, bo6, bo10, bo14 ..\" );\n      alice_bc += ao2_remain;\n      alice_bc += ao4_remain;\n      //alice_bc += ao6_remain; // can only have ao5 or ao6 but not both\n      alice_bc += ao10_remain;\n      alice_bc += ao14_remain;\n      bob_bu += bo2_remain;\n      bob_bu += bo4_remain;\n      bob_bu += bo6_remain; // can only have bo5 or bo6 but not both\n      bob_bu += bo10_remain;\n      bob_bu += bo14_remain;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // partially fill ao13\n      BOOST_TEST_MESSAGE( \"Partially fill ao13 ..\" ); // 1:1.5\n      create_sell_order( bob_id, asset(78, usd_id), asset(52) );\n\n      alice_bu += 78;\n      bob_bc -= order_create_fee;\n      bob_bu -= 78;\n      bob_bc += 52;\n      ao13_remain -= 52;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo13\n      BOOST_TEST_MESSAGE( \"Partially fill bo13 ..\" ); // 1:1.5\n      create_sell_order( alice_id, asset(63), asset(42, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 63;\n      alice_bu += 42;\n      bob_bc += 63;\n      bo13_remain -= 42;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // don't refund fee, only refund remaining funds, for manually cancellations with an explicit fee:\n      // * orders created before hard fork 445 : ao1, ao3, ao5, bo1, bo3, bo5\n      // * partially filled orders (cancellation fee capped at 0) : ao9, ao13, bo9, bo13\n\n      // cancel ao1\n      BOOST_TEST_MESSAGE( \"Cancel order ao1 ..\" );\n      cancel_limit_order( ao1_id(db) );\n\n      alice_bc += ao1_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo1\n      BOOST_TEST_MESSAGE( \"Cancel order bo1 ..\" );\n      cancel_limit_order( bo1_id(db) );\n\n      bob_bu += bo1_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel ao3\n      BOOST_TEST_MESSAGE( \"Cancel order ao3 ..\" );\n      cancel_limit_order( ao3_id(db) );\n\n      alice_bc += ao3_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo3\n      BOOST_TEST_MESSAGE( \"Cancel order bo3 ..\" );\n      cancel_limit_order( bo3_id(db) );\n\n      bob_bu += bo3_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel ao5\n      BOOST_TEST_MESSAGE( \"Cancel order ao5 ..\" );\n      cancel_limit_order( ao5_id(db) );\n\n      alice_bc += ao5_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo5\n      if( false ) { // can only have bo5 or bo6 but not both\n      BOOST_TEST_MESSAGE( \"Cancel order bo5 ..\" );\n      //cancel_limit_order( bo5_id(db) );\n\n      //bob_bu += bo5_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n      }\n\n      // cancel ao9\n      BOOST_TEST_MESSAGE( \"Cancel order ao9 ..\" );\n      cancel_limit_order( ao9_id(db) );\n\n      alice_bc += ao9_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo9\n      BOOST_TEST_MESSAGE( \"Cancel order bo9 ..\" );\n      cancel_limit_order( bo9_id(db) );\n\n      bob_bu += bo9_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel ao13\n      BOOST_TEST_MESSAGE( \"Cancel order ao13 ..\" );\n      cancel_limit_order( ao13_id(db) );\n\n      alice_bc += ao13_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo13\n      BOOST_TEST_MESSAGE( \"Cancel order bo13 ..\" );\n      cancel_limit_order( bo13_id(db) );\n\n      bob_bu += bo13_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n\n      // generate block to save the changes\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate blocks util exp1, so some orders will expire\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n      generate_blocks( exp1, true, skip );\n\n      // orders created after hard fork 445 but before core-604, no partially filled,\n      // will refund remaining funds, and will refund create fee in core (minus cancel fee, capped)\n      BOOST_TEST_MESSAGE( \"Checking expired orders: ao8, bo8 ..\" );\n      alice_bc += ao8_remain;\n      alice_bc += std::max(order_create_fee - order_cancel_fee, int64_t(0));\n      bob_bu += bo8_remain;\n      bob_bc += std::max(core_create_fee - order_cancel_fee, int64_t(0));\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // orders created after hard fork 445 but before core-604, no partially filled,\n      // when manually cancelling (with an explicit fee),\n      // will refund remaining funds, and will refund create fee in core\n\n      // cancel ao7\n      BOOST_TEST_MESSAGE( \"Cancel order ao7 ..\" );\n      cancel_limit_order( ao7_id(db) );\n\n      alice_bc += ao7_remain;\n      alice_bc -= order_cancel_fee;\n      alice_bc += order_create_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo7\n      BOOST_TEST_MESSAGE( \"Cancel order bo7 ..\" );\n      cancel_limit_order( bo7_id(db) );\n\n      bob_bu += bo7_remain;\n      bob_bc -= order_cancel_fee;\n      bob_bc += core_create_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill ao12\n      BOOST_TEST_MESSAGE( \"Partially fill ao12 ..\" ); // 1:16\n      create_sell_order( bob_id, asset(688, usd_id), asset(43) );\n\n      alice_bu += 688;\n      bob_bc -= order_create_fee;\n      bob_bu -= 688;\n      bob_bc += 43;\n      ao12_remain -= 43;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo12\n      BOOST_TEST_MESSAGE( \"Partially fill bo12 ..\" ); // 1:17\n      create_sell_order( alice_id, asset(629), asset(37, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 629;\n      alice_bu += 37;\n      bob_bc += 629;\n      bo12_remain -= 37;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block to save the changes\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate blocks util exp2, so some orders will expire\n      generate_blocks( exp2, true, skip );\n\n      // no fee refund for orders created before hard fork 445, cancellation fee capped at 0\n      // remaining funds will be refunded\n      BOOST_TEST_MESSAGE( \"Checking expired orders: ao12, bo12 ..\" );\n      alice_bc += ao12_remain;\n      bob_bu += bo12_remain;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // prepare for new transactions\n      enable_fees();\n      change_fees( new_fees );\n      usd_stat = &usd_id( db ).dynamic_asset_data_id( db );\n      set_expiration( db, trx );\n\n      // partially fill ao11\n      BOOST_TEST_MESSAGE( \"Partially fill ao11 ..\" ); // 1:18\n      create_sell_order( bob_id, asset(1422, usd_id), asset(79) );\n\n      alice_bu += 1422;\n      bob_bc -= order_create_fee;\n      bob_bu -= 1422;\n      bob_bc += 79;\n      ao11_remain -= 79;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // partially fill bo11\n      BOOST_TEST_MESSAGE( \"Partially fill bo11 ..\" ); // 1:18\n      create_sell_order( alice_id, asset(1494), asset(83, usd_id) );\n\n      alice_bc -= order_create_fee;\n      alice_bc -= 1494;\n      alice_bu += 83;\n      bob_bc += 1494;\n      bo11_remain -= 83;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // no fee refund for orders created before hard fork 445, if manually cancelled with an explicit fee.\n      // remaining funds will be refunded\n\n      // cancel ao11\n      BOOST_TEST_MESSAGE( \"Cancel order ao11 ..\" );\n      cancel_limit_order( ao11_id(db) );\n\n      alice_bc += ao11_remain;\n      alice_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // cancel bo11\n      BOOST_TEST_MESSAGE( \"Cancel order bo11 ..\" );\n      cancel_limit_order( bo11_id(db) );\n\n      bob_bu += bo11_remain;\n      bob_bc -= order_cancel_fee;\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n      // generate block to save the changes\n      BOOST_TEST_MESSAGE( \"Generating blocks ...\" );\n      generate_block( skip );\n\n      BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc );\n      BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_bu );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_bc );\n      BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_bu );\n      BOOST_CHECK_EQUAL( usd_stat->fee_pool.value,          pool_b );\n      BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value,  accum_b );\n\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( stealth_fba_test )\n{\n   try\n   {\n      ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) );\n      upgrade_to_lifetime_member(philbin_id);\n\n      generate_blocks( HARDFORK_555_TIME );\n      generate_blocks( HARDFORK_563_TIME );\n\n      // Philbin (registrar who registers Rex)\n\n      // Izzy (initial issuer of stealth asset, will later transfer to Tom)\n      // Alice, Bob, Chloe, Dan (ABCD)\n      // Rex (recycler -- buyback account for stealth asset)\n      // Tom (owner of stealth asset who will be set as top_n authority)\n\n      // Izzy creates STEALTH\n      asset_id_type stealth_id = create_user_issued_asset( \"STEALTH\", izzy_id(db),\n         disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id;\n\n      /*\n      // this is disabled because it doesn't work, our modify() is probably being overwritten by undo\n\n      //\n      // Init blockchain with stealth ID's\n      // On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET\n      // causing the designated_asset fields of these objects to be set at genesis, but for\n      // this test we modify the db directly.\n      //\n      auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id )\n      {\n         db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba )\n         {\n            fba.designated_asset = asset_id;\n         } );\n      };\n\n      set_fba_asset( fba_accumulator_id_transfer_to_blind  , stealth_id );\n      set_fba_asset( fba_accumulator_id_blind_transfer     , stealth_id );\n      set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id );\n      */\n\n      // Izzy kills some permission bits (this somehow happened to the real STEALTH in production)\n      {\n         asset_update_operation update_op;\n         update_op.issuer = izzy_id;\n         update_op.asset_to_update = stealth_id;\n         asset_options new_options;\n         new_options = stealth_id(db).options;\n         new_options.issuer_permissions = charge_market_fee;\n         new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee;\n         // after fixing #579 you should be able to delete the following line\n         new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) );\n         update_op.new_options = new_options;\n         signed_transaction tx;\n         tx.operations.push_back( update_op );\n         set_expiration( db, tx );\n         sign( tx, izzy_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      // Izzy transfers issuer duty to Tom\n      {\n         asset_update_operation update_op;\n         update_op.issuer = izzy_id;\n         update_op.asset_to_update = stealth_id;\n         update_op.new_issuer = tom_id;\n         // new_options should be optional, but isn't...the following line should be unnecessary #580\n         update_op.new_options = stealth_id(db).options;\n         signed_transaction tx;\n         tx.operations.push_back( update_op );\n         set_expiration( db, tx );\n         sign( tx, izzy_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      // Tom re-enables the permission bits to clear the flags, then clears them again\n      // Allowed by #572 when current_supply == 0\n      {\n         asset_update_operation update_op;\n         update_op.issuer = tom_id;\n         update_op.asset_to_update = stealth_id;\n         asset_options new_options;\n         new_options = stealth_id(db).options;\n         new_options.issuer_permissions = new_options.flags | charge_market_fee;\n         update_op.new_options = new_options;\n         signed_transaction tx;\n         // enable perms is one op\n         tx.operations.push_back( update_op );\n\n         new_options.issuer_permissions = charge_market_fee;\n         new_options.flags = charge_market_fee;\n         update_op.new_options = new_options;\n         // reset wrongly set flags and reset permissions can be done in a single op\n         tx.operations.push_back( update_op );\n\n         set_expiration( db, tx );\n         sign( tx, tom_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      // Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom)\n      account_id_type rex_id;\n      {\n         buyback_account_options bbo;\n         bbo.asset_to_buy = stealth_id;\n         bbo.asset_to_buy_issuer = tom_id;\n         bbo.markets.emplace( asset_id_type() );\n         account_create_operation create_op = make_account( \"rex\" );\n         create_op.registrar = philbin_id;\n         create_op.extensions.value.buyback_options = bbo;\n         create_op.owner = authority::null_authority();\n         create_op.active = authority::null_authority();\n\n         signed_transaction tx;\n         tx.operations.push_back( create_op );\n         set_expiration( db, tx );\n         sign( tx, philbin_private_key );\n         sign( tx, tom_private_key );\n\n         processed_transaction ptx = PUSH_TX( db, tx );\n         rex_id = ptx.operation_results.back().get< object_id_type >();\n      }\n\n      // Tom issues some asset to Alice and Bob\n      set_expiration( db, trx );  // #11\n      issue_uia( alice_id, asset( 1000, stealth_id ) );\n      issue_uia(   bob_id, asset( 1000, stealth_id ) );\n\n      // Tom sets his authority to the top_n of the asset\n      {\n         top_holders_special_authority top2;\n         top2.num_top_holders = 2;\n         top2.asset = stealth_id;\n\n         account_update_operation op;\n         op.account = tom_id;\n         op.extensions.value.active_special_authority = top2;\n         op.extensions.value.owner_special_authority = top2;\n\n         signed_transaction tx;\n         tx.operations.push_back( op );\n\n         set_expiration( db, tx );\n         sign( tx, tom_private_key );\n\n         PUSH_TX( db, tx );\n      }\n\n      // Wait until the next maintenance interval for top_n to take effect\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      // Do a blind op to add some fees to the pool.\n      fund( chloe_id(db), asset( 100000, asset_id_type() ) );\n\n      auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation\n      {\n         fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+\"-privkey\" ) );\n         public_key_type blind_pub = blind_key.get_public_key();\n\n         fc::sha256 secret = fc::sha256::hash( key+\"-secret\" );\n         fc::sha256 nonce = fc::sha256::hash( key+\"-nonce\" );\n\n         transfer_to_blind_operation op;\n         blind_output blind_out;\n         blind_out.owner = authority( 1, blind_pub, 1 );\n         blind_out.commitment = fc::ecc::blind( secret, amount.amount.value );\n         blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value );\n\n         op.amount = amount;\n         op.from = account;\n         op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 );\n         op.outputs = {blind_out};\n\n         return op;\n      };\n\n      {\n         transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), \"chloe-key\" );\n         op.fee = asset( 1000, asset_id_type() );\n\n         signed_transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         sign( tx, chloe_private_key );\n\n         PUSH_TX( db, tx );\n      }\n\n      // wait until next maint interval\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      idump( ( get_operation_history( chloe_id ) ) );\n      idump( ( get_operation_history( rex_id ) ) );\n      idump( ( get_operation_history( tom_id ) ) );\n   }\n   catch( const fc::exception& e )\n   {\n      elog( \"caught exception ${e}\", (\"e\", e.to_detail_string()) );\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( defaults_test )\n{ try {\n    fee_schedule schedule;\n    const limit_order_create_operation::fee_parameters_type default_order_fee {};\n\n    // no fees set yet -> default\n    asset fee = schedule.calculate_fee( limit_order_create_operation() );\n    BOOST_CHECK_EQUAL( (int64_t)default_order_fee.fee, fee.amount.value );\n\n    limit_order_create_operation::fee_parameters_type new_order_fee; new_order_fee.fee = 123;\n    // set fee + check\n    schedule.parameters.insert( new_order_fee );\n    fee = schedule.calculate_fee( limit_order_create_operation() );\n    BOOST_CHECK_EQUAL( (int64_t)new_order_fee.fee, fee.amount.value );\n\n    // bid_collateral fee defaults to call_order_update fee\n    // call_order_update fee is unset -> default\n    const call_order_update_operation::fee_parameters_type default_short_fee {};\n    call_order_update_operation::fee_parameters_type new_short_fee; new_short_fee.fee = 123;\n    fee = schedule.calculate_fee( bid_collateral_operation() );\n    BOOST_CHECK_EQUAL( (int64_t)default_short_fee.fee, fee.amount.value );\n\n    // set call_order_update fee + check bid_collateral fee\n    schedule.parameters.insert( new_short_fee );\n    fee = schedule.calculate_fee( bid_collateral_operation() );\n    BOOST_CHECK_EQUAL( (int64_t)new_short_fee.fee, fee.amount.value );\n\n    // set bid_collateral fee + check\n    bid_collateral_operation::fee_parameters_type new_bid_fee; new_bid_fee.fee = 124;\n    schedule.parameters.insert( new_bid_fee );\n    fee = schedule.calculate_fee( bid_collateral_operation() );\n    BOOST_CHECK_EQUAL( (int64_t)new_bid_fee.fee, fee.amount.value );\n  }\n  catch( const fc::exception& e )\n  {\n     elog( \"caught exception ${e}\", (\"e\", e.to_detail_string()) );\n     throw;\n  }\n}\n\nBOOST_AUTO_TEST_CASE( sub_asset_creation_fee_test )\n{ try {\n   fee_schedule schedule;\n\n   asset_create_operation::fee_parameters_type default_ac_fee;\n\n   asset_create_operation op;\n   op.symbol = \"TEST.SUB\";\n\n   auto op_size = fc::raw::pack_size(op);\n\n   auto expected_data_fee = op.calculate_data_fee( op_size, default_ac_fee.price_per_kbyte );\n   int64_t expected_fee = default_ac_fee.long_symbol + expected_data_fee;\n\n   // no fees set yet -> default\n   BOOST_TEST_MESSAGE(\"Testing default fee schedule\");\n   asset fee = schedule.calculate_fee( op );\n   BOOST_CHECK_EQUAL( fee.amount.value, expected_fee );\n\n   // set fee + check\n   asset_create_operation::fee_parameters_type ac_fee;\n   ac_fee.long_symbol = 100100;\n   ac_fee.symbol4 = 2000200;\n   ac_fee.symbol3 = 30000300;\n   ac_fee.price_per_kbyte = 1050;\n\n   schedule.parameters.insert( ac_fee );\n\n   expected_data_fee = op.calculate_data_fee( op_size, ac_fee.price_per_kbyte );\n   expected_fee = ac_fee.long_symbol + expected_data_fee;\n\n   fee = schedule.calculate_fee( op );\n   BOOST_CHECK_EQUAL( fee.amount.value, expected_fee );\n\n   // set fee for account_transfer_operation, no change on asset creation fee\n   BOOST_TEST_MESSAGE(\"Testing our fee schedule without sub-asset creation fee enabled\");\n   account_transfer_operation::fee_parameters_type at_fee;\n   at_fee.fee = 5500;\n\n   schedule.parameters.insert( at_fee );\n\n   fee = schedule.calculate_fee( op );\n   BOOST_CHECK_EQUAL( fee.amount.value, expected_fee );\n\n   // enable sub-asset creation fee\n   BOOST_TEST_MESSAGE(\"Testing our fee schedule with sub-asset creation fee enabled\");\n   schedule.parameters.insert( ticket_create_operation::fee_parameters_type() );\n\n   expected_fee = at_fee.fee + expected_data_fee;\n\n   fee = schedule.calculate_fee( op );\n   BOOST_CHECK_EQUAL( fee.amount.value, expected_fee );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( issue_429_test )\n{\n   try\n   {\n      ACTORS((alice));\n\n      transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( asset_id_type()(db).precision ) ) );\n\n      // make sure the database requires our fee to be nonzero\n      enable_fees();\n\n      const auto& fees = db.get_global_properties().parameters.get_current_fees();\n      auto fees_to_pay = fees.get<asset_create_operation>();\n\n      {\n         signed_transaction tx;\n         asset_create_operation op;\n         op.issuer = alice_id;\n         op.symbol = \"ALICE\";\n         op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );\n         op.fee = asset( (fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) & (~1) );\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         sign( tx, alice_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      verify_asset_supplies( db );\n\n      {\n         signed_transaction tx;\n         asset_create_operation op;\n         op.issuer = alice_id;\n         op.symbol = \"ALICE.ODD\";\n         op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );\n         op.fee = asset((fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) | 1);\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         sign( tx, alice_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      verify_asset_supplies( db );\n\n      generate_blocks( HARDFORK_CORE_429_TIME + 10 );\n\n      {\n         signed_transaction tx;\n         asset_create_operation op;\n         op.issuer = alice_id;\n         op.symbol = \"ALICE.ODDER\";\n         op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );\n         op.fee = asset((fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) | 1);\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         sign( tx, alice_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      verify_asset_supplies( db );\n   }\n   catch( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( issue_433_test )\n{\n   try\n   {\n      ACTORS((alice));\n\n      auto& core = asset_id_type()(db);\n\n      transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( core.precision ) ) );\n\n      const auto& myusd = create_user_issued_asset( \"MYUSD\", alice, 0 );\n      issue_uia( alice, myusd.amount( 2000000000 ) );\n\n      // make sure the database requires our fee to be nonzero\n      enable_fees();\n\n      const auto& fees = db.get_global_properties().parameters.get_current_fees();\n      const auto asset_create_fees = fees.get<asset_create_operation>();\n\n      fund_fee_pool( alice, myusd, 5*asset_create_fees.long_symbol );\n\n      asset_create_operation op;\n      op.issuer = alice_id;\n      op.symbol = \"ALICE\";\n      op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );\n      op.fee = myusd.amount( ((asset_create_fees.long_symbol + asset_create_fees.price_per_kbyte) & (~1)) );\n      signed_transaction tx;\n      tx.operations.push_back( op );\n      set_expiration( db, tx );\n      sign( tx, alice_private_key );\n      PUSH_TX( db, tx );\n\n      verify_asset_supplies( db );\n   }\n   catch( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( issue_433_indirect_test )\n{\n   try\n   {\n      ACTORS((alice));\n\n      auto& core = asset_id_type()(db);\n\n      transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( core.precision ) ) );\n\n      const auto& myusd = create_user_issued_asset( \"MYUSD\", alice, 0 );\n      issue_uia( alice, myusd.amount( 2000000000 ) );\n\n      // make sure the database requires our fee to be nonzero\n      enable_fees();\n\n      const auto& fees = db.get_global_properties().parameters.get_current_fees();\n      const auto asset_create_fees = fees.get<asset_create_operation>();\n\n      fund_fee_pool( alice, myusd, 5*asset_create_fees.long_symbol );\n\n      asset_create_operation op;\n      op.issuer = alice_id;\n      op.symbol = \"ALICE\";\n      op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );\n      op.fee = myusd.amount( ((asset_create_fees.long_symbol + asset_create_fees.price_per_kbyte) & (~1)) );\n\n      const auto proposal_create_fees = fees.get<proposal_create_operation>();\n      proposal_create_operation prop;\n      prop.fee_paying_account = alice_id;\n      prop.proposed_ops.emplace_back( op );\n      prop.expiration_time =  db.head_block_time() + fc::days(1);\n      prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n      object_id_type proposal_id;\n      {\n         signed_transaction tx;\n         tx.operations.push_back( prop );\n         set_expiration( db, tx );\n         sign( tx, alice_private_key );\n         proposal_id = PUSH_TX( db, tx ).operation_results.front().get<object_id_type>();\n      }\n      const proposal_object& proposal = db.get<proposal_object>( proposal_id );\n\n      const auto proposal_update_fees = fees.get<proposal_update_operation>();\n      proposal_update_operation pup;\n      pup.proposal = proposal.id;\n      pup.fee_paying_account = alice_id;\n      pup.active_approvals_to_add.insert(alice_id);\n      pup.fee = asset( proposal_update_fees.fee + proposal_update_fees.price_per_kbyte );\n      {\n         signed_transaction tx;\n         tx.operations.push_back( pup );\n         set_expiration( db, tx );\n         sign( tx, alice_private_key );\n         PUSH_TX( db, tx );\n      }\n\n      verify_asset_supplies( db );\n   }\n   catch( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/force_settle_fee_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 Michel Santos, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/market.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nstruct force_settle_database_fixture : database_fixture {\n   force_settle_database_fixture()\n           : database_fixture() {\n   }\n\n   /**\n    * Create an operation to create a smart asset\n    * @param name Asset name\n    * @param issuer Issuer ID\n    * @param force_settlement_offset_percent Force-settlement offset percent\n    * @param force_settlement_fee_percent Force-settlement fee percent (BSIP87)\n    * @return An asset_create_operation\n    */\n   const asset_create_operation create_smart_asset_op(\n           const string &name,\n           account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */,\n           uint16_t force_settlement_offset_percent /* 100 = 1% */,\n           optional<uint16_t> force_settlement_fee_percent /* 100 = 1% */\n   ) {\n      try {\n         uint16_t market_fee_percent = 100; /*1%*/\n         uint16_t flags = charge_market_fee;\n         uint16_t precision = 2;\n         asset_id_type backing_asset = {};\n         share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n\n         asset_create_operation creator;\n         creator.issuer = issuer;\n         creator.fee = asset();\n         creator.symbol = name;\n         creator.precision = precision;\n\n         creator.common_options.max_supply = max_supply;\n         creator.common_options.market_fee_percent = market_fee_percent;\n         if (issuer == GRAPHENE_WITNESS_ACCOUNT)\n            flags |= witness_fed_asset;\n         creator.common_options.issuer_permissions = flags;\n         creator.common_options.flags = flags & ~global_settle;\n         creator.common_options.core_exchange_rate = price(asset(1, asset_id_type(1)), asset(1));\n\n         creator.bitasset_opts = bitasset_options();\n         creator.bitasset_opts->force_settlement_offset_percent = force_settlement_offset_percent;\n         creator.bitasset_opts->short_backing_asset = backing_asset;\n         creator.bitasset_opts->extensions.value.force_settle_fee_percent = force_settlement_fee_percent;\n\n         return creator;\n\n      } FC_CAPTURE_AND_RETHROW((name)(issuer))\n   }\n\n\n   /**\n    * Create a smart asset without a force settlement fee percent\n    * @param name Asset name\n    * @param issuer Issuer ID\n    * @param force_settlement_offset_percent Force-settlement offset percent\n    * @return Asset object\n    */\n   const asset_object &create_smart_asset(\n           const string &name,\n           account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */,\n           uint16_t force_settlement_offset_percent /* = 100 */ /* 1% */\n   ) {\n      try {\n         optional<uint16_t> force_settlement_fee_percent; // Not specified\n         return create_smart_asset(name, issuer, force_settlement_offset_percent, force_settlement_fee_percent);\n      } FC_CAPTURE_AND_RETHROW((name)(issuer)(force_settlement_offset_percent))\n   }\n\n\n   /**\n    * Create a smart asset\n    * @param name Asset name\n    * @param issuer Issuer ID\n    * @param force_settlement_offset_percent Force-settlement offset percent\n    * @param force_settlement_fee_percent Force-settlement fee percent (BSIP87)\n    * @return Asset object\n    */\n   const asset_object &create_smart_asset(\n           const string &name,\n           account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */,\n           uint16_t force_settlement_offset_percent /* 100 = 1% */,\n           optional<uint16_t> force_settlement_fee_percent /* 100 = 1% */\n   ) {\n      try {\n         asset_create_operation creator = create_smart_asset_op(name, issuer, force_settlement_offset_percent,\n                                                                force_settlement_fee_percent);\n\n         trx.operations.push_back(std::move(creator));\n         trx.validate();\n         processed_transaction ptx = PUSH_TX(db, trx, ~0);\n         trx.operations.clear();\n         return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());\n      } FC_CAPTURE_AND_RETHROW((name)(issuer))\n   }\n};\n\n\n/**\n * Test the effects of the new force settlement fee from BSIP87\n */\nBOOST_FIXTURE_TEST_SUITE(force_settle_tests, force_settle_database_fixture)\n\n   /**\n    * Test when one holder of a smart asset force settles (FS) their holding when there are two debtors\n    *\n    * There are three primary actors: michael, paul, rachel\n    *\n    * 1. Asset owner creates the smart coin called bitUSD\n    * 2. The feed price is 20 satoshi bitUSD for 1 satoshi Core -> 0.2 bitUSD for 0.00001 Core = 20000 bitUSD for 1 Core\n    * 3. Michael borrows 0.06 bitUSD (6 satoshis of bitUSD) from the blockchain with a high amount of collateral\n    * 4. Paul borrows 1000 bitUSD (100000 satoshis of bitUSD) from the blockchain with a low amount of collateral\n    * 5. Paul gives Rachel 200 bitUSD\n    * 6. Rachel force-settles 20 bitUSD which should be collected from Paul's debt position\n    * because of its relatively lower collateral ratio\n    *\n    * The force-settlement by Rachel should account for both the force-settlement offset fee,\n    * and the new force settlement fee from BSIP87.\n    *\n    * Michael's debt and balances should be unaffected by the activities of Paul and Rachel\n    */\n   BOOST_AUTO_TEST_CASE(force_settle_fee_1_test) {\n      try {\n         ///////\n         // Initialize the scenario\n         ///////\n         // Get around Graphene issue #615 feed expiration bug\n         generate_blocks(HARDFORK_615_TIME);\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         // Advance to the when the force-settlement fee activates\n         generate_blocks(HARDFORK_CORE_BSIP87_TIME);\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         // Create actors\n         ACTORS((assetowner)(feedproducer)(paul)(michael)(rachel));\n\n         // Fund actors\n         uint64_t initial_balance_core = 10000000;\n         transfer(committee_account, assetowner.id, asset(initial_balance_core));\n         transfer(committee_account, feedproducer.id, asset(initial_balance_core));\n         transfer(committee_account, michael_id, asset(initial_balance_core));\n         transfer(committee_account, paul.id, asset(initial_balance_core));\n\n         // 1. Create assets\n         const uint16_t usd_fso_percent = 5 * GRAPHENE_1_PERCENT; // 5% Force-settlement offset fee %\n         const uint16_t usd_fsf_percent = 3 * GRAPHENE_1_PERCENT; // 3% Force-settlement fee % (BSIP87)\n         create_smart_asset(\"USDBIT\", assetowner.id, usd_fso_percent, usd_fsf_percent);\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bitusd = get_asset(\"USDBIT\");\n         const auto &core = asset_id_type()(db);\n         asset_id_type bitusd_id = bitusd.id;\n         asset_id_type core_id = core.id;\n         const int64_t bitusd_unit = asset::scaled_precision(bitusd.precision).value; // 100 satoshi USDBIT in 1 USDBIT\n\n         // 2. Publish a feed for the smart asset\n         update_feed_producers(bitusd_id(db), {feedproducer_id});\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         // Requirement of 20 satoshi bitUSD for 1 satoshi Core -> 0.2 bitUSD for 0.00001 Core = 20000 bitUSD for 1 Core\n         current_feed.settlement_price = bitusd.amount(20) / core.amount(1);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         ///////\n         // 3. Michael borrows 0.06 bitUSD\n         ///////\n         int64_t michael_initial_usd = 6; // 0.06 USD\n         int64_t michael_initial_core = 8;\n         const call_order_object &call_michael = *borrow(michael, bitusd.amount(michael_initial_usd),\n                                                         core.amount(michael_initial_core));\n         call_order_id_type call_michael_id = call_michael.id;\n\n         BOOST_CHECK_EQUAL(get_balance(michael, bitusd), michael_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(michael, core), initial_balance_core - michael_initial_core);\n\n\n         ///////\n         // 4. Paul borrows 1000 bitUSD\n         ///////\n         // Paul will borrow bitUSD by providing 2x collateral required: 2 * 1/20 = 1/10\n         int64_t paul_initial_usd = 1000 * bitusd_unit; // 100000\n         int64_t paul_initial_core = paul_initial_usd * 2 / 20; // 10000\n         const call_order_object &call_paul = *borrow(paul, bitusd.amount(paul_initial_usd),\n                                                      core.amount(paul_initial_core));\n         call_order_id_type call_paul_id = call_paul.id;\n         BOOST_REQUIRE_EQUAL(get_balance(paul, bitusd), paul_initial_usd);\n\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 5. Paul transfers 200 bitUSD to Rachel\n         ///////\n         int64_t rachel_initial_usd = 200 * bitusd_unit;\n         transfer(paul.id, rachel.id, asset(rachel_initial_usd, bitusd.id));\n\n         BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd);\n\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 6. Rachel force settles 20 bitUSD\n         ///////\n         const int64_t rachel_settle_amount = 20 * bitusd_unit;\n         operation_result result = force_settle(rachel, bitusd.amount(rachel_settle_amount));\n\n         force_settlement_id_type rachel_settle_id = result.get<object_id_type>();\n         BOOST_CHECK_EQUAL(rachel_settle_id(db).balance.amount.value, rachel_settle_amount);\n\n         // Check Rachel's balance\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd - rachel_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         BOOST_CHECK_EQUAL(paul_initial_usd, call_paul.debt.value);\n         BOOST_CHECK_EQUAL(paul_initial_core, call_paul.collateral.value);\n\n         // Check Michael's balance\n         BOOST_CHECK_EQUAL(get_balance(michael, bitusd), michael_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(michael, core), initial_balance_core - michael_initial_core);\n\n         // Check Michael's debt to the blockchain\n         BOOST_CHECK_EQUAL(michael_initial_usd, call_michael.debt.value);\n         BOOST_CHECK_EQUAL(michael_initial_core, call_michael.collateral.value);\n\n\n         ///////\n         // Advance time and update the price feed\n         ///////\n         generate_blocks(db.head_block_time() + fc::hours(20));\n         set_expiration(db, trx);\n         trx.clear();\n\n         // The default feed and settlement expires at the same time\n         // Publish another feed to have a valid price to exit\n         publish_feed(bitusd_id(db), feedproducer_id(db), current_feed);\n\n\n         ///////\n         // Advance time to trigger the conclusion of the force settlement\n         ///////\n         generate_blocks(db.head_block_time() + fc::hours(6));\n         set_expiration(db, trx);\n         trx.clear();\n\n\n         //////\n         // Check\n         //////\n         // Rachel's settlement should have completed and should no longer be present\n         BOOST_CHECK(!db.find(rachel_settle_id));\n\n         // Check Rachel's balance\n         // Rachel redeemed some smart asset and should get the equivalent collateral amount (according to the feed price)\n         // minus the force_settlement_offset_fee - force_settlement_fee\n         // Rachel redeemed  20 USD (2000 satoshi bitUSD) and should get\n         // 100 satoshi Core - 5 satoshi Core - 2 satoshi Core; 3% * (100 - 5) = 2.85 trunacted to 2 satoshi Core\n         uint64_t rachel_settle_core = rachel_settle_amount * 1 / 20; // Settle amount * feed price\n         uint64_t rachel_fso_fee_core = rachel_settle_core * usd_fso_percent / GRAPHENE_100_PERCENT; // 5 satoshi Core\n         uint64_t rachel_fso_remainder_core = rachel_settle_core - rachel_fso_fee_core; // 95 satoshi Core\n         uint64_t rachel_fsf_fee_core =\n                 (rachel_fso_remainder_core) * usd_fsf_percent / GRAPHENE_100_PERCENT; // 2 satoshi Core\n         uint64_t expected_rachel_core =\n                 rachel_settle_core - rachel_fso_fee_core - rachel_fsf_fee_core; // 93 satoshi Core\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd - rachel_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), expected_rachel_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         // Rachel redeemed 20 usd from the blockchain, and the blockchain closed this amount from Paul's debt to it\n         BOOST_CHECK_EQUAL(paul_initial_usd - rachel_settle_amount, call_paul_id(db).debt.value);\n         // The call order has the original amount of collateral less what was redeemed by Rachel\n         BOOST_CHECK_EQUAL(paul_initial_core - rachel_fso_remainder_core, call_paul_id(db).collateral.value);\n\n         // Check Michael's balance\n         // Rachel's redemption should not have affected Michael's balance\n         BOOST_CHECK_EQUAL(get_balance(michael, bitusd), michael_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(michael, core), initial_balance_core - michael_initial_core);\n\n         // Check Michael's debt to the blockchain\n         // Rachel's redemption should not have affected Michael's debt to the blockchain\n         BOOST_CHECK_EQUAL(michael_initial_usd, call_michael_id(db).debt.value);\n         BOOST_CHECK_EQUAL(michael_initial_core, call_michael_id(db).collateral.value);\n\n         // The supply of USD equals the amount borrowed/created by Paul and Michael\n         // minus the amount redeemed/destroyed by Rachel\n         BOOST_CHECK_EQUAL(bitusd_id(db).dynamic_data(db).current_supply.value,\n                           paul_initial_usd + michael_initial_usd - rachel_settle_amount);\n\n         // Check the asset owner's vesting fees\n         // The market fee reward should be zero because the market fee reward % is 0\n         const auto assetowner_fs_fees_usd = get_market_fee_reward(assetowner, bitusd);\n         BOOST_CHECK_EQUAL(assetowner_fs_fees_usd, 0);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == rachel_fsf_fee_core);\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * This test evaluates:\n    *\n    * - collecting collateral-denominated fees before and after BSIP87,\n    * - applying different force-settlement fee percentages,\n    * - accumulating fees from multiple force-settlements,\n    * - changing the backing asset of a smart asset is prohibited when there are unclaimed collateral-denominated fees.\n    *\n    * There are five actors: asset owner, paul, rachel, michael, yanna, vikram\n    *\n    * Before HARDFORK_CORE_BSIP87_TIME\n    *\n    * 1. Asset owner creates the smart coin called bitUSD\n    *\n    * NOTE: To avoid rounding issues in the test, 1 satoshi of the smart asset will be worth more than 1 satoshi\n    * of the backing asset.  This allows force settlements of the smart asset to yield more satoshis of the backing asset\n    * with controllable truncation and rounding that will not affect the tests.\n    * 2. The feed price is 1 satoshi bitUSD for 20 satoshi Core = 0.01 bitUSD for 0.00020 Core = 50 bitUSD for 1 Core\n    *\n    * 3. Paul borrows 100 bitUSD (10000 satoshis of bitUSD) from the blockchain\n    * 4. Paul gives Rachel 20 bitUSD and retains 80 bitUSD\n    * 5. Rachel force-settles 2 bitUSD which should be collected from Paul's debt position\n    * 6. Asset owner attempts and fails to claim the collateral fees\n    *\n    *\n    * 7. Activate HARDFORK_CORE_BSIP87_TIME\n    *\n    *\n    * After HARDFORK_CORE_BSIP87_TIME\n    *\n    * 8. Paul gives Michael 30 bitUSD and retains 50 bitUSD\n    * 9. Michael force-settles 5 bitUSD which should be collected from Paul's debt position\n    *\n    * 10. Asset owner sets the force-fee percentage to 3%\n    * 11. Paul gives Yanna 40 bitUSD and retains 10 bitUSD\n    * 12. Yanna force-settles 10 bitUSD which should be collected from Paul's debt position\n    *\n    * 13. Asset owner updates the force-settlement fee to 4%\n    * 14. Paul gives Vikram 10 bitUSD and retains 0 bitUSD\n    * 15. Vikram force-settles 10 bitUSD which should be collected from Paul's debt position\n    *\n    * 16. Asset owner attempts and fails to change the backing of the smart asset because of its outstanding supply\n    * 17. All current holders of bitUSD close their bitUSD positions\n    * 18. Asset owner attempts and fails to change the backing of the smart asset because of unclaimed collateral fees\n    * 19. Asset owner claims all of the unclaimed collateral fees\n    * 20. Asset owner attempts and succeeds in changing the backing of the smart asset\n    */\n   BOOST_AUTO_TEST_CASE(force_settle_fee_2_test) {\n      try {\n         ///////\n         // Initialize the scenario\n         ///////\n         // Get around Graphene issue #615 feed expiration bug\n         generate_blocks(HARDFORK_615_TIME);\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Create actors\n         ACTORS((assetowner)(feedproducer)(paul)(rachel)(michael)(yanna)(vikram));\n\n         // Fund actors\n         uint64_t initial_balance_core = 10000000;\n         transfer(committee_account, assetowner.id, asset(initial_balance_core));\n         transfer(committee_account, feedproducer.id, asset(initial_balance_core));\n         transfer(committee_account, michael_id, asset(initial_balance_core));\n         transfer(committee_account, paul.id, asset(initial_balance_core));\n\n         ///////\n         // 1. Create assets\n         ///////\n         const uint16_t usd_fso_percent = 5 * GRAPHENE_1_PERCENT; // 5% Force-settlement offset fee %\n         const uint16_t usd_fsf_percent_0 = 0 * GRAPHENE_1_PERCENT; // 0% Force-settlement fee %\n\n         // Attempt and fail to create the smart asset with a force-settlement fee % before HARDFORK_CORE_BSIP87_TIME\n         trx.clear();\n         REQUIRE_EXCEPTION_WITH_TEXT(create_smart_asset(\"USDBIT\", assetowner.id, usd_fso_percent, usd_fsf_percent_0),\n                                     \"cannot be set before Hardfork BSIP87\");\n\n\n         // Create the smart asset without a force-settlement fee %\n         trx.clear();\n         create_smart_asset(\"USDBIT\", assetowner.id, usd_fso_percent);\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bitusd = get_asset(\"USDBIT\");\n         const auto &core = asset_id_type()(db);\n         const int64_t core_unit = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n         const int64_t bitusd_unit = asset::scaled_precision(bitusd.precision).value; // 100 satoshi USDBIT in 1 USDBIT\n\n\n         ///////\n         // 2. Publish a feed for the smart asset\n         ///////\n         update_feed_producers(bitusd.id, {feedproducer_id});\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         // Requirement of 20x collateral in satoshis: 1 satoshi bitUSD for 20 satoshi Core\n         // -> 0.01 bitUSD for 0.00020 Core = 100 bitUSD for 2 Core = 50 bitUSD for 1 Core\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(20);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         ///////\n         // 3. Paul borrows 100 bitUSD\n         ///////\n         // Paul will borrow bitUSD by providing 2x collateral required: 2 * 20 = 40\n         int64_t paul_initial_usd = 100 * bitusd_unit; // 10000\n         int64_t paul_initial_core = paul_initial_usd * 2 * 20; // 400000\n         const call_order_object &call_paul = *borrow(paul, bitusd.amount(paul_initial_usd),\n                                                      core.amount(paul_initial_core));\n         call_order_id_type call_paul_id = call_paul.id;\n         BOOST_REQUIRE_EQUAL(get_balance(paul, bitusd), paul_initial_usd);\n\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 4. Paul gives Rachel 20 bitUSD and retains 80 bitUSD\n         ///////\n         int64_t rachel_initial_usd = 20 * bitusd_unit;\n         transfer(paul.id, rachel.id, asset(rachel_initial_usd, bitusd.id));\n\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 5. Rachel force-settles 2 bitUSD which should be collected from Paul's debt position\n         ///////\n         const int64_t rachel_settle_amount = 2 * bitusd_unit;\n         operation_result result = force_settle(rachel, bitusd.amount(rachel_settle_amount));\n\n         force_settlement_id_type rachel_settle_id = result.get<object_id_type>();\n         BOOST_CHECK_EQUAL(rachel_settle_id(db).balance.amount.value, rachel_settle_amount);\n\n         // Advance time to complete the force settlement and to update the price feed\n         generate_blocks(db.head_block_time() + fc::hours(26));\n         set_expiration(db, trx);\n         trx.clear();\n         publish_feed(bitusd, feedproducer_id(db), current_feed);\n         trx.clear();\n\n         // Rachel's settlement should have completed and should no longer be present\n         BOOST_CHECK(!db.find(rachel_settle_id));\n\n         // Check Rachel's balance\n         // Rachel redeemed some smart asset and should get the equivalent collateral amount (according to the feed price)\n         // minus the force_settlement_offset_fee - force_settlement_fee\n         // Rachel redeemed 2 bitUSD and should get 4000 satoshi Core - 200 satoshi Core - 0 satoshi  Core\n         uint64_t rachel_settle_core = rachel_settle_amount * 20; // Settle amount * feed price\n         uint64_t rachel_fso_fee_core = rachel_settle_core * usd_fso_percent / GRAPHENE_100_PERCENT;\n         uint64_t rachel_fso_remainder_core = rachel_settle_core - rachel_fso_fee_core;\n         uint64_t rachel_fsf_fee_core = (rachel_fso_remainder_core) * 0 / GRAPHENE_100_PERCENT;\n         uint64_t expected_rachel_core = rachel_settle_core - rachel_fso_fee_core - rachel_fsf_fee_core;\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd - rachel_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(rachel, core), expected_rachel_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         // Rachel redeemed 2 bitUSD from the blockchain, and the blockchain closed this amount from Paul's debt to it\n         BOOST_CHECK_EQUAL(paul_initial_usd - rachel_settle_amount, call_paul_id(db).debt.value);\n         // The call order has the original amount of collateral less what was redeemed by Rachel\n         BOOST_CHECK_EQUAL(paul_initial_core - rachel_fso_remainder_core, call_paul_id(db).collateral.value);\n\n\n         ///////\n         // 6. Asset owner attempts to claim the collateral fees.\n         // Although no collateral-denominated fees should be present, the error should indicate the\n         // that claiming such fees are not yet active.\n         ///////\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == 0); // There should be no fees\n         trx.clear();\n         asset_claim_fees_operation claim_op;\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = core.amount(5 * core_unit);\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Collateral-denominated fees are not yet active\");\n\n         // Early proposals to claim should also fail\n         proposal_create_operation cop;\n         cop.review_period_seconds = 86400;\n         uint32_t buffer_seconds = 60 * 60;\n         cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.proposed_ops.emplace_back(claim_op);\n\n         trx.clear();\n         trx.operations.push_back(cop);\n         // sign(trx, smartissuer_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Collateral-denominated fees are not yet active\");\n\n\n         ///////\n         // 7. Activate HARDFORK_CORE_BSIP87_TIME\n         ///////\n         generate_blocks(HARDFORK_CORE_BSIP87_TIME);\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         // Update the price feed\n         publish_feed(bitusd, feedproducer_id(db), current_feed);\n         trx.clear();\n\n\n         ///////\n         // 8. Paul gives Michael 30 bitUSD and retains 50 bitUSD\n         ///////\n         int64_t michael_initial_usd = 30 * bitusd_unit;\n         transfer(paul.id, michael.id, asset(michael_initial_usd, bitusd.id));\n\n         // Check Michael's balance\n         BOOST_CHECK_EQUAL(get_balance(michael, bitusd), michael_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(michael, core), initial_balance_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd - michael_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 9. Michael force-settles 5 bitUSD which should be collected from Paul's debt position\n         ///////\n         const int64_t michael_settle_amount = 5 * bitusd_unit;\n         result = force_settle(michael, bitusd.amount(michael_settle_amount));\n\n         force_settlement_id_type michael_settle_id = result.get<object_id_type>();\n         BOOST_CHECK_EQUAL(michael_settle_id(db).balance.amount.value, michael_settle_amount);\n\n         // Advance time to complete the force settlement and to update the price feed\n         generate_blocks(db.head_block_time() + fc::hours(26));\n         set_expiration(db, trx);\n         trx.clear();\n         publish_feed(bitusd, feedproducer_id(db), current_feed);\n\n         // Michael's settlement should have completed and should no longer be present\n         BOOST_CHECK(!db.find(michael_settle_id));\n\n         // Check Michael's balance\n         // Michael redeemed some smart asset and should get the equivalent collateral amount (according to the feed price)\n         // minus the force_settlement_offset_fee - force_settlement_fee\n         // Michael redeemed 5 bitUSD and should get 10000 satoshi Core - 500 satoshi Core - 0 satoshi  Core\n         uint64_t michael_settle_core = michael_settle_amount * 20; // Settle amount * feed price\n         uint64_t michael_fso_fee_core = michael_settle_core * usd_fso_percent / GRAPHENE_100_PERCENT;\n         uint64_t michael_fso_remainder_core = michael_settle_core - michael_fso_fee_core;\n         uint64_t michael_fsf_fee_core = (michael_fso_remainder_core) * usd_fsf_percent_0 / GRAPHENE_100_PERCENT;\n         uint64_t expected_michael_core = michael_settle_core - michael_fso_fee_core - michael_fsf_fee_core;\n         BOOST_CHECK_EQUAL(get_balance(michael, bitusd), michael_initial_usd - michael_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(michael, core), initial_balance_core + expected_michael_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd - michael_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         // Michael redeemed 5 bitUSD from the blockchain, and the blockchain closed this amount from Paul's debt to it\n         BOOST_CHECK_EQUAL(paul_initial_usd - rachel_settle_amount - michael_settle_amount,\n                           call_paul_id(db).debt.value);\n         // The call order has the original amount of collateral less what was redeemed by Rachel, Michael\n         BOOST_CHECK_EQUAL(paul_initial_core - rachel_fso_remainder_core - michael_fso_remainder_core,\n                           call_paul_id(db).collateral.value);\n\n         // The asset's force settlement fee % should still not be set\n         BOOST_CHECK(!bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         // There should be no accumulated asset fees\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == 0);\n\n\n         ///////\n         // 10. Asset owner sets the force-fee percentage to 3%\n         ///////\n         const uint16_t usd_fsf_percent_3 = 3 * GRAPHENE_1_PERCENT; // 3% Force-settlement fee % (BSIP87)\n         asset_update_bitasset_operation uop;\n         uop.issuer = assetowner.id;\n         uop.asset_to_update = bitusd.get_id();\n         uop.new_options = bitusd.bitasset_data(db).options;\n         uop.new_options.extensions.value.force_settle_fee_percent = usd_fsf_percent_3;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n\n         // The force settlement fee % should be set\n         BOOST_CHECK(bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         BOOST_CHECK_EQUAL( usd_fsf_percent_3,\n                            *bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent\n                          );\n\n         ///////\n         // 11. Paul gives Yanna 40 bitUSD and retains 10 bitUSD\n         ///////\n         int64_t yanna_initial_usd = 40 * bitusd_unit;\n         transfer(paul.id, yanna.id, asset(yanna_initial_usd, bitusd.id));\n\n         // Check Yanna's balance\n         BOOST_CHECK_EQUAL(get_balance(yanna, bitusd), yanna_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(yanna, core), 0);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd),\n                           paul_initial_usd - rachel_initial_usd - michael_initial_usd - yanna_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 12. Yanna force-settles 10 bitUSD which should be collected from Paul's debt position\n         ///////\n         const int64_t yanna_settle_amount = 10 * bitusd_unit;\n         result = force_settle(yanna, bitusd.amount(yanna_settle_amount));\n\n         force_settlement_id_type yanna_settle_id = result.get<object_id_type>();\n         BOOST_CHECK_EQUAL(yanna_settle_id(db).balance.amount.value, yanna_settle_amount);\n\n         // Advance time to complete the force settlement and to update the price feed\n         generate_blocks(db.head_block_time() + fc::hours(26));\n         set_expiration(db, trx);\n         trx.clear();\n         publish_feed(bitusd, feedproducer_id(db), current_feed);\n\n         // Yanna's settlement should have completed and should no longer be present\n         BOOST_CHECK(!db.find(yanna_settle_id));\n\n         // Check Yanna's balance\n         // Yanna redeemed some smart asset and should get the equivalent collateral amount (according to the feed price)\n         // minus the force_settlement_offset_fee - force_settlement_fee\n         // Yanna redeemed 10 bitUSD and should get 20000 satoshi Core - 1000 satoshi Core - 570 satoshi  Core; (20000 - 1000) * 3% = 570\n         uint64_t yanna_settle_core = yanna_settle_amount * 20; // Settle amount * feed price\n         uint64_t yanna_fso_fee_core = yanna_settle_core * usd_fso_percent / GRAPHENE_100_PERCENT;\n         uint64_t yanna_fso_remainder_core = yanna_settle_core - yanna_fso_fee_core;\n         uint64_t yanna_fsf_fee_core = (yanna_fso_remainder_core) * usd_fsf_percent_3 / GRAPHENE_100_PERCENT;\n         uint64_t expected_yanna_core = yanna_settle_core - yanna_fso_fee_core - yanna_fsf_fee_core;\n         BOOST_CHECK_EQUAL(get_balance(yanna, bitusd), yanna_initial_usd - yanna_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(yanna, core), 0 + expected_yanna_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd),\n                           paul_initial_usd - rachel_initial_usd - michael_initial_usd - yanna_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         // Yanna redeemed 10 bitUSD from the blockchain, and the blockchain closed this amount from Paul's debt to it\n         BOOST_CHECK_EQUAL(paul_initial_usd - rachel_settle_amount - michael_settle_amount - yanna_settle_amount,\n                           call_paul_id(db).debt.value);\n         // The call order has the original amount of collateral less what was redeemed by Rachel, Michael, Yanna\n         BOOST_CHECK_EQUAL(paul_initial_core - rachel_fso_remainder_core - michael_fso_remainder_core - yanna_fso_remainder_core,\n                           call_paul_id(db).collateral.value);\n\n         // The asset's force settlement fee % should be valid\n         BOOST_CHECK(bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         // There should be some accumulated collateral-deonominated fees\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == yanna_fsf_fee_core);\n\n\n         ///////\n         // 13. Asset owner updates the force-settlement fee to 4%\n         ///////\n         const uint16_t usd_fsf_percent_4 = 4 * GRAPHENE_1_PERCENT; // 4% Force-settlement fee % (BSIP87)\n         uop = asset_update_bitasset_operation();\n         uop.issuer = assetowner.id;\n         uop.asset_to_update = bitusd.get_id();\n         uop.new_options = bitusd.bitasset_data(db).options;\n         uop.new_options.extensions.value.force_settle_fee_percent = usd_fsf_percent_4;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n\n         // The force settlement fee % should be set\n         BOOST_CHECK(bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         BOOST_CHECK_EQUAL( usd_fsf_percent_4,\n                            *bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent\n                          );\n\n         ///////\n         // 14. Paul gives Vikram 10 bitUSD and retains 0 bitUSD\n         ///////\n         int64_t vikram_initial_usd = 10 * bitusd_unit;\n         transfer(paul.id, vikram.id, asset(vikram_initial_usd, bitusd.id));\n\n         // Check Yanna's balance\n         BOOST_CHECK_EQUAL(get_balance(vikram, bitusd), vikram_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(vikram, core), 0);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd),\n                           paul_initial_usd - rachel_initial_usd - michael_initial_usd - yanna_initial_usd -\n                           vikram_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 15. Vikram force-settles 10 bitUSD which should be collected from Paul's debt position\n         ///////\n         const int64_t vikram_settle_amount = 10 * bitusd_unit;\n         result = force_settle(vikram, bitusd.amount(vikram_settle_amount));\n\n         force_settlement_id_type vikram_settle_id = result.get<object_id_type>();\n         BOOST_CHECK_EQUAL(vikram_settle_id(db).balance.amount.value, vikram_settle_amount);\n\n         // Advance time to complete the force settlement and to update the price feed\n         generate_blocks(db.head_block_time() + fc::hours(26));\n         set_expiration(db, trx);\n         trx.clear();\n         publish_feed(bitusd, feedproducer_id(db), current_feed);\n\n         // Vikrams's settlement should have completed and should no longer be present\n         BOOST_CHECK(!db.find(vikram_settle_id));\n\n         // Check Vikrams's balance\n         // Vikram redeemed some smart asset and should get the equivalent collateral amount (according to the feed price)\n         // minus the force_settlement_offset_fee - force_settlement_fee\n         // Vikram redeemed 10 bitUSD and should get 20000 satoshi Core - 1000 satoshi Core - 760 satoshi  Core; (20000 - 1000) * 4% = 760\n         uint64_t vikram_settle_core = vikram_settle_amount * 20; // Settle amount * feed price\n         uint64_t vikram_fso_fee_core = vikram_settle_core * usd_fso_percent / GRAPHENE_100_PERCENT;\n         uint64_t vikram_fso_remainder_core = vikram_settle_core - vikram_fso_fee_core;\n         uint64_t vikram_fsf_fee_core = (vikram_fso_remainder_core) * usd_fsf_percent_4 / GRAPHENE_100_PERCENT;\n         uint64_t expected_vikram_core = vikram_settle_core - vikram_fso_fee_core - vikram_fsf_fee_core;\n         BOOST_CHECK_EQUAL(get_balance(vikram, bitusd), vikram_initial_usd - vikram_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(vikram, core), 0 + expected_vikram_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd),\n                           paul_initial_usd - rachel_initial_usd - michael_initial_usd - yanna_initial_usd -\n                           vikram_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         // Vikram redeemed 10 bitUSD from the blockchain, and the blockchain closed this amount from Paul's debt to it\n         BOOST_CHECK_EQUAL(paul_initial_usd - rachel_settle_amount - michael_settle_amount - yanna_settle_amount -\n                           vikram_settle_amount,\n                           call_paul_id(db).debt.value);\n         // The call order has the original amount of collateral less what was redeemed by Rachel, Michael, Yanna, Vikram\n         BOOST_CHECK_EQUAL(\n                 paul_initial_core - rachel_fso_remainder_core - michael_fso_remainder_core - yanna_fso_remainder_core -\n                 vikram_fso_remainder_core,\n                 call_paul_id(db).collateral.value);\n\n         // The asset's force settlement fee % should still not be set\n         BOOST_CHECK(bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         // There should be some accumulated collateral-deonominated fees\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         const uint64_t expected_accumulation_fsf_core_amount = yanna_fsf_fee_core + vikram_fsf_fee_core;\n         BOOST_CHECK(\n                 bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == expected_accumulation_fsf_core_amount);\n\n\n         ///////\n         // 16. Asset owner attempts and fails to change the backing of the smart asset\n         // because of its outstanding supply\n         ///////\n         // Create a new user-issued asset\n         trx.clear();\n         ACTOR(jill);\n         trx.clear();\n         price core_exchange_rate(asset(1, asset_id_type(1)), asset(1));\n         uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, core_exchange_rate, 2, market_fee_percent);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n         const asset_object& jillcoin = get_asset(\"JCOIN\");\n\n\n         // Attempt to change the backing of the smart asset to the new user-issued asset\n         trx.clear();\n         asset_update_bitasset_operation change_backing_asset_op;\n         change_backing_asset_op.asset_to_update = bitusd.id;\n         change_backing_asset_op.issuer = assetowner.id;\n         change_backing_asset_op.new_options.short_backing_asset = jillcoin.id;\n         trx.operations.push_back(change_backing_asset_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"there is already a current supply\");\n\n\n         ///////\n         // 17. All current holdings of bitUSD are removed\n         ///////\n         // Rachel, Michael, and Yanna return their remaining bitUSD to Paul\n         trx.clear();\n         transfer(rachel.id, paul.id, bitusd.amount(get_balance(rachel, bitusd)));\n         transfer(michael.id, paul.id, bitusd.amount(get_balance(michael, bitusd)));\n         transfer(yanna.id, paul.id, bitusd.amount(get_balance(yanna, bitusd)));\n\n         // Vikram has no bitUSD to transfer\n         BOOST_CHECK_EQUAL(get_balance(vikram, bitusd), 0);\n\n         // Paul closes his debt to the blockchain\n         cover(paul, bitusd.amount(call_paul_id(db).debt), core.amount(call_paul_id(db).collateral.value));\n\n         // Check the bitUSD holdings of the actors\n         BOOST_CHECK_EQUAL(get_balance(assetowner, bitusd), 0);\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), 0);\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), 0);\n         BOOST_CHECK_EQUAL(get_balance(michael, bitusd), 0);\n         BOOST_CHECK_EQUAL(get_balance(yanna, bitusd), 0);\n         BOOST_CHECK_EQUAL(get_balance(vikram, bitusd), 0);\n\n\n         ///////\n         // 18. Asset owner attempts and fails to change the backing of the smart asset\n         // because of unclaimed collateral fees\n         ///////\n         // Repeat check of the accumulated fees\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(\n                 bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == expected_accumulation_fsf_core_amount);\n\n         trx.clear();\n         trx.operations.push_back(change_backing_asset_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Must claim collateral-denominated fees\");\n\n\n         ///////\n         // 19. Asset owner claims all of the unclaimed collateral fees\n         ///////\n         trx.clear();\n         claim_op = asset_claim_fees_operation();\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = core.amount(expected_accumulation_fsf_core_amount);\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == 0);\n\n\n         ///////\n         // 20. Asset owner attempts and succeeds in changing the backing of the smart asset\n         ///////\n         // Confirm that the asset is backed by CORE\n         const asset_bitasset_data_object& bitusd_bitasset_data = (*bitusd.bitasset_data_id)(db);\n         BOOST_CHECK(bitusd_bitasset_data.options.short_backing_asset == core.id);\n\n         trx.clear();\n         trx.operations.push_back(change_backing_asset_op);\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n\n         // Confirm the change to the backing asset\n         BOOST_CHECK(bitusd_bitasset_data.options.short_backing_asset == jillcoin.id);\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Attempt to claim invalid fees\n    */\n   BOOST_AUTO_TEST_CASE(force_settle_fee_invalid_claims_test) {\n      try {\n         INVOKE(force_settle_fee_1_test);\n\n         GET_ACTOR(assetowner);\n\n         // Check the asset owner's accumulated asset fees\n         const auto &core = asset_id_type()(db);\n         const int64_t core_unit = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n         const asset_object& bitusd = get_asset(\"USDBIT\");\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees > 0);\n         share_type rachel_fsf_fee_core = bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees;\n\n         // Attempt to claim negative fees\n         trx.clear();\n         asset_claim_fees_operation claim_op;\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = core.amount(-5 * core_unit);\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"amount_to_claim.amount > 0\");\n\n         // Attempt to claim 0 fees\n         trx.clear();\n         claim_op = asset_claim_fees_operation();\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = core.amount(0 * core_unit);\n         trx.operations.push_back(claim_op);\n         set_expiration(db, trx);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"amount_to_claim.amount > 0\");\n\n         // Attempt to claim excessive claim fee\n         trx.clear();\n         claim_op = asset_claim_fees_operation();\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = rachel_fsf_fee_core + 1;\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Attempt to claim more backing-asset fees\");\n\n         // Attempt to claim with an invalid asset asset type\n         trx.clear();\n         ACTOR(jill);\n         price price(asset(1, asset_id_type(1)), asset(1));\n         uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2, market_fee_percent);\n         generate_block(); trx.clear(); set_expiration(db, trx);\n         const asset_object& jillcoin = get_asset(\"JCOIN\");\n\n         trx.clear();\n         claim_op = asset_claim_fees_operation();\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = jillcoin.amount(rachel_fsf_fee_core.value);\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"is not backed by asset\");\n\n         // Attempt to claim part of all that can be claimed\n         share_type partial_claim_core = 1; // 1 satoshi\n         share_type expected_remainder_core = rachel_fsf_fee_core - partial_claim_core;\n         FC_ASSERT(expected_remainder_core.value > 0); // Remainder should be positive\n         trx.clear();\n         claim_op = asset_claim_fees_operation();\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = partial_claim_core;\n         trx.operations.push_back(claim_op);\n         set_expiration(db, trx);\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == expected_remainder_core);\n\n         // Attempt to claim all that can be claimed\n         generate_block();\n         trx.clear();\n         claim_op = asset_claim_fees_operation();\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = expected_remainder_core;\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == 0);\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test 100% force settlement fee.\n    *\n    * There are two primary actors: paul, rachel\n    *\n    * 1. Asset owner creates the smart coin called bitUSD\n    * 2. The feed price is 1 satoshi bitUSD for 20 satoshi Core = 0.01 bitUSD for 0.00020 Core = 50 bitUSD for 1 Core\n    * 3. Paul borrows 100 bitUSD (10000 satoshis of bitUSD) from the blockchain with a low amount of collateral\n    * 4. Paul gives Rachel 20 bitUSD\n    * 5. Rachel force-settles 2 bitUSD which should be collected from Paul's debt position\n    * because of its relatively lower collateral ratio\n    *\n    * The force-settlement by Rachel should account for both the force-settlement offset fee,\n    * and the new force settlement fee from BSIP87.\n    */\n   BOOST_AUTO_TEST_CASE(force_settle_fee_extreme_1_test) {\n      try {\n         ///////\n         // Initialize the scenario\n         ///////\n         // Advance to the when the force-settlement fee activates\n         generate_blocks(HARDFORK_CORE_BSIP87_TIME);\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         // Create actors\n         ACTORS((assetowner)(feedproducer)(paul)(rachel));\n\n         // Fund actors\n         uint64_t initial_balance_core = 10000000;\n         transfer(committee_account, assetowner.id, asset(initial_balance_core));\n         transfer(committee_account, feedproducer.id, asset(initial_balance_core));\n         transfer(committee_account, paul.id, asset(initial_balance_core));\n\n         // 1. Create assets\n         const uint16_t usd_fso_percent = 5 * GRAPHENE_1_PERCENT; // 5% Force-settlement offset fee %\n         const uint16_t usd_fsf_percent = 100 * GRAPHENE_1_PERCENT; // 100% Force-settlement fee % (BSIP87)\n         create_smart_asset(\"USDBIT\", assetowner.id, usd_fso_percent, usd_fsf_percent);\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bitusd = get_asset(\"USDBIT\");\n         const int64_t bitusd_unit = asset::scaled_precision(bitusd.precision).value; // 100 satoshi USDBIT in 1 USDBIT\n         const auto &core = asset_id_type()(db);\n\n\n         ///////\n         // 2. Publish a feed for the smart asset\n         ///////\n         update_feed_producers(bitusd.id, {feedproducer_id});\n         price_feed current_feed;\n         current_feed.maintenance_collateral_ratio = 1750;\n         current_feed.maximum_short_squeeze_ratio = 1100;\n         // Requirement of 20x collateral in satoshis: 1 satoshi bitUSD for 20 satoshi Core\n         // -> 0.01 bitUSD for 0.00020 Core = 100 bitUSD for 2 Core = 50 bitUSD for 1 Core\n         current_feed.settlement_price = bitusd.amount(1) / core.amount(20);\n         publish_feed(bitusd, feedproducer, current_feed);\n\n\n         ///////\n         // 3. Paul borrows 100 bitUSD\n         ///////\n         // Paul will borrow bitUSD by providing 2x collateral required: 2 * 20 = 40\n         int64_t paul_initial_usd = 100 * bitusd_unit; // 10000\n         int64_t paul_initial_core = paul_initial_usd * 2 * 20; // 400000\n         const call_order_object &call_paul = *borrow(paul, bitusd.amount(paul_initial_usd),\n                                                      core.amount(paul_initial_core));\n         call_order_id_type call_paul_id = call_paul.id;\n         BOOST_REQUIRE_EQUAL(get_balance(paul, bitusd), paul_initial_usd);\n\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 4. Paul gives Rachel 20 bitUSD and retains 80 bitUSD\n         ///////\n         int64_t rachel_initial_usd = 20 * bitusd_unit;\n         transfer(paul.id, rachel.id, asset(rachel_initial_usd, bitusd.id));\n\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n\n         ///////\n         // 5. Rachel force-settles 2 bitUSD which should be collected from Paul's debt position\n         ///////\n         const int64_t rachel_settle_amount = 2 * bitusd_unit; // 200 satoshi bitusd\n         operation_result result = force_settle(rachel, bitusd.amount(rachel_settle_amount));\n\n         force_settlement_id_type rachel_settle_id = result.get<object_id_type>();\n         BOOST_CHECK_EQUAL(rachel_settle_id(db).balance.amount.value, rachel_settle_amount);\n\n         // Advance time to complete the force settlement and to update the price feed\n         generate_blocks(db.head_block_time() + fc::hours(26));\n         set_expiration(db, trx);\n         trx.clear();\n         publish_feed(bitusd, feedproducer_id(db), current_feed);\n         trx.clear();\n\n         // Rachel's settlement should have completed and should no longer be present\n         BOOST_CHECK(!db.find(rachel_settle_id));\n\n         // Check Rachel's balance\n         // Rachel redeemed some smart asset and should get the equivalent collateral amount (according to the feed price)\n         // minus the force_settlement_offset_fee - force_settlement_fee\n         // uint64_t rachel_settle_core = 4000; // rachel_settle_amount * 20\n         // uint64_t rachel_fso_fee_core = 200; // rachel_settle_core * usd_fso_percent / GRAPHENE_100_PERCENT\n         uint64_t rachel_fso_remainder_core = 3800; // rachel_settle_core - rachel_fso_fee_core\n         uint64_t rachel_fsf_fee_core = 3800; // (rachel_fso_remainder_core) * usd_fsf_percent / GRAPHENE_100_PERCENT\n         // Rachel redeemed 2 bitUSD and should get 4000 satoshi Core - 200 satoshi Core - 3800 satoshi  Core\n         uint64_t expected_rachel_core = 0; // rachel_settle_core - rachel_fso_fee_core - rachel_fsf_fee_core\n         BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), rachel_initial_usd - rachel_settle_amount);\n         BOOST_CHECK_EQUAL(get_balance(rachel, core), expected_rachel_core);\n\n         // Check Paul's balance\n         BOOST_CHECK_EQUAL(get_balance(paul, bitusd), paul_initial_usd - rachel_initial_usd);\n         BOOST_CHECK_EQUAL(get_balance(paul, core), initial_balance_core - paul_initial_core);\n\n         // Check Paul's debt to the blockchain\n         // Rachel redeemed 2 bitUSD from the blockchain, and the blockchain closed this amount from Paul's debt to it\n         BOOST_CHECK_EQUAL(paul_initial_usd - rachel_settle_amount, call_paul_id(db).debt.value);\n         // The call order has the original amount of collateral less what was redeemed by Rachel\n         BOOST_CHECK_EQUAL(paul_initial_core - rachel_fso_remainder_core, call_paul_id(db).collateral.value);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == rachel_fsf_fee_core);\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test the ability to create and update assets with force-settlement fee % before HARDFORK_CORE_BSIP87_TIME\n    *\n    *\n    * Before HARDFORK_CORE_BSIP87_TIME\n    *\n    * 1. Asset owner fails to create the smart coin called USDBIT with a force-settlement fee %\n    * 2. Asset owner fails to create the smart coin called USDBIT with a force-settlement fee % in a proposal\n    * 3. Asset owner succeeds to create the smart coin called USDBIT without a force-settlement fee %\n    *\n    * 4. Asset owner fails to update the smart coin with a force-settlement fee %\n    * 5. Asset owner fails to update the smart coin with a force-settlement fee % in a proposal\n    *\n    * 6. Asset owner fails to claim collateral-denominated fees\n    * 7. Asset owner fails to claim collateral-denominated fees in a proposal\n    *\n    *\n    * 8. Activate HARDFORK_CORE_BSIP87_TIME\n    *\n    *\n    * After HARDFORK_CORE_BSIP87_TIME\n    *\n    * 9. Asset owner succeeds to create the smart coin called CNYBIT with a force-settlement fee %\n    * 10. Asset owner succeeds to create the smart coin called RUBBIT with a force-settlement fee % in a proposal\n    *\n    * 11. Asset owner succeeds to update the smart coin called CNYBIT with a force-settlement fee %\n    * 12. Asset owner succeeds to update the smart coin called RUBBIT with a force-settlement fee % in a proposal\n    */\n   BOOST_AUTO_TEST_CASE(prevention_before_hardfork_test) {\n      try {\n         ///////\n         // Initialize the scenario\n         ///////\n         // Get around Graphene issue #615 feed expiration bug\n         generate_blocks(HARDFORK_615_TIME);\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Create actors\n         ACTORS((assetowner));\n\n         // Fund actors\n         uint64_t initial_balance_core = 10000000;\n         transfer(committee_account, assetowner.id, asset(initial_balance_core));\n\n         // Confirm before hardfork activation\n         BOOST_CHECK(db.head_block_time() < HARDFORK_CORE_BSIP87_TIME);\n\n\n         ///////\n         // 1. Asset owner fails to create the smart coin called bitUSD with a force-settlement fee %\n         ///////\n         const uint16_t usd_fso_percent = 5 * GRAPHENE_1_PERCENT; // 5% Force-settlement offset fee %\n         const uint16_t usd_fsf_percent_0 = 0 * GRAPHENE_1_PERCENT; // 0% Force-settlement fee %\n\n         // Attempt to create the smart asset with a force-settlement fee %\n         // The attempt should fail because it is before HARDFORK_CORE_BSIP87_TIME\n         trx.clear();\n         REQUIRE_EXCEPTION_WITH_TEXT(create_smart_asset(\"USDBIT\", assetowner.id, usd_fso_percent, usd_fsf_percent_0),\n                                     \"cannot be set before Hardfork BSIP87\");\n\n\n         ///////\n         // 2. Asset owner fails to create the smart coin called bitUSD with a force-settlement fee % in a proposal\n         ///////\n         {\n            asset_create_operation create_op = create_smart_asset_op(\"USDBIT\", assetowner.id, usd_fso_percent,\n                                                                     usd_fsf_percent_0);\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(create_op);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP87\");\n         }\n\n\n         ///////\n         // 3. Asset owner succeeds to create the smart coin called bitUSD without a force-settlement fee %\n         ///////\n         trx.clear();\n         create_smart_asset(\"USDBIT\", assetowner.id, usd_fso_percent);\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bitusd = get_asset(\"USDBIT\");\n         const auto &core = asset_id_type()(db);\n\n\n         ///////\n         // 4. Asset owner fails to update the smart coin with a force-settlement fee %\n         ///////\n         const uint16_t usd_fsf_percent_3 = 3 * GRAPHENE_1_PERCENT; // 3% Force-settlement fee % (BSIP87)\n         asset_update_bitasset_operation uop;\n         uop.issuer = assetowner.id;\n         uop.asset_to_update = bitusd.get_id();\n         uop.new_options = bitusd.bitasset_data(db).options;\n         uop.new_options.extensions.value.force_settle_fee_percent = usd_fsf_percent_3;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP87\");\n\n         // The force settlement fee % should not be set\n         BOOST_CHECK(!bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n\n\n         ///////\n         // 5. Asset owner fails to update the smart coin with a force-settlement fee % in a proposal\n         ///////\n         {\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP87\");\n\n            // The force settlement fee % should not be set\n            BOOST_CHECK(!bitusd.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         }\n\n\n         ///////\n         // 6. Asset owner fails to claim collateral-denominated fees\n         ///////\n         // Although no collateral-denominated fees should be present, the error should indicate the\n         // that claiming such fees are not yet active.\n         BOOST_CHECK(bitusd.dynamic_asset_data_id(db).accumulated_collateral_fees == 0); // There should be no fees\n         trx.clear();\n         asset_claim_fees_operation claim_op;\n         claim_op.issuer = assetowner.id;\n         claim_op.extensions.value.claim_from_asset_id = bitusd.id;\n         claim_op.amount_to_claim = core.amount(5);\n         trx.operations.push_back(claim_op);\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Collateral-denominated fees are not yet active\");\n\n\n         ///////\n         // 7. Asset owner fails to claim collateral-denominated fees in a proposal\n         ///////\n         proposal_create_operation cop;\n         cop.review_period_seconds = 86400;\n         uint32_t buffer_seconds = 60 * 60;\n         cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.proposed_ops.emplace_back(claim_op);\n\n         trx.clear();\n         trx.operations.push_back(cop);\n         // sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"Collateral-denominated fees are not yet active\");\n\n\n\n         ///////\n         // 8. Activate HARDFORK_CORE_BSIP87_TIME\n         ///////\n         BOOST_CHECK(db.head_block_time() < HARDFORK_CORE_BSIP87_TIME); // Confirm still before hardfork activation\n         generate_blocks(HARDFORK_CORE_BSIP87_TIME);\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n\n         ///////\n         // 9. Asset owner succeeds to create the smart coin called CNYBIT with a force-settlement fee %\n         ///////\n         const uint16_t fsf_percent_1 = 1 * GRAPHENE_1_PERCENT; // 1% Force-settlement fee % (BSIP87)\n         const uint16_t fsf_percent_5 = 1 * GRAPHENE_1_PERCENT; // 5% Force-settlement fee % (BSIP87)\n         trx.clear();\n         create_smart_asset(\"CNYBIT\", assetowner.id, usd_fso_percent, fsf_percent_1);\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bitcny = get_asset(\"CNYBIT\");\n\n         // The force settlement fee % should be set\n         BOOST_CHECK(bitcny.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         BOOST_CHECK_EQUAL(fsf_percent_1, *bitcny.bitasset_data(db).options.extensions.value.force_settle_fee_percent);\n\n\n         ///////\n         // 10. Asset owner succeeds to create the smart coin called RUBBIT with a force-settlement fee % in a proposal\n         ///////\n         {\n            // Create the proposal\n            asset_create_operation create_op = create_smart_asset_op(\"RUBBIT\", assetowner.id, usd_fso_percent,\n                                                                     fsf_percent_1);\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(create_op);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            processed_transaction processed = PUSH_TX(db, trx);\n\n\n            // Approve the proposal\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = assetowner_id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(assetowner_id);\n            trx.clear();\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, assetowner_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n\n            // Advance to the activation of the proposal\n            generate_blocks(cop.expiration_time);\n            set_expiration(db, trx);\n         }\n         const auto &bitrub = get_asset(\"RUBBIT\");\n\n         // The force settlement fee % should be set\n         BOOST_CHECK(bitrub.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         BOOST_CHECK_EQUAL(fsf_percent_1, *bitrub.bitasset_data(db).options.extensions.value.force_settle_fee_percent);\n\n\n         ///////\n         // 11. Asset owner succeeds to update the smart coin called CNYBIT with a force-settlement fee %\n         ///////\n         uop = asset_update_bitasset_operation();\n         uop.issuer = assetowner.id;\n         uop.asset_to_update = bitcny.get_id();\n         uop.new_options = bitcny.bitasset_data(db).options;\n         uop.new_options.extensions.value.force_settle_fee_percent = fsf_percent_5;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n\n         // The force settlement fee % should be set\n         BOOST_CHECK(bitcny.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         BOOST_CHECK_EQUAL(fsf_percent_5, *bitcny.bitasset_data(db).options.extensions.value.force_settle_fee_percent);\n\n\n         ///////\n         // 12. Asset owner succeeds to update the smart coin called RUBBIT with a force-settlement fee % in a proposal\n         ///////\n         {\n            // Create the proposal\n            uop = asset_update_bitasset_operation();\n            uop.issuer = assetowner.id;\n            uop.asset_to_update = bitrub.get_id();\n            uop.new_options = bitrub.bitasset_data(db).options;\n            uop.new_options.extensions.value.force_settle_fee_percent = fsf_percent_5;\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            processed_transaction processed = PUSH_TX(db, trx);\n\n\n            // Approve the proposal\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = assetowner_id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(assetowner_id);\n            trx.clear();\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, assetowner_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n            // Advance to the activation of the proposal\n            generate_blocks(cop.expiration_time);\n            set_expiration(db, trx);\n         }\n\n         // The force settlement fee % should be set\n         BOOST_CHECK(bitrub.bitasset_data(db).options.extensions.value.force_settle_fee_percent.valid());\n         BOOST_CHECK_EQUAL(fsf_percent_5, *bitrub.bitasset_data(db).options.extensions.value.force_settle_fee_percent);\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/grouped_orders_api_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 manikey123, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/app/api.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::app;\n\nBOOST_FIXTURE_TEST_SUITE(grouped_orders_api_tests, database_fixture)\nBOOST_AUTO_TEST_CASE(api_limit_get_grouped_limit_orders) {\n   try\n   {\n   app.enable_plugin(\"grouped_orders\");\n   graphene::app::orders_api orders_api(app);\n   optional< api_access_info > acc;\n   optional<price> start;\n\n\t//account_id_type() do 3 ops\n   create_bitasset(\"USD\", account_id_type());\n   create_account(\"dan\");\n   create_account(\"bob\");\n   asset_id_type bit_jmj_id = create_bitasset(\"JMJBIT\").id;\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n   GRAPHENE_CHECK_THROW(orders_api.get_grouped_limit_orders(std::string( static_cast<object_id_type>(asset_id_type())), std::string( static_cast<object_id_type>(asset_id_type())),10, start,260), fc::exception);\n   vector< limit_order_group > orders =orders_api.get_grouped_limit_orders(std::string( static_cast<object_id_type>(asset_id_type())), std::string( static_cast<object_id_type>(bit_jmj_id)), 10,start,240);\n   BOOST_REQUIRE_EQUAL( orders.size(), 0u);\n   }catch (fc::exception &e)\n   {\n    edump((e.to_detail_string()));\n    throw;\n   }\n}\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/history_api_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/api.hpp>\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::app;\nBOOST_FIXTURE_TEST_SUITE( history_api_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE(get_account_history) {\n   try {\n      graphene::app::history_api hist_api(app);\n\n      //account_id_type() do 3 ops\n      create_bitasset(\"USD\", account_id_type());\n      create_account(\"dan\");\n      create_account(\"bob\");\n\n      generate_block();\n      fc::usleep(fc::milliseconds(2000));\n\n      int asset_create_op_id = operation::tag<asset_create_operation>::value;\n      int account_create_op_id = operation::tag<account_create_operation>::value;\n\n      //account_id_type() did 3 ops and includes id0\n      vector<operation_history_object> histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 100, operation_history_id_type());\n\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n      BOOST_CHECK_EQUAL(histories[2].op.which(), asset_create_op_id);\n\n      // 1 account_create op larger than id1\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 100, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK(histories[0].id.instance() != 0);\n      BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n      // Limit 2 returns 2 result\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 2, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK(histories[1].id.instance() != 0);\n      BOOST_CHECK_EQUAL(histories[1].op.which(), account_create_op_id);\n      // bob has 1 op\n      histories = hist_api.get_account_history(\"bob\", operation_history_id_type(), 100, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(get_account_history_additional) {\n   try {\n      graphene::app::history_api hist_api(app);\n\n      // A = account_id_type() with records { 5, 3, 1, 0 }, and\n      // B = dan with records { 6, 4, 2, 1 }\n      // account_id_type() and dan share operation id 1(account create) - share can be also in id 0\n\n      // no history at all in the chain\n      vector<operation_history_object> histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(0), 4, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      create_bitasset(\"USD\", account_id_type()); // create op 0\n      generate_block();\n      // what if the account only has one history entry and it is 0?\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);\n\n      const account_object& dan = create_account(\"dan\"); // create op 1\n\n      create_bitasset(\"CNY\", dan.id); // create op 2\n      create_bitasset(\"BTC\", account_id_type()); // create op 3\n      create_bitasset(\"XMR\", dan.id); // create op 4\n      create_bitasset(\"EUR\", account_id_type()); // create op 5\n      create_bitasset(\"OIL\", dan.id); // create op 6\n\n      generate_block();\n\n      // f(A, 0, 4, 9) = { 5, 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(9));\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n      // f(A, 0, 4, 6) = { 5, 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(6));\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n      // f(A, 0, 4, 5) = { 5, 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(5));\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n      // f(A, 0, 4, 4) = { 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(4));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n      // f(A, 0, 4, 3) = { 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(3));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n      // f(A, 0, 4, 2) = { 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n      // f(A, 0, 4, 1) = { 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type(1));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n      // f(A, 0, 4, 0) = { 5, 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 4, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n      // f(A, 1, 5, 9) = { 5, 3 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(9));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n      // f(A, 1, 5, 6) = { 5, 3 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(6));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n      // f(A, 1, 5, 5) = { 5, 3 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(5));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n      // f(A, 1, 5, 4) = { 3 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(4));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n\n      // f(A, 1, 5, 3) = { 3 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(3));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n\n      // f(A, 1, 5, 2) = { }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // f(A, 1, 5, 1) = { }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(1));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // f(A, 1, 5, 0) = { 5, 3 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 5, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n      // f(A, 0, 3, 9) = { 5, 3, 1 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(9));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n      // f(A, 0, 3, 6) = { 5, 3, 1 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(6));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n      // f(A, 0, 3, 5) = { 5, 3, 1 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(5));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n      // f(A, 0, 3, 4) = { 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(4));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n      // f(A, 0, 3, 3) = { 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(3));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n\n      // f(A, 0, 3, 2) = { 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n      // f(A, 0, 3, 1) = { 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type(1));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n      // f(A, 0, 3, 0) = { 5, 3, 1 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 3, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n      // f(B, 0, 4, 9) = { 6, 4, 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(9));\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n\n      // f(B, 0, 4, 6) = { 6, 4, 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(6));\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n\n      // f(B, 0, 4, 5) = { 4, 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(5));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n      // f(B, 0, 4, 4) = { 4, 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(4));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n\n      // f(B, 0, 4, 3) = { 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(3));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n\n      // f(B, 0, 4, 2) = { 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);\n\n      // f(B, 0, 4, 1) = { 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type(1));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);\n\n      // f(B, 0, 4, 0) = { 6, 4, 2, 1 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(), 4, operation_history_id_type());\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n\n      // f(B, 2, 4, 9) = { 6, 4 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(9));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n\n      // f(B, 2, 4, 6) = { 6, 4 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(6));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n\n      // f(B, 2, 4, 5) = { 4 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(5));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n\n      // f(B, 2, 4, 4) = { 4 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(4));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n\n      // f(B, 2, 4, 3) = { }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(3));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // f(B, 2, 4, 2) = { }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // f(B, 2, 4, 1) = { }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(1));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // f(B, 2, 4, 0) = { 6, 4 }\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(2), 4, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n\n      // 0 limits\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(0), 0, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(3), 0, operation_history_id_type(9));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // non existent account\n      histories = hist_api.get_account_history(\"1.2.18\", operation_history_id_type(0), 4, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // create a new account C = alice { 7 }\n      create_account(\"alice\");\n\n      generate_block();\n\n      // f(C, 0, 4, 10) = { 7 }\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(0), 4, operation_history_id_type(10));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u);\n\n      // f(C, 8, 4, 10) = { }\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(8), 4, operation_history_id_type(10));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 }\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 5u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[4].id.instance(), 0u);\n\n   }\n   catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(track_account) {\n   try {\n      graphene::app::history_api hist_api(app);\n\n      // account_id_type() is not tracked\n\n      // account_id_type() creates alice(not tracked account)\n      create_account(\"alice\");\n\n      //account_id_type() creates some ops\n      create_bitasset(\"CNY\", account_id_type());\n      create_bitasset(\"USD\", account_id_type());\n\n      // account_id_type() creates dan(account tracked)\n      const account_object& dan = create_account(\"dan\");\n      auto dan_id = dan.id;\n\n      // dan makes 1 op\n      create_bitasset(\"EUR\", dan_id);\n\n      generate_block();\n\n      // anything against account_id_type() should be {}\n      vector<operation_history_object> histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 1, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // anything against alice should be {}\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(1), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(1), 1, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // dan should have history\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);\n\n      // create more ops, starting with an untracked account\n      create_bitasset( \"BTC\", account_id_type() );\n      create_bitasset( \"GBP\", dan_id );\n\n      generate_block();\n\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);\n\n      db.pop_block();\n\n      // Try again, should result in same object IDs\n      create_bitasset( \"BTC\", account_id_type() );\n      create_bitasset( \"GBP\", dan_id );\n\n      generate_block();\n\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 3u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(track_account2) {\n   try {\n      graphene::app::history_api hist_api(app);\n\n      // account_id_type() is tracked\n\n      // account_id_type() creates alice(tracked account)\n      const account_object& alice = create_account(\"alice\");\n      auto alice_id = alice.id;\n\n      //account_id_type() creates some ops\n      create_bitasset(\"CNY\", account_id_type());\n      create_bitasset(\"USD\", account_id_type());\n\n      // alice makes 1 op\n      create_bitasset(\"EUR\", alice_id);\n\n      // account_id_type() creates dan(account not tracked)\n      create_account(\"dan\");\n\n      generate_block();\n\n      // all account_id_type() should have 4 ops {4,2,1,0}\n      vector<operation_history_object> histories = hist_api.get_account_history(\"committee-account\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 4u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);\n      BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);\n      BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);\n\n      // all alice account should have 2 ops {3, 0}\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n      BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);\n\n      // alice first op should be {0}\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(0), 1, operation_history_id_type(1));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);\n\n      // alice second op should be {3}\n      histories = hist_api.get_account_history(\"alice\", operation_history_id_type(1), 1, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);\n\n      // anything against dan should be {}\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(0), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(1), 10, operation_history_id_type(0));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n      histories = hist_api.get_account_history(\"dan\", operation_history_id_type(1), 1, operation_history_id_type(2));\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(get_account_history_operations) {\n   try {\n      graphene::app::history_api hist_api(app);\n\n      //account_id_type() do 3 ops\n      create_bitasset(\"CNY\", account_id_type());\n      create_account(\"sam\");\n      create_account(\"alice\");\n\n      generate_block();\n      fc::usleep(fc::milliseconds(2000));\n\n      int asset_create_op_id = operation::tag<asset_create_operation>::value;\n      int account_create_op_id = operation::tag<account_create_operation>::value;\n\n      //account_id_type() did 1 asset_create op\n      vector<operation_history_object> histories = hist_api.get_account_history_operations(\n            \"committee-account\", asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);\n      BOOST_CHECK_EQUAL(histories[0].op.which(), asset_create_op_id);\n\n      //account_id_type() did 2 account_create ops\n      histories = hist_api.get_account_history_operations(\n            \"committee-account\", account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n      BOOST_CHECK_EQUAL(histories.size(), 2u);\n      BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n      // No asset_create op larger than id1\n      histories = hist_api.get_account_history_operations(\n            \"committee-account\", asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 100);\n      BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n      // Limit 1 returns 1 result\n      histories = hist_api.get_account_history_operations(\n            \"committee-account\", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1);\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n      // alice has 1 op\n      histories = hist_api.get_account_history_operations(\n         \"alice\", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 100);\n      BOOST_CHECK_EQUAL(histories.size(), 1u);\n      BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n      // create a bunch of accounts\n      for(int i = 0; i < 80; ++i)\n      {\n         std::string acct_name = \"mytempacct\" + std::to_string(i);\n         create_account(acct_name);\n      }\n      generate_block();\n\n      // history is set to limit transactions to 75 (see database_fixture.hpp)\n      // so asking for more should only return 75 (and not throw exception, \n      // see https://github.com/bitshares/bitshares-core/issues/1490\n      histories = hist_api.get_account_history_operations(\n            \"committee-account\", account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n      BOOST_CHECK_EQUAL(histories.size(), 75u);\n      if (histories.size() > 0)\n         BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n\n   } catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n//new test case for increasing the limit based on the config file\nBOOST_AUTO_TEST_CASE(api_limit_get_account_history_operations) {\n   try {\n   graphene::app::history_api hist_api(app);\n   //account_id_type() do 3 ops\n   create_bitasset(\"CNY\", account_id_type());\n   create_account(\"sam\");\n   create_account(\"alice\");\n\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n\n   int asset_create_op_id = operation::tag<asset_create_operation>::value;\n   int account_create_op_id = operation::tag<account_create_operation>::value;\n\n   //account_id_type() did 1 asset_create op\n   vector<operation_history_object> histories = hist_api.get_account_history_operations(\n\t\"committee-account\", asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 200);\n   BOOST_CHECK_EQUAL(histories.size(), 1u);\n   BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);\n   BOOST_CHECK_EQUAL(histories[0].op.which(), asset_create_op_id);\n\n   //account_id_type() did 2 account_create ops\n   histories = hist_api.get_account_history_operations(\n\t\"committee-account\", account_create_op_id, operation_history_id_type(), operation_history_id_type(), 200);\n   BOOST_CHECK_EQUAL(histories.size(), 2u);\n   BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n   // No asset_create op larger than id1\n   histories = hist_api.get_account_history_operations(\n\t\"committee-account\", asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 200);\n   BOOST_CHECK_EQUAL(histories.size(), 0u);\n\n   // Limit 1 returns 1 result\n   histories = hist_api.get_account_history_operations(\n\t\"committee-account\", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1);\n   BOOST_CHECK_EQUAL(histories.size(), 1u);\n   BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n   // alice has 1 op\n   histories = hist_api.get_account_history_operations(\n\t\"alice\", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 200);\n   BOOST_CHECK_EQUAL(histories.size(), 1u);\n   BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n   // create a bunch of accounts\n   for(int i = 0; i < 126; ++i)\n   {\n   \tstd::string acct_name = \"mytempacct\" + std::to_string(i);\n      create_account(acct_name);\n   }\n   generate_block();\n\n   // history is set to limit transactions to 125 (see database_fixture.hpp)\n   // so asking for more should only return 125 (and not throw exception,\n   // see https://github.com/bitshares/bitshares-core/issues/1490\n   GRAPHENE_CHECK_THROW(hist_api.get_account_history_operations(\"commitee-account\", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 301), fc::exception);\n   histories = hist_api.get_account_history_operations(\"committee-account\", account_create_op_id, operation_history_id_type(), operation_history_id_type(), 200);\n   BOOST_REQUIRE_EQUAL( histories.size(), 125u );\n   }\n   catch (fc::exception &e)\n   {\n \tedump((e.to_detail_string()));\n   throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(api_limit_get_account_history) {\n   try{\n   graphene::app::history_api hist_api(app);\n   //account_id_type() do 3 ops\n   create_bitasset(\"USD\", account_id_type());\n   create_account(\"dan\");\n   create_account(\"bob\");\n\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n\n   int asset_create_op_id = operation::tag<asset_create_operation>::value;\n   int account_create_op_id = operation::tag<account_create_operation>::value;\n   //account_id_type() did 3 ops and includes id0\n   vector<operation_history_object> histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 210, operation_history_id_type());\n\n   BOOST_CHECK_EQUAL(histories.size(), 3u);\n   BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);\n   BOOST_CHECK_EQUAL(histories[2].op.which(), asset_create_op_id);\n\n   // 1 account_create op larger than id1\n   histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(1), 210, operation_history_id_type());\n   BOOST_CHECK_EQUAL(histories.size(), 1u);\n   BOOST_CHECK(histories[0].id.instance() != 0u);\n   BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n\n   // Limit 2 returns 2 result\n   histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 2, operation_history_id_type());\n   BOOST_CHECK_EQUAL(histories.size(), 2u);\n   BOOST_CHECK(histories[1].id.instance() != 0u);\n   BOOST_CHECK_EQUAL(histories[1].op.which(), account_create_op_id);\n   // bob has 1 op\n   histories = hist_api.get_account_history(\"bob\", operation_history_id_type(), 210, operation_history_id_type());\n   BOOST_CHECK_EQUAL(histories.size(), 1u);\n   BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);\n\n   // create a bunch of accounts\n   for(int i = 0; i < 126; ++i)\n   {\n     \tstd::string acct_name = \"mytempacct\" + std::to_string(i);\n     \tcreate_account(acct_name);\n   }\n   generate_block();\n\n   GRAPHENE_CHECK_THROW(hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 260, operation_history_id_type()), fc::exception);\n   histories = hist_api.get_account_history(\"1.2.0\", operation_history_id_type(), 210, operation_history_id_type());\n   BOOST_REQUIRE_EQUAL( histories.size(), 125u );\n   } catch (fc::exception &e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\nBOOST_AUTO_TEST_CASE(api_limit_get_relative_account_history) {\n   try{\n   graphene::app::history_api hist_api(app);\n   //account_id_type() do 3 ops\n   create_bitasset(\"USD\", account_id_type());\n   create_account(\"dan\");\n   create_account(\"bob\");\n\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n\n   GRAPHENE_CHECK_THROW(hist_api.get_relative_account_history(\"1.2.0\", 126, 260, 0), fc::exception);\n   vector<operation_history_object> histories = hist_api.get_relative_account_history(\"1.2.0\", 126, 210, 0);\n   BOOST_REQUIRE_EQUAL( histories.size(), 0u );\n\n   } catch (fc::exception &e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(api_limit_get_account_history_by_operations) {\n   try {\n   graphene::app::history_api hist_api(app);\n   flat_set<uint16_t> operation_types;\n   //account_id_type() do 3 ops\n   create_bitasset(\"USD\", account_id_type());\n   create_account(\"dan\");\n   create_account(\"bob\");\n   generate_block();\n   fc::usleep(fc::milliseconds(100));\n   GRAPHENE_CHECK_THROW(hist_api.get_account_history_by_operations(\"1.2.0\", operation_types, 0, 260), fc::exception);\n   history_operation_detail histories = hist_api.get_account_history_by_operations(\"1.2.0\", operation_types, 0, 210);\n   BOOST_REQUIRE_EQUAL( histories.total_count, 3u );\n   }\n   catch (fc::exception &e) {\n   edump((e.to_detail_string()));\n   throw;\n   }\n}\n\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/htlc_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 jmjatlanta and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n// below are for random bytes for htlc\n#include <vector>\n#include <random>\n#include <climits>\n#include <algorithm>\n#include <functional>\n// for htlc timeout\n#include <chrono>\n#include <thread>\n\n#include <boost/test/unit_test.hpp>\n\n#include <boost/container/flat_set.hpp>\n\n#include <fc/optional.hpp>\n\n#include <graphene/protocol/htlc.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/htlc_object.hpp>\n\n#include <graphene/app/api.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture )\n\nvoid generate_random_preimage(uint16_t key_size, std::vector<char>& vec)\n{\n\tstd::independent_bits_engine<std::default_random_engine, sizeof(unsigned), unsigned int> rbe;\n\tstd::generate(begin(vec), end(vec), std::ref(rbe));\n\treturn;\n}\n\nvoid advance_past_htlc_first_hardfork(database_fixture* db_fixture)\n{\n   db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME);\n   set_expiration(db_fixture->db, db_fixture->trx);\n   db_fixture->set_htlc_committee_parameters();\n   set_expiration(db_fixture->db, db_fixture->trx);\n}\n\nBOOST_AUTO_TEST_CASE( htlc_expires )\n{\ntry {\n   ACTORS((alice)(bob));\n\n   int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n\n   advance_past_htlc_first_hardfork(this);\n\n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(256);\n   generate_random_preimage(preimage_size, pre_image);\n\n   graphene::chain::htlc_id_type alice_htlc_id;\n   // cler everything out\n   generate_block();\n   trx.clear();\n   // Alice puts a contract on the blockchain\n   {\n      graphene::chain::htlc_create_operation create_operation;\n      BOOST_TEST_MESSAGE(\"Alice (who has 100 coins, is transferring 3 coins to Bob\");\n      create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 60;\n      create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n      graphene::chain::signed_block blk = generate_block();\n      processed_transaction alice_trx = blk.transactions[0];\n      alice_htlc_id = alice_trx.operation_results[0].get<object_id_type>();\n      generate_block();\n   }\n\n   // verify funds on hold... 100 - 3 = 97, minus the 4 coin fee = 93\n   BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION );\n\n   // make sure Bob (or anyone) can see the details of the transaction\n   graphene::app::database_api db_api(db);\n   auto obj = db_api.get_objects( {alice_htlc_id }).front();\n   graphene::chain::htlc_object htlc = obj.template as<graphene::chain::htlc_object>(GRAPHENE_MAX_NESTED_OBJECTS);\n\n   // someone else attempts to extend it (bob says he's alice, but he's not)\n   {\n      graphene::chain::htlc_extend_operation bad_extend;\n      bad_extend.htlc_id = alice_htlc_id;\n      bad_extend.seconds_to_add = 10;\n      bad_extend.fee = db.get_global_properties().parameters.current_fees->calculate_fee(bad_extend);\n      bad_extend.update_issuer = alice_id;\n      trx.operations.push_back(bad_extend);\n      sign(trx, bob_private_key);\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, database::skip_nothing ), fc::exception );\n      trx.clear();\n   }\n   // someone else attempts to extend it (bob wants to extend Alice's contract)\n   {\n      graphene::chain::htlc_extend_operation bad_extend;\n      bad_extend.htlc_id = alice_htlc_id;\n      bad_extend.seconds_to_add = 10;\n      bad_extend.fee = db.get_global_properties().parameters.current_fees->calculate_fee(bad_extend);\n      bad_extend.update_issuer = bob_id;\n      trx.operations.push_back(bad_extend);\n      sign(trx, bob_private_key);\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0 ), fc::exception );\n      trx.clear();\n   }\n   // attempt to extend it with too much time\n   {\n      graphene::chain::htlc_extend_operation big_extend;\n      big_extend.htlc_id = alice_htlc_id;\n      big_extend.seconds_to_add = db.get_global_properties().parameters.extensions.value\n            .updatable_htlc_options->max_timeout_secs + 10;\n      big_extend.fee = db.get_global_properties().parameters.current_fees->calculate_fee(big_extend);\n      big_extend.update_issuer = alice_id;\n      trx.operations.push_back(big_extend);\n      sign(trx, alice_private_key);\n      GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      trx.clear();\n   }\n\n   // attempt to extend properly\n   {\n      graphene::chain::htlc_extend_operation extend;\n      extend.htlc_id = alice_htlc_id;\n      extend.seconds_to_add = 10;\n      extend.fee = db.get_global_properties().parameters.current_fees->calculate_fee(extend);\n      extend.update_issuer = alice_id;\n      trx.operations.push_back(extend);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n\n   // let it expire (wait for timeout)\n   generate_blocks( db.head_block_time() + fc::seconds(120) );\n   // verify funds return (minus the fees)\n   BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 92 * GRAPHENE_BLOCKCHAIN_PRECISION );\n   // verify Bob cannot execute the contract after the fact\n} FC_LOG_AND_RETHROW()\n}\n\n/****\n * @brief helper to create htlc_create_operation\n */\nhtlc_create_operation create_htlc(const database& db, const account_id_type& from, const account_id_type& to,\n      const asset& amount, const graphene::protocol::htlc_hash& preimage_hash, uint16_t preimage_size,\n      uint64_t seconds, const fc::optional<memo_data>& memo = fc::optional<memo_data>())\n{\n   htlc_create_operation ret_val;\n   ret_val.from = from;\n   ret_val.to = to;\n   ret_val.amount = amount;\n   ret_val.preimage_hash = preimage_hash;\n   ret_val.preimage_size = preimage_size;\n   ret_val.claim_period_seconds = seconds;\n   ret_val.extensions.value.memo = memo;\n   ret_val.fee = db.get_global_properties().parameters.current_fees->calculate_fee(ret_val);\n   return ret_val;\n}\n\n/****\n * @brief helper to create a proposal\n */\nproposal_create_operation create_proposal(const database& db, const account_id_type& from, \n      const htlc_create_operation& op, const fc::time_point_sec& expiration)\n{\n      proposal_create_operation ret_val;\n      ret_val.fee_paying_account = from;\n      ret_val.expiration_time = expiration;\n      ret_val.proposed_ops.emplace_back(op);\n      ret_val.fee = db.get_global_properties().parameters.current_fees->calculate_fee(ret_val);\n      return ret_val;\n}\n\nBOOST_AUTO_TEST_CASE( htlc_hf_bsip64 )\n{\ntry {\n   ACTORS((alice)(bob)(joker));\n\n   int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n   transfer( committee_account, joker_id, graphene::chain::asset(init_balance) );\n   transfer( committee_account, bob_id, graphene::chain::asset(init_balance) );\n\n   advance_past_htlc_first_hardfork(this);\n\n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(256);\n   generate_random_preimage(preimage_size, pre_image);\n\n   graphene::chain::htlc_id_type alice_htlc_id;\n   // cler everything out\n   generate_block();\n   trx.clear();\n\n   /***\n    * Proposals before the hardfork\n    */\n   {\n      BOOST_TEST_MESSAGE(\n            \"Alice is creating a proposal with an HTLC that contains a memo before the hardfork (should fail)\");\n      memo_data data;\n      data.from = alice_public_key;\n      data.to = bob_public_key;\n      data.set_message( alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60, data);\n      graphene::chain::proposal_create_operation prop1 = create_proposal(\n            db, alice_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop1);\n      sign(trx, alice_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"memo\");\n      trx.clear();\n   }   \n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating a proposal with HASH160 (should fail)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::hash160>(pre_image), 0, 60);\n      graphene::chain::proposal_create_operation prop1 = create_proposal(\n            db, alice_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop1);\n      sign(trx, alice_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"HASH160\");\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating a proposal with a preimage size of 0 (should pass)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60);\n      graphene::chain::proposal_create_operation prop1 = create_proposal(\n            db, alice_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop1);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n   /***\n    * HTLC Contracts (non-proposals) before hardfork\n    */\n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating an HTLC that contains a memo before the hardfork (should fail)\");\n      memo_data data;\n      data.from = alice_public_key;\n      data.to = bob_public_key;\n      data.set_message( alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60, data);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"memo\");\n      trx.clear();\n   }   \n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating an HTLC with HASH160 (should fail)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::hash160>(pre_image), 0, 60);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"HASH160\");\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating an HTLC with a preimage size of 0 (should pass)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      htlc_id_type htlc_id = PUSH_TX(db, trx, ~0).operation_results[0].get<object_id_type>();\n      trx.clear();\n      BOOST_TEST_MESSAGE(\"Bob attempts to redeem, but can't because preimage size is 0 (should fail)\");\n      graphene::chain::htlc_redeem_operation redeem;\n      redeem.htlc_id = htlc_id;\n      redeem.preimage = pre_image;\n      redeem.redeemer = bob_id;\n      redeem.fee = db.current_fee_schedule().calculate_fee(redeem);\n      trx.operations.push_back(redeem);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"Preimage size mismatch\");\n      trx.clear();\n   }\n\n   // Alice creates an asset\n   BOOST_TEST_MESSAGE(\"Create ALICECOIN so transfer_restricted can be controlled\");\n   const asset_id_type uia_id = create_user_issued_asset( \"ALICECOIN\", alice, transfer_restricted).id;\n   BOOST_TEST_MESSAGE(\"Issuing ALICECOIN to Bob\");\n   issue_uia(bob, asset(10000, uia_id) );\n   // verify transfer restrictions are in place\n   REQUIRE_EXCEPTION_WITH_TEXT(transfer(bob, joker, asset(1, uia_id)), \"transfer\");\n   trx.operations.clear();\n\n   {\n      BOOST_TEST_MESSAGE(\"Bob wants to transfer ALICECOIN, which is transfer_restricted (allowed before HF)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id,\n         asset(3, uia_id), hash_it<fc::sha256>(pre_image), preimage_size, 60);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\n            \"Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, asset(3,uia_id),\n            hash_it<fc::sha256>(pre_image), preimage_size, 60);\n      graphene::chain::proposal_create_operation prop_create = create_proposal(\n            db, bob_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop_create);\n      sign(trx, bob_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n\n   BOOST_TEST_MESSAGE(\"Fast Forwarding beyond HF BSIP64\");\n   generate_blocks( HARDFORK_CORE_BSIP64_TIME + 60 );\n   set_expiration( db, trx );\n\n   /***\n    * Proposals after the hardfork\n    */\n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating a proposal with an HTLC that contains a memo (should pass)\");\n      memo_data data;\n      data.from = alice_public_key;\n      data.to = bob_public_key;\n      data.set_message( alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60, data);\n      graphene::chain::proposal_create_operation prop1 = create_proposal(\n            db, alice_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop1);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }   \n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating a proposal with HASH160 (should pass)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::hash160>(pre_image), 0, 60);\n      graphene::chain::proposal_create_operation prop1 = create_proposal(\n            db, alice_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop1);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating a proposal with a preimage size of 0 (should pass)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60);\n      graphene::chain::proposal_create_operation prop1 = create_proposal(\n            db, alice_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop1);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n   /***\n    * HTLC Contracts (non-proposals) after hardfork\n    */\n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating an HTLC that contains a memo after the hardfork (should pass)\");\n      memo_data data;\n      data.from = alice_public_key;\n      data.to = bob_public_key;\n      data.set_message( alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::sha256>(pre_image), 0, 60, data);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }   \n   {\n      BOOST_TEST_MESSAGE(\"Alice is creating an HTLC with HASH160 (should pass)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, \n            asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it<fc::hash160>(pre_image), 0, 60);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"Bob wants to transfer ALICECOIN, which is transfer_restricted (no longer allowed)\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id,\n         asset(3, uia_id), hash_it<fc::sha256>(pre_image), preimage_size, 60);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"transfer_restricted\");\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\n            \"Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later\");\n      graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, asset(3,uia_id),\n            hash_it<fc::sha256>(pre_image), preimage_size, 60);\n      graphene::chain::proposal_create_operation prop_create = create_proposal(\n            db, bob_id, create_operation, db.head_block_time() + 100);\n      trx.operations.push_back(prop_create);\n      sign(trx, bob_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n   }\n   {\n      BOOST_TEST_MESSAGE(\"A memo field should include a charge per kb (uses fee from transfer_operation)\");\n      htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3), hash_it<fc::sha256>(pre_image),\n            preimage_size, 60);\n      asset no_memo_fee = op.fee;\n      memo_data data;\n      data.from = alice_public_key;\n      data.to = bob_public_key;\n      data.set_message( alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n      op.extensions.value.memo = data;\n      asset with_memo_fee = db.current_fee_schedule().calculate_fee(op);\n      BOOST_CHECK_GT( with_memo_fee.amount.value, no_memo_fee.amount.value );\n   }\n   // After HF 64, a zero in the preimage_size means 2 things, no preimage (can happen, but who would do such a\n   // thing), or simply skip the size validation. To test, we must attempt to redeem both cases\n   {\n      // case 1: 0 preimage with 0 preimage_size\n      BOOST_TEST_MESSAGE(\"Attempt to create an HTLC with no preimage (should pass)\");\n      htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION),\n            hash_it<fc::sha256>(std::vector<char>()), 0, 60);\n      trx.operations.push_back(op);\n      sign(trx, alice_private_key);\n      graphene::protocol::processed_transaction results = PUSH_TX(db, trx, ~0);\n      trx.operations.clear();\n      htlc_id_type htlc_id = results.operation_results[0].get<object_id_type>();\n      BOOST_TEST_MESSAGE(\"Attempt to redeem HTLC that has no preimage, but include one anyway (should fail)\");\n      htlc_redeem_operation redeem;\n      redeem.htlc_id = htlc_id;\n      redeem.preimage = pre_image;\n      redeem.redeemer = bob_id;\n      redeem.fee = db.current_fee_schedule().calculate_fee(redeem);\n      trx.operations.push_back(redeem);\n      sign(trx, bob_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"Provided preimage does not generate\");\n      trx.operations.clear();\n      BOOST_TEST_MESSAGE(\"Attempt to redeem HTLC that has no preimage (should pass)\");\n      redeem.preimage = std::vector<char>();\n      redeem.fee = db.current_fee_schedule().calculate_fee(redeem);\n      trx.operations.push_back(redeem);\n      sign(trx, bob_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.operations.clear();\n   }\n   {\n      // case 2: a real preimage with 0 preimage size\n      htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION),\n            hash_it<fc::hash160>(pre_image), 0, 60);\n      trx.operations.push_back(op);\n      sign(trx, alice_private_key);\n      graphene::protocol::processed_transaction results = PUSH_TX(db, trx, ~0);\n      trx.operations.clear();\n      htlc_id_type htlc_id = results.operation_results[0].get<object_id_type>();\n      BOOST_TEST_MESSAGE(\"Attempt to redeem with no preimage (should fail)\");\n      htlc_redeem_operation redeem;\n      redeem.htlc_id = htlc_id;\n      redeem.preimage = std::vector<char>();\n      redeem.redeemer = bob_id;\n      redeem.fee = db.current_fee_schedule().calculate_fee(redeem);\n      trx.operations.push_back(redeem);\n      sign(trx, bob_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"Provided preimage does not generate\");\n      trx.operations.clear();\n      BOOST_TEST_MESSAGE(\"Attempt to redeem with no preimage size, but incorrect preimage\");\n      redeem.preimage = std::vector<char>{'H','e','l','l','o'};\n      redeem.fee = db.current_fee_schedule().calculate_fee(redeem);\n      trx.operations.push_back(redeem);\n      sign(trx, bob_private_key);\n      REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), \"Provided preimage does not generate\");\n      trx.operations.clear();\n      BOOST_TEST_MESSAGE(\"Attempt to redeem with no preimage size (should pass)\");\n      redeem.preimage = pre_image;\n      redeem.fee = db.current_fee_schedule().calculate_fee(redeem);\n      trx.operations.push_back(redeem);\n      sign(trx, bob_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.operations.clear();\n   }\n} FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( htlc_fulfilled )\n{\ntry {\n   ACTORS((alice)(bob)(joker));\n\n   int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n   transfer( committee_account, bob_id, graphene::chain::asset(init_balance) );\n   transfer( committee_account, joker_id, graphene::chain::asset(init_balance) );\n\n   advance_past_htlc_first_hardfork(this);\n   \n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(preimage_size);\n   generate_random_preimage(preimage_size, pre_image);\n\n   graphene::chain::htlc_id_type alice_htlc_id;\n   // clear everything out\n   generate_block();\n   trx.clear();\n   // Alice puts a contract on the blockchain\n   {\n      graphene::chain::htlc_create_operation create_operation;\n\n      create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 86400;\n      create_operation.preimage_hash = hash_it<fc::sha1>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n      trx.operations.push_back( create_operation );\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n      graphene::chain::signed_block blk = generate_block();\n      processed_transaction alice_trx = blk.transactions[0];\n      alice_htlc_id = alice_trx.operation_results[0].get<object_id_type>();\n   }\n\n   // make sure Alice's money gets put on hold (100 - 20 - 4(fee) )\n   BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION );\n\n   // extend the timeout so that Bob has more time\n   {\n      graphene::chain::htlc_extend_operation extend_operation;\n      extend_operation.htlc_id = alice_htlc_id;\n      extend_operation.seconds_to_add = 86400;\n      extend_operation.update_issuer = alice_id;\n      extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation );\n      trx.operations.push_back( extend_operation );\n      sign( trx, alice_private_key );\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n      generate_blocks( db.head_block_time() + fc::seconds(87000) );\n      set_expiration( db, trx );\n   }\n\n   // make sure Alice's money is still on hold, and account for extra fee\n   BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION );\n\n   // grab number of history objects to make sure everyone gets notified\n   size_t alice_num_history = get_operation_history(alice_id).size();\n   size_t bob_num_history = get_operation_history(bob_id).size();\n   size_t joker_num_history = get_operation_history(joker_id).size();\n\n   // joker sends a redeem operation to claim the funds for bob\n   {\n      graphene::chain::htlc_redeem_operation update_operation;\n      update_operation.redeemer = joker_id;\n      update_operation.htlc_id = alice_htlc_id;\n      update_operation.preimage = pre_image;\n      update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation );\n      trx.operations.push_back( update_operation );\n      sign(trx, joker_private_key);\n      PUSH_TX( db, trx, ~0 );\n      generate_block();\n      trx.clear();\n   }\n   // verify funds end up in Bob's account (100 + 20 )\n   BOOST_CHECK_EQUAL( get_balance(bob_id,   graphene::chain::asset_id_type()), 120 * GRAPHENE_BLOCKCHAIN_PRECISION );\n   // verify funds remain out of Alice's acount ( 100 - 20 - 4 )\n   BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION );\n   // verify all three get notified\n   BOOST_CHECK_EQUAL( get_operation_history(alice_id).size(), alice_num_history + 1);\n   BOOST_CHECK_EQUAL( get_operation_history(bob_id).size(), bob_num_history + 1);\n   BOOST_CHECK_EQUAL( get_operation_history(joker_id).size(), joker_num_history + 1);\n} FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( other_peoples_money )\n{\ntry {\n   advance_past_htlc_first_hardfork(this);\n\t\n   ACTORS((alice)(bob));\n\n   int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION );\n\n   transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n\n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(256);\n   generate_random_preimage(preimage_size, pre_image);\n\n   graphene::chain::htlc_id_type alice_htlc_id;\n   // cler everything out\n   generate_block();\n   trx.clear();\n   // Bob attempts to put a contract on the blockchain using Alice's funds\n   {\n      graphene::chain::htlc_create_operation create_operation;\n      create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 3;\n      create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n      trx.operations.push_back(create_operation);\n      sign(trx, bob_private_key);\n      GRAPHENE_CHECK_THROW( PUSH_TX( db, trx ), fc::exception);\n      trx.clear();\n   }\n   // now try the same but with Alice's signature (should work)\n   {\n      graphene::chain::htlc_create_operation create_operation;\n      create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 3;\n      create_operation.preimage_hash = hash_it<fc::ripemd160>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n} FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( htlc_hardfork_test )\n{ \n   try {\n      ACTORS( (alice) );\n      int64_t init_balance(10000 * GRAPHENE_BLOCKCHAIN_PRECISION);\n      transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n      {\n         // try to set committee parameters before hardfork\n         proposal_create_operation cop = proposal_create_operation::committee_proposal(\n               db.get_global_properties().parameters, db.head_block_time());\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n         committee_member_update_global_parameters_operation cmuop;\n         graphene::chain::htlc_options new_params;\n         new_params.max_preimage_size = 2048;\n         new_params.max_timeout_secs = 60 * 60 * 24 * 28;\n         cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params;\n         cop.proposed_ops.emplace_back(cmuop);\n         trx.operations.push_back(cop);\n         // update with signatures\n         proposal_update_operation uop;\n         uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         uop.active_approvals_to_add = {get_account(\"init0\").get_id(), get_account(\"init1\").get_id(),\n                                       get_account(\"init2\").get_id(), get_account(\"init3\").get_id(),\n                                       get_account(\"init4\").get_id(), get_account(\"init5\").get_id(),\n                                       get_account(\"init6\").get_id(), get_account(\"init7\").get_id()};\n         trx.operations.push_back(uop);\n         sign( trx, init_account_priv_key );\n         BOOST_TEST_MESSAGE(\"Sending proposal.\");\n         GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception);\n         BOOST_TEST_MESSAGE(\"Verifying that proposal did not succeeed.\");\n         BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid());\n         trx.clear();\n      }\n\n      {\n         BOOST_TEST_MESSAGE(\"Attempting to set HTLC fees before hard fork.\");\n\n         // get existing fee_schedule\n         const chain_parameters& existing_params = db.get_global_properties().parameters;\n         const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees);\n         // create a new fee_shedule\n         std::shared_ptr<fee_schedule_type> new_fee_schedule = std::make_shared<fee_schedule_type>();\n         new_fee_schedule->scale = existing_fee_schedule.scale;\n         // replace the old with the new\n         flat_map<uint64_t, graphene::chain::fee_parameters> params_map = get_htlc_fee_parameters();\n         for(auto param : existing_fee_schedule.parameters)\n         {\n            auto itr = params_map.find(param.which());\n            if (itr == params_map.end())\n               new_fee_schedule->parameters.insert(param);\n            else\n            {\n               new_fee_schedule->parameters.insert( (*itr).second);\n            }\n         }\n         proposal_create_operation cop = proposal_create_operation::committee_proposal(\n               db.get_global_properties().parameters, db.head_block_time());\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n         committee_member_update_global_parameters_operation uop;\n         uop.new_parameters.current_fees = new_fee_schedule;\n         cop.proposed_ops.emplace_back(uop);\n         cop.fee = asset( 100000 );\n         trx.operations.push_back( cop );\n         GRAPHENE_CHECK_THROW(db.push_transaction( trx ), fc::exception);\n         trx.clear();\n      }\n\n      // now things should start working...\n      BOOST_TEST_MESSAGE(\"Advancing to HTLC hardfork time.\");\n      advance_past_htlc_first_hardfork(this);\n\n      proposal_id_type good_proposal_id;\n      BOOST_TEST_MESSAGE( \"Creating a proposal to change the max_preimage_size to 2048 and set higher fees\" );\n      {\n         // get existing fee_schedule\n         const chain_parameters& existing_params = db.get_global_properties().parameters;\n         const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees);\n         // create a new fee_shedule\n         std::shared_ptr<fee_schedule_type> new_fee_schedule = std::make_shared<fee_schedule_type>();\n         new_fee_schedule->scale = existing_fee_schedule.scale;\n         // replace the old with the new\n         flat_map<uint64_t, graphene::chain::fee_parameters> params_map = get_htlc_fee_parameters();\n         for(auto param : existing_fee_schedule.parameters)\n         {\n            auto itr = params_map.find(param.which());\n            if (itr == params_map.end())\n               new_fee_schedule->parameters.insert(param);\n            else\n            {\n               new_fee_schedule->parameters.insert( (*itr).second);\n            }\n         }\n         proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties()\n               .parameters, db.head_block_time());\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;\n         committee_member_update_global_parameters_operation uop;\n         graphene::chain::htlc_options new_params;\n         new_params.max_preimage_size = 2048;\n         new_params.max_timeout_secs = 60 * 60 * 24 * 28;\n         uop.new_parameters.extensions.value.updatable_htlc_options = new_params;\n         uop.new_parameters.current_fees = new_fee_schedule;\n         cop.proposed_ops.emplace_back(uop);\n         trx.operations.push_back(cop);\n         graphene::chain::processed_transaction proc_trx =db.push_transaction(trx);\n         good_proposal_id = proc_trx.operation_results[0].get<object_id_type>();\n      }\n\n      BOOST_TEST_MESSAGE( \"Updating proposal by signing with the committee_member private key\" );\n      {\n         proposal_update_operation uop;\n         uop.proposal = good_proposal_id;\n         uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         uop.active_approvals_to_add = {get_account(\"init0\").get_id(), get_account(\"init1\").get_id(),\n                                       get_account(\"init2\").get_id(), get_account(\"init3\").get_id(),\n                                       get_account(\"init4\").get_id(), get_account(\"init5\").get_id(),\n                                       get_account(\"init6\").get_id(), get_account(\"init7\").get_id()};\n         trx.operations.push_back(uop);\n         sign( trx, init_account_priv_key );\n         db.push_transaction(trx);\n         BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db));\n      }\n      BOOST_TEST_MESSAGE( \"Verifying that the parameters didn't change immediately\" );\n\n      BOOST_CHECK_EQUAL(\n            db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u);\n\n      BOOST_TEST_MESSAGE( \"Generating blocks until proposal expires\" );\n      generate_blocks(good_proposal_id(db).expiration_time + 5);\n      BOOST_TEST_MESSAGE( \"Verify that the parameters still have not changed\" );\n      BOOST_CHECK_EQUAL(db.get_global_properties()\n            .parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u);\n\n      BOOST_TEST_MESSAGE( \"Generating blocks until next maintenance interval\" );\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();   // get the maintenance skip slots out of the way\n\n      BOOST_TEST_MESSAGE( \"Verify that the change has been implemented\" );\n      \n      BOOST_CHECK_EQUAL(db.get_global_properties()\n            .parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u);\n      const graphene::chain::fee_schedule& current_fee_schedule =\n            *(db.get_global_properties().parameters.current_fees);\n      const htlc_create_operation::fee_parameters_type& htlc_fee \n            = current_fee_schedule.get<htlc_create_operation>();\n      BOOST_CHECK_EQUAL(htlc_fee.fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   } \n   FC_LOG_AND_RETHROW() \n}\n\nBOOST_AUTO_TEST_CASE( htlc_before_hardfork )\n{ try {\n   ACTORS((alice)(bob));\n\n   int64_t init_balance(100000);\n\n   transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n\n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(256);\n   generate_random_preimage(preimage_size, pre_image);\n\n   graphene::chain::htlc_id_type alice_htlc_id;\n   // clear everything out\n   generate_block();\n   trx.clear();\n\n   // Alice tries to put a contract on the blockchain\n   {\n      graphene::chain::htlc_create_operation create_operation;\n\n      create_operation.amount = graphene::chain::asset( 10000 );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 60;\n      create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception);\n      trx.clear();\n   }\n\n   // Propose htlc_create\n   {\n      proposal_create_operation pco;\n      pco.expiration_time = db.head_block_time() + fc::minutes(1);\n      pco.fee_paying_account = alice_id;\n\n      graphene::chain::htlc_create_operation create_operation;\n      create_operation.amount = graphene::chain::asset( 10000 );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 60;\n      create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n\n      pco.proposed_ops.emplace_back( create_operation );\n      trx.operations.push_back( pco );\n      GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception );\n      trx.clear();\n   }\n\n   // Propose htlc_redeem\n   {\n      proposal_create_operation pco;\n      pco.expiration_time = db.head_block_time() + fc::minutes(1);\n      pco.fee_paying_account = alice_id;\n\n      graphene::chain::htlc_redeem_operation rop;\n      rop.redeemer = bob_id;\n      rop.htlc_id = alice_htlc_id;\n      string preimage_str = \"Arglebargle\";\n      rop.preimage.insert( rop.preimage.begin(), preimage_str.begin(), preimage_str.end() );\n\n      pco.proposed_ops.emplace_back( rop );\n      trx.operations.push_back( pco );\n      GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception );\n      trx.clear();\n   }\n\n   // Propose htlc_extend\n   {\n      proposal_create_operation pco;\n      pco.expiration_time = db.head_block_time() + fc::minutes(1);\n      pco.fee_paying_account = alice_id;\n\n      graphene::chain::htlc_extend_operation xop;\n      xop.htlc_id = alice_htlc_id;\n      xop.seconds_to_add = 100;\n      xop.update_issuer = alice_id;\n\n      pco.proposed_ops.emplace_back( xop );\n      trx.operations.push_back( pco );\n      GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception );\n      trx.clear();\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( fee_calculations )\n{\n   // create\n   {\n      htlc_create_operation::fee_parameters_type create_fee;\n      create_fee.fee = 2;\n      create_fee.fee_per_day = 2;\n      htlc_create_operation create;\n      // no days\n      create.claim_period_seconds = 0;\n      BOOST_CHECK_EQUAL( create.calculate_fee(create_fee, 2).value, 2 );\n      // exactly 1 day\n      create.claim_period_seconds = 60 * 60 * 24;\n      BOOST_CHECK_EQUAL( create.calculate_fee(create_fee, 2).value, 4 );\n      // tad over a day\n      create.claim_period_seconds++;\n      BOOST_CHECK_EQUAL( create.calculate_fee(create_fee, 2).value, 6 );\n   }\n   // redeem\n   {\n      htlc_redeem_operation::fee_parameters_type redeem_fee;\n      redeem_fee.fee_per_kb = 2;\n      redeem_fee.fee = 2;\n      htlc_redeem_operation redeem;\n      // no preimage\n      redeem.preimage = std::vector<char>();\n      BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ;\n      // exactly 1KB\n      std::string test(1024, 'a');\n      redeem.preimage = std::vector<char>( test.begin(), test.end() );\n      BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ;\n      // just 1 byte over 1KB\n      std::string larger(1025, 'a');\n      redeem.preimage = std::vector<char>( larger.begin(), larger.end() );\n      BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 6 ) ;\n   }\n   // extend\n   {\n      htlc_extend_operation::fee_parameters_type extend_fee;\n      extend_fee.fee = 2;\n      extend_fee.fee_per_day = 2;\n      htlc_extend_operation extend;\n      // no days\n      extend.seconds_to_add = 0;\n      BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 2 );\n      // exactly 1 day\n      extend.seconds_to_add = 60 * 60 * 24;\n      BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 );\n      // 1 day and 1 second\n      extend.seconds_to_add++;\n      BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 6 );\n   }\n}\n\nBOOST_AUTO_TEST_CASE( htlc_blacklist )\n{\ntry {\n   ACTORS((nathan)(alice)(bob));\n\n   upgrade_to_lifetime_member( nathan );\n\n   // create a UIA\n   const asset_id_type uia_id = create_user_issued_asset( \"NATHANCOIN\", nathan, white_list ).id;\n   // Make a whitelist authority\n   {\n      BOOST_TEST_MESSAGE( \"Changing the whitelist authority\" );\n      asset_update_operation uop;\n      uop.issuer = nathan_id;\n      uop.asset_to_update = uia_id;\n      uop.new_options = uia_id(db).options;\n      uop.new_options.blacklist_authorities.insert(nathan_id);\n      trx.operations.push_back(uop);\n      PUSH_TX( db, trx, ~0 );\n      trx.operations.clear();\n   }\n\n\n   int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n   fund( alice, graphene::chain::asset(init_balance) );\n   fund( bob, graphene::chain::asset(init_balance) );\n\n   advance_past_htlc_first_hardfork(this);\n\n   // blacklist bob\n   {\n      graphene::chain::account_whitelist_operation op;\n      op.authorizing_account = nathan_id;\n      op.account_to_list = bob_id;\n      op.new_listing = graphene::chain::account_whitelist_operation::account_listing::black_listed;\n      op.fee = db.current_fee_schedule().calculate_fee( op );\n      trx.operations.push_back( op );\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n      generate_block();\n   }\n\n   issue_uia( alice_id, asset( init_balance, uia_id ) );\n\n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(preimage_size);\n   generate_random_preimage(preimage_size, pre_image);\n\n   // Alice attempts to put a contract on the blockchain\n   {\n      graphene::chain::htlc_create_operation create_operation;\n\n      create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION, uia_id );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 86400;\n      create_operation.preimage_hash = hash_it<fc::sha1>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n      trx.operations.push_back( create_operation );\n      sign(trx, alice_private_key);\n      // bob cannot accept it, so it fails\n      GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );\n      trx.clear();\n   }\n\n   // unblacklist Bob\n   {\n      graphene::chain::account_whitelist_operation op;\n      op.authorizing_account = nathan_id;\n      op.account_to_list = bob_id;\n      op.new_listing = graphene::chain::account_whitelist_operation::account_listing::no_listing;\n      op.fee = db.current_fee_schedule().calculate_fee( op );\n      trx.operations.push_back( op );\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n      generate_block();\n   }\n\n   graphene::chain::htlc_id_type alice_htlc_id;\n\n   // Alice again attempts to put a contract on the blockchain\n   {\n      graphene::chain::htlc_create_operation create_operation;\n\n      create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION, uia_id );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 86400;\n      create_operation.preimage_hash = hash_it<fc::sha1>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation );\n      trx.operations.push_back( create_operation );\n      sign(trx, alice_private_key);\n      // bob can now accept it, so it works\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n      graphene::chain::signed_block blk = generate_block();\n      processed_transaction alice_trx = blk.transactions[0];\n      alice_htlc_id = alice_trx.operation_results[0].get<object_id_type>();\n   }\n\n   // blacklist bob\n   {\n      graphene::chain::account_whitelist_operation op;\n      op.authorizing_account = nathan_id;\n      op.account_to_list = bob_id;\n      op.new_listing = graphene::chain::account_whitelist_operation::account_listing::black_listed;\n      op.fee = db.current_fee_schedule().calculate_fee( op );\n      trx.operations.push_back( op );\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n      generate_block();\n   }\n\n   // bob can redeem even though he's blacklisted\n   {\n      graphene::chain::htlc_redeem_operation update_operation;\n      update_operation.redeemer = bob_id;\n      update_operation.htlc_id = alice_htlc_id;\n      update_operation.preimage = pre_image;\n      update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation );\n      trx.operations.push_back( update_operation );\n      sign(trx, bob_private_key);\n      PUSH_TX( db, trx, ~0 );\n      generate_block();\n      trx.clear();\n   }\n\n} FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(htlc_database_api) {\ntry {\n\n   ACTORS((alice)(bob)(carl)(dan));\n\n   int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION);\n\n   transfer( committee_account, alice_id, graphene::chain::asset(init_balance) );\n\n   generate_blocks(HARDFORK_CORE_1468_TIME);\n   set_expiration( db, trx );\n\n   set_htlc_committee_parameters();\n\n   uint16_t preimage_size = 256;\n   std::vector<char> pre_image(256);\n   std::independent_bits_engine<std::default_random_engine, sizeof(unsigned), unsigned int> rbe;\n   std::generate(begin(pre_image), end(pre_image), std::ref(rbe));\n   graphene::chain::htlc_id_type alice_htlc_id_bob;\n   graphene::chain::htlc_id_type alice_htlc_id_carl;\n   graphene::chain::htlc_id_type alice_htlc_id_dan;\n\n   generate_block();\n   set_expiration( db, trx );\n   trx.clear();\n   // alice puts a htlc contract to bob\n   {\n      graphene::chain::htlc_create_operation create_operation;\n      BOOST_TEST_MESSAGE(\"Alice, who has 100 coins, is transferring 3 coins to Bob\");\n      create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = bob_id;\n      create_operation.claim_period_seconds = 60;\n      create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n      set_expiration( db, trx );\n      graphene::chain::signed_block blk = generate_block();\n      processed_transaction alice_trx = blk.transactions[0];\n      alice_htlc_id_bob = alice_trx.operation_results[0].get<object_id_type>();\n      generate_block();\n      set_expiration( db, trx );\n   }\n\n   trx.clear();\n   // alice puts a htlc contract to carl\n   {\n      graphene::chain::htlc_create_operation create_operation;\n      BOOST_TEST_MESSAGE(\"Alice, who has 100 coins, is transferring 3 coins to Carl\");\n      create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = carl_id;\n      create_operation.claim_period_seconds = 60;\n      create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n      set_expiration( db, trx );\n      graphene::chain::signed_block blk = generate_block();\n      processed_transaction alice_trx = blk.transactions[0];\n      alice_htlc_id_carl = alice_trx.operation_results[0].get<object_id_type>();\n      generate_block();\n      set_expiration( db, trx );\n   }\n\n   trx.clear();\n   // alice puts a htlc contract to dan\n   {\n      graphene::chain::htlc_create_operation create_operation;\n      BOOST_TEST_MESSAGE(\"Alice, who has 100 coins, is transferring 3 coins to Dan\");\n      create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION );\n      create_operation.to = dan_id;\n      create_operation.claim_period_seconds = 60;\n      create_operation.preimage_hash = hash_it<fc::sha256>( pre_image );\n      create_operation.preimage_size = preimage_size;\n      create_operation.from = alice_id;\n      create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation);\n      trx.operations.push_back(create_operation);\n      sign(trx, alice_private_key);\n      PUSH_TX(db, trx, ~0);\n      trx.clear();\n      set_expiration( db, trx );\n      graphene::chain::signed_block blk = generate_block();\n      processed_transaction alice_trx = blk.transactions[0];\n      alice_htlc_id_dan = alice_trx.operation_results[0].get<object_id_type>();\n      generate_block();\n      set_expiration( db, trx );\n   }\n\n   graphene::app::database_api db_api(db, &(this->app.get_options()) ) ;\n\n   auto htlc = db_api.get_htlc(alice_htlc_id_bob);\n   BOOST_CHECK_EQUAL( htlc->id.instance(), 0);\n   BOOST_CHECK_EQUAL( htlc->transfer.from.instance.value, 16 );\n   BOOST_CHECK_EQUAL( htlc->transfer.to.instance.value, 17 );\n\n   htlc = db_api.get_htlc(alice_htlc_id_carl);\n   BOOST_CHECK_EQUAL( htlc->id.instance(), 1);\n   BOOST_CHECK_EQUAL( htlc->transfer.from.instance.value, 16 );\n   BOOST_CHECK_EQUAL( htlc->transfer.to.instance.value, 18 );\n\n   htlc = db_api.get_htlc(alice_htlc_id_dan);\n   BOOST_CHECK_EQUAL( htlc->id.instance(), 2);\n   BOOST_CHECK_EQUAL( htlc->transfer.from.instance.value, 16 );\n   BOOST_CHECK_EQUAL( htlc->transfer.to.instance.value, 19 );\n\n   auto htlcs_alice = db_api.get_htlc_by_from(alice.name, graphene::chain::htlc_id_type(0), 100);\n   BOOST_CHECK_EQUAL( htlcs_alice.size(), 3 );\n   BOOST_CHECK_EQUAL( htlcs_alice[0].id.instance(), 0 );\n   BOOST_CHECK_EQUAL( htlcs_alice[1].id.instance(), 1 );\n   BOOST_CHECK_EQUAL( htlcs_alice[2].id.instance(), 2 );\n\n   htlcs_alice = db_api.get_htlc_by_from(alice.name, graphene::chain::htlc_id_type(1), 1);\n   BOOST_CHECK_EQUAL( htlcs_alice.size(), 1 );\n   BOOST_CHECK_EQUAL( htlcs_alice[0].id.instance(), 1 );\n\n   htlcs_alice = db_api.get_htlc_by_from(alice.name, graphene::chain::htlc_id_type(1), 2);\n   BOOST_CHECK_EQUAL( htlcs_alice.size(), 2 );\n   BOOST_CHECK_EQUAL( htlcs_alice[0].id.instance(), 1 );\n   BOOST_CHECK_EQUAL( htlcs_alice[1].id.instance(), 2 );\n\n   auto htlcs_bob = db_api.get_htlc_by_to(bob.name, graphene::chain::htlc_id_type(0), 100);\n   BOOST_CHECK_EQUAL( htlcs_bob.size(), 1 );\n   BOOST_CHECK_EQUAL( htlcs_bob[0].id.instance(), 0 );\n\n   auto htlcs_carl = db_api.get_htlc_by_to(carl.name, graphene::chain::htlc_id_type(0), 100);\n   BOOST_CHECK_EQUAL( htlcs_carl.size(), 1 );\n   BOOST_CHECK_EQUAL( htlcs_carl[0].id.instance(), 1 );\n\n   auto htlcs_dan = db_api.get_htlc_by_to(dan.name, graphene::chain::htlc_id_type(0), 100);\n   BOOST_CHECK_EQUAL( htlcs_dan.size(), 1 );\n   BOOST_CHECK_EQUAL( htlcs_dan[0].id.instance(), 2 );\n\n   auto full = db_api.get_full_accounts({alice.name}, false);\n   BOOST_CHECK_EQUAL( full[alice.name].htlcs_from.size(), 3 );\n\n   full = db_api.get_full_accounts({bob.name}, false);\n   BOOST_CHECK_EQUAL( full[bob.name].htlcs_to.size(), 1 );\n\n   auto list = db_api.list_htlcs(graphene::chain::htlc_id_type(0), 1);\n   BOOST_CHECK_EQUAL( list.size(), 1 );\n   BOOST_CHECK_EQUAL( list[0].id.instance(), 0 );\n\n   list = db_api.list_htlcs(graphene::chain::htlc_id_type(1), 1);\n   BOOST_CHECK_EQUAL( list.size(), 1 );\n   BOOST_CHECK_EQUAL( list[0].id.instance(), 1 );\n\n   list = db_api.list_htlcs(graphene::chain::htlc_id_type(2), 1);\n   BOOST_CHECK_EQUAL( list.size(), 1 );\n   BOOST_CHECK_EQUAL( list[0].id.instance(), 2 );\n\n   list = db_api.list_htlcs(graphene::chain::htlc_id_type(1), 2);\n   BOOST_CHECK_EQUAL( list.size(), 2 );\n   BOOST_CHECK_EQUAL( list[0].id.instance(), 1 );\n   BOOST_CHECK_EQUAL( list[1].id.instance(), 2 );\n\n   list = db_api.list_htlcs(graphene::chain::htlc_id_type(1), 3);\n   BOOST_CHECK_EQUAL( list.size(), 2 );\n   BOOST_CHECK_EQUAL( list[0].id.instance(), 1 );\n   BOOST_CHECK_EQUAL( list[1].id.instance(), 2 );\n\n   list = db_api.list_htlcs(graphene::chain::htlc_id_type(0), 100);\n   BOOST_CHECK_EQUAL( list.size(), 3 );\n   BOOST_CHECK_EQUAL( list[0].id.instance(), 0 );\n   BOOST_CHECK_EQUAL( list[1].id.instance(), 1 );\n   BOOST_CHECK_EQUAL( list[2].id.instance(), 2 );\n\n   list = db_api.list_htlcs(graphene::chain::htlc_id_type(10), 100);\n   BOOST_CHECK_EQUAL( list.size(), 0 );\n\n} catch (fc::exception &e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/main.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#include <cstdlib>\n#include <iostream>\n#include <boost/test/included/unit_test.hpp>\n\nextern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;\n\nboost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {\n   std::srand(time(NULL));\n   std::cout << \"Random number generator seeded to \" << time(NULL) << std::endl;\n   const char* genesis_timestamp_str = getenv(\"GRAPHENE_TESTING_GENESIS_TIMESTAMP\");\n   if( genesis_timestamp_str != nullptr )\n   {\n      GRAPHENE_TESTING_GENESIS_TIMESTAMP = std::stoul( genesis_timestamp_str );\n   }\n   std::cout << \"GRAPHENE_TESTING_GENESIS_TIMESTAMP is \" << GRAPHENE_TESTING_GENESIS_TIMESTAMP << std::endl;\n   return nullptr;\n}\n"
  },
  {
    "path": "tests/tests/margin_call_fee_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 Contributors\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n// For account history\n#include <vector>\n#include <graphene/app/api.hpp>\n\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nstruct bitasset_database_fixture : database_fixture {\n   bitasset_database_fixture()\n           : database_fixture() {\n   }\n\n   const limit_order_create_operation\n   create_sell_operation(account_id_type user, const asset &amount, const asset &recv) {\n      const time_point_sec order_expiration = time_point_sec::maximum();\n      const price &fee_core_exchange_rate = price::unit_price();\n      limit_order_create_operation op = create_sell_operation(user, amount, recv, order_expiration,\n                                                              fee_core_exchange_rate);\n      return op;\n   }\n\n   const limit_order_create_operation\n   create_sell_operation(account_id_type user, const asset &amount, const asset &recv,\n                         const time_point_sec order_expiration,\n                         const price &fee_core_exchange_rate) {\n      limit_order_create_operation op = create_sell_operation(user(db), amount, recv, order_expiration,\n                                                              fee_core_exchange_rate);\n      return op;\n   }\n\n   const limit_order_create_operation\n   create_sell_operation(const account_object &user, const asset &amount, const asset &recv,\n                         const time_point_sec order_expiration,\n                         const price &fee_core_exchange_rate) {\n      limit_order_create_operation sell_order;\n      sell_order.seller = user.id;\n      sell_order.amount_to_sell = amount;\n      sell_order.min_to_receive = recv;\n      sell_order.expiration = order_expiration;\n\n      return sell_order;\n   }\n\n   const asset_create_operation create_user_issued_asset_operation(const string &name, const account_object &issuer,\n                                                                   uint16_t flags, const price &core_exchange_rate,\n                                                                   uint8_t precision, uint16_t maker_fee_percent,\n                                                                   uint16_t taker_fee_percent) {\n      asset_create_operation creator;\n      creator.issuer = issuer.id;\n      creator.fee = asset();\n      creator.symbol = name;\n      creator.common_options.max_supply = 0;\n      creator.precision = precision;\n\n      creator.common_options.core_exchange_rate = core_exchange_rate;\n      creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      creator.common_options.flags = flags;\n      creator.common_options.issuer_permissions = flags;\n      creator.common_options.market_fee_percent = maker_fee_percent;\n      creator.common_options.extensions.value.taker_fee_percent = taker_fee_percent;\n\n      return creator;\n\n   }\n};\n\n\nBOOST_FIXTURE_TEST_SUITE(margin_call_fee_tests, bitasset_database_fixture)\n\n   /**\n    * Test the effects of different MCFRs on derived prices and ratios\n    */\n   BOOST_AUTO_TEST_CASE(mcfr_tests) {\n      try {\n         ACTORS((charlie))\n         const asset_id_type core_id;\n\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n         //////\n         // Initialize\n         //////\n         fc::optional<uint16_t> mcfr;\n         price expected_offer_price; // The price offered by the margin call\n         price expected_paid_price; // The effective price paid by the margin call\n         ratio_type expected_margin_call_pays_ratio;\n\n         const asset_object core = core_id(db);\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT2\", charlie_id, smartbit_market_fee_percent, charge_market_fee, 2);\n         generate_block();\n         const asset_object smartbit2 = get_asset(\"SMARTBIT2\");\n         BOOST_CHECK_EQUAL(2, smartbit2.precision);\n\n         // Construct a price feed\n         // Initial price of 1 satoshi SMARTBIT2 for 20 satoshi CORE\n         // = 0.0001 SMARTBIT2 for 0.00020 CORE = 1 SMARTBIT2 for 2 CORE\n         const price initial_price =\n                 smartbit2.amount(1) / core.amount(20); // 1 satoshi SMARTBIT2 for 20 satoshi CORE\n\n         price_feed feed;\n         feed.settlement_price = initial_price;\n         feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         feed.maximum_short_squeeze_ratio = 1500; // MSSR of 1.50x\n\n         //////\n         // Check prices and ratios when MSSR = 150% and MCFR is not set\n         //////\n         mcfr = {};\n\n         // Expected paid price = price / MSSR\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1500 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.max_short_squeeze_price());\n\n         // Expected offer price = price / (MSSR-MCFR)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / ([1500 - 0] / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.margin_call_order_price(mcfr));\n\n         // Expected margin call pays ratio = (MSSR-MCFR) / MSSR\n         // = (1500 - 0) / 1500\n         // = 1\n         expected_margin_call_pays_ratio = ratio_type(1, 1);\n         BOOST_CHECK(expected_margin_call_pays_ratio == feed.margin_call_pays_ratio(mcfr));\n\n\n         //////\n         // Check prices and ratios when MSSR = 150% and MCFR = 0\n         //////\n         mcfr = 0;\n\n         // Expected paid price = price / MSSR\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1500 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.max_short_squeeze_price());\n\n         // Expected offer price = price / (MSSR-MCFR)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / ([1500 - 0] / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.margin_call_order_price(mcfr));\n\n         // Expected margin call pays ratio = (MSSR-MCFR) / MSSR\n         // = (1500 - 0) / 1500\n         // = 1\n         expected_margin_call_pays_ratio = ratio_type(1, 1);\n         BOOST_CHECK(expected_margin_call_pays_ratio == feed.margin_call_pays_ratio(mcfr));\n\n\n         //////\n         // Check prices and ratios when MSSR = 150% and MCFR = 5%\n         //////\n         mcfr = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n\n         // Expected paid price = price / MSSR\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1500 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.max_short_squeeze_price());\n\n         // Expected offer price = price / (MSSR-MCFR)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / ([1500 - 50] / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1450 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (145 / 100)\n         // = (1 satoshi SMARTBIT2 / 2 satoshi Core) / (145 / 10)\n         // = (10 satoshi SMARTBIT2 / 290 satoshi Core)\n         // = (1 satoshi SMARTBIT2 / 29 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(29));\n         BOOST_CHECK(expected_paid_price == feed.margin_call_order_price(mcfr));\n\n         // Expected margin call pays ratio = (MSSR-MCFR) / MSSR\n         // = (1500 - 50) / 1500\n         // = 1450 / 1500 = 145 / 150 = 29 / 30\n         expected_margin_call_pays_ratio = ratio_type(29, 30);\n         BOOST_CHECK(expected_margin_call_pays_ratio == feed.margin_call_pays_ratio(mcfr));\n\n\n         //////\n         // Check prices and ratios when MSSR = 150% and MCFR = 30%\n         //////\n         mcfr = 300; // 30% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n\n         // Expected paid price = price / MSSR\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1500 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.max_short_squeeze_price());\n\n         // Expected offer price = price / (MSSR-MCFR)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / ([1500 - 300] / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1200 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (6 / 5)\n         // = (5 satoshi SMARTBIT2 / 120 satoshi Core)\n         // = (1 satoshi SMARTBIT2 / 24 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(24));\n         BOOST_CHECK(expected_paid_price == feed.margin_call_order_price(mcfr));\n\n         // Expected margin call pays ratio = (MSSR-MCFR) / MSSR\n         // = (1500 - 300) / 1500\n         // = 1200 / 1500 = 4 / 5\n         expected_margin_call_pays_ratio = ratio_type(4, 5);\n         BOOST_CHECK(expected_margin_call_pays_ratio == feed.margin_call_pays_ratio(mcfr));\n\n\n         //////\n         // Check prices and ratios when MSSR = 150% and MCFR = 60%\n         //////\n         mcfr = 600; // 60% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n\n         // Expected paid price = price / MSSR\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (1500 / 1000)\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core) / (3 / 2)\n         // = (1 satoshi SMARTBIT2 / 30 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(30));\n         BOOST_CHECK(expected_paid_price == feed.max_short_squeeze_price());\n\n         // Expected offer price = price / (MSSR-MCFR)\n         // but (MSSR-MCFR) has a floor 1\n         // Therefore = price / 1 = price\n         // = (1 satoshi SMARTBIT2 / 20 satoshi Core)\n         expected_paid_price = price(smartbit2.amount(1), core.amount(20));\n         BOOST_CHECK(expected_paid_price == feed.margin_call_order_price(mcfr));\n\n         // Expected margin call pays ratio = (MSSR-MCFR) / MSSR\n         // but (MSSR-MCFR) has a floor 1\n         // Therefore = 1 / MSSR\n         // = 1000 / 1500 = 2 / 3\n         expected_margin_call_pays_ratio = ratio_type(2, 3);\n         BOOST_CHECK(expected_margin_call_pays_ratio == feed.margin_call_pays_ratio(mcfr));\n\n      }\n      FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test a simple scenario of a Complete Fill of a Call Order as a Maker after HF\n    *\n    * 0. Advance to HF\n    * 1. Initialize actors and a smart asset called SMARTBIT\n    * 2. Publish feed\n    * 3. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n    *     Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n    *     because his debt position is what will be tracked.\n    * 4. The feed price is updated to indicate that the collateral drops enough to trigger a margin call\n    *    **but not enough** to trigger a global settlement.\n    *    Bob's activated margin call cannot be matched against any existing limit order's price.\n    * 5. (Order 2: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT at a price\n    *    that will overlap with Bob's \"activated\" call order / margin call.\n    *    **Bob should be charged as a maker, and Alice as a taker.**\n    *    Alice's limit order should be (partially or completely) filled, but Bob's order should be completely filled,\n    *    and the debt position should be closed.\n    */\n   BOOST_AUTO_TEST_CASE(complete_fill_of_call_order_as_maker) {\n      try {\n         //////\n         // 0. Advance to activate hardfork\n         //////\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n\n         //////\n         // 1. Initialize actors and a smart asset called SMARTBIT\n         //////\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         // CORE asset exists by default\n         const asset_object &core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n         const int64_t CORE_UNIT = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n\n         // Create the SMARTBIT asset\n         const int16_t SMARTBIT_UNIT = 10000; // 10000 satoshi SMARTBIT in 1 SMARTBIT\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const uint16_t smartbit_margin_call_fee_ratio = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         // Define the margin call fee ratio\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent, charge_market_fee, 4, core_id,\n                         GRAPHENE_MAX_SHARE_SUPPLY, {}, smartbit_margin_call_fee_ratio);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object smartbit = get_asset(\"SMARTBIT\");\n         const asset_id_type smartbit_id = smartbit.id;\n         update_feed_producers(smartbit, {feedproducer_id});\n\n         // Initialize token balance of actors\n         // Alice should start with 5,000,000 CORE\n         const asset alice_initial_core = asset(5000000 * CORE_UNIT);\n         transfer(committee_account, alice_id, alice_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id, core_id), alice_initial_core.amount.value);\n\n         // Bob should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 2x\n         const price initial_feed_price =\n                 smartbit.amount(1) / core.amount(20); // 1 satoshi SMARTBIT for 20 satoshi CORE\n         const asset bob_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset bob_initial_core = core.amount(\n                 2 * (bob_initial_smart * initial_feed_price).amount); // 80,000,000 satoshi CORE\n         transfer(committee_account, bob_id, bob_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, core), 80000000);\n\n\n         //////\n         // 2. Publish feed\n         //////\n         price_feed current_feed;\n         current_feed.settlement_price = initial_feed_price;\n         current_feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         current_feed.maximum_short_squeeze_ratio = 1500; // MSSR of 1.50x\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // 3. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n         //    Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n         //    because his debt position is what will be tracked.\n         //////\n         call_order_id_type bob_call_id = (*borrow(bob, bob_initial_smart, bob_initial_core)).id;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price bob_initial_cr = bob_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(bob_initial_cr.base.amount.value, 80000000); // Collateral of 80,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(bob_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n\n         //////\n         // 4. The feed price is updated to indicate that the collateral drops enough to trigger a margin call\n         //    **but not enough** to trigger a global settlement.\n         //    Bob's activated margin call cannot be matched against any existing limit order's price.\n         //////\n         // Adjust the price such that the initial CR of Bob's position (CR_0) drops to 1.7x = (17/10)x\n         // Want new price = 1.7 / CR_0 = (17/10) / CR_0\n         //\n         // Collateral ratios are defined as collateral / debt\n         // BitShares prices are conventionally defined as debt / collateral\n         // The new price can be expressed with the available codebase as\n         // = (17/10) * ~CR_0 = ~CR_0 * (17/10)\n         const price intermediate_feed_price = ~bob_initial_cr * ratio_type(17, 10); // Units of debt / collateral\n         // Reduces to (2000000 * 17) / (80000000 * 10) = (17) / (40 * 10) = 17 / 400\n         BOOST_CHECK(intermediate_feed_price < initial_feed_price);\n         BOOST_CHECK_EQUAL(intermediate_feed_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(intermediate_feed_price.quote.amount.value, 400); // satoshi CORE\n\n         current_feed.settlement_price = intermediate_feed_price;\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n\n         BOOST_CHECK(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n\n         // Check Bob's debt to the blockchain\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value, bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value, bob_initial_core.amount.value);\n\n         // Check Bob's balances\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 0);\n\n\n\n         //////\n         // 5. (Order 2: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT at a price\n         //    that will overlap with Bob's \"activated\" call order / margin call.\n         //    **Bob should be charged as a maker, and Alice as a taker.**\n         //    Alice's limit order should be (partially or completely) filled, but Bob's order should be completely filled,\n         //    and the debt position should be closed.\n         //////\n         // Alice obtains her SMARTBIT from Bob\n         transfer(bob_id, alice_id, bob_initial_smart);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), bob_initial_smart.amount.value);\n\n         // The margin call should be priced at settlement_price / (MSSR-MCFR)\n         // where settlement_price is expressed as debt / collateral\n         // Create a \"large\" sell order at a \"high\" price of settlement_price * 1.1 = settlement_price * (11/10)\n         const price alice_order_price_implied = intermediate_feed_price * ratio_type(11, 10);\n\n\n         const asset alice_debt_to_sell = smartbit.amount(get_balance(alice_id(db), smartbit_id(db)));\n         // multiply_and_round_up() handles inverting the price so that the output is in correct collateral units\n         const asset alice_collateral_to_buy = alice_debt_to_sell.multiply_and_round_up(alice_order_price_implied);\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice_id, alice_debt_to_sell,\n                                                                            alice_collateral_to_buy);\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         // asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Margin call should exchange all of the available debt (X) for X*(MSSR-MCFR)/settlement_price\n         // The match price should be the settlement_price/(MSSR-MCFR) = settlement_price/(MSSR-MCFR)\n         const uint16_t ratio_numerator = current_feed.maximum_short_squeeze_ratio - smartbit_margin_call_fee_ratio;\n         BOOST_REQUIRE_EQUAL(ratio_numerator,\n                             1450); // GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO - smartbit_margin_call_fee_ratio\n         const price expected_match_price = intermediate_feed_price * ratio_type(GRAPHENE_COLLATERAL_RATIO_DENOM,\n                                                                           ratio_numerator);\n         // Reduces to (17 satoshi SMARTBIT / 400 satoshi CORE) * (1000 / 1450)\n         // = (17 satoshi SMARTBIT / 400 satoshi CORE) * (100 / 145)\n         // = (17 satoshi SMARTBIT / 4 satoshi CORE) * (1 / 145)\n         // = 17 satoshi SMARTBIT / 580 satoshi CORE\n         BOOST_CHECK_EQUAL(expected_match_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(expected_match_price.quote.amount.value, 580); // satoshi CORE\n\n         // Payment to limit order = X*(MSSR-MCFR)/settlement_price\n         // = 2000000 satoshi SMARTBIT * (580 satoshi CORE / 17 satoshi SMARTBIT)\n         // = 68235294.1176 satoshi CORE rounded up to 68235295 satoshi CORE = 682.35295 CORE\n         const asset expected_payment_to_alice_core = core.amount(68235295);\n\n         // Expected payment by call order: filled_debt * (MSSR / settlement_price) = filled_debt * (MSSR / settlement_price)\n         //\n         // (MSSR / settlement_price) = (1500 / 1000) / (17 satoshi SMARTBIT / 400 satoshi CORE)\n         // = (15 / 10) / (17 satoshi SMARTBIT / 400 satoshi CORE)\n         // = (15 / 1) / (17 satoshi SMARTBIT / 40 satoshi CORE)\n         // = (15 * 40 satoshi CORE) / (17 satoshi SMARTBIT)\n         // = (15 * 40 satoshi CORE) / (17 satoshi SMARTBIT)\n         // = 600 satoshi CORE / 17 satoshi SMARTBIT\n         //\n         // Expected payment by call order = 2000000 satoshi SMARTBIT * (600 satoshi CORE / 17 satoshi SMARTBIT)\n         // = 2000000 * 600 satoshi CORE / 17\n         // = 70588235.2941 satoshi CORE rounding up to 70588236 satoshi CORE = 705.88236 CORE\n         const asset expected_payment_from_bob_core = core.amount(70588236);\n\n         // Expected fee = payment by call order - payment to limit order\n         // fee = (70588236 - 68235295) satoshi CORE = 2352941 satoshi CORE = 23.52941 CORE\n         const asset expected_margin_call_fee =\n                 expected_payment_from_bob_core - expected_payment_to_alice_core; // core.amount(2352941);\n\n         // Check Alice's balances\n         BOOST_CHECK_EQUAL(get_balance(alice, smartbit), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice, core),\n                           alice_initial_core.amount.value + expected_payment_to_alice_core.amount.value);\n\n         // Check Alice's limit order is closed\n         BOOST_CHECK(!db.find(alice_order_id));\n\n         // Check Bob's debt position is closed\n         BOOST_CHECK(!db.find(bob_call_id));\n\n         // Check Bob's balances\n         // Bob should have no debt asset\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0);\n         // Bob should have collected the balance of his collateral after the margin call\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)),\n                           bob_initial_core.amount.value - expected_payment_from_bob_core.amount.value);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees.value,\n                           expected_margin_call_fee.amount.value);\n\n         // Check the fee of the fill operations for Alice and Bob\n         generate_block(); // To trigger db_notify() and record pending operations into histories\n         graphene::app::history_api hist_api(app);\n         vector <operation_history_object> histories;\n         const int fill_order_op_id = operation::tag<fill_order_operation>::value;\n\n         // Check Alice's history\n         histories = hist_api.get_account_history_operations(\n                 \"alice\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Alice's fill order for her limit order should have zero fee\n         fill_order_operation alice_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(alice_fill_op.fee == asset(0));\n         // Alice's fill order's fill price should equal the expected match price\n         BOOST_CHECK(~alice_fill_op.fill_price == expected_match_price);\n\n         // Check Bob's history\n         histories = hist_api.get_account_history_operations(\n                 \"bob\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Bob's fill order for his margin call should have a fee equal to the margin call fee\n         fill_order_operation bob_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(bob_fill_op.fee == expected_margin_call_fee);\n         // Bob's fill order's fill price should equal the expected match price\n         BOOST_CHECK(~bob_fill_op.fill_price == expected_match_price);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test a scenario of a Complete Fill of a Call Order as a Maker after HF\n    * that evaluates the price ranges of matchable limit orders.\n    * Before BSIP74, taker limit orders must be priced >= settlement_price/MSSR\n    * After BSIP74, taker limit orders must priced >= settlement_price/(MSSR-MCFR)\n    *\n    * 0. Advance to HF\n    * 1. Initialize actors and a smart asset called SMARTBIT\n    * 2. Publish feed\n    * 3. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n    *     Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n    *     because his debt position is what will be tracked.\n    * 4. The feed price is updated to indicate that the collateral drops enough to trigger a margin call\n    *    **but not enough** to trigger a global settlement.\n    *    Bob's activated margin call cannot be matched against any existing limit order's price.\n    * 5. (Order 2: Limit order) Charlie places a **\"large\"** limit order to sell SMARTBIT at a price\n    *    that should NOT overlap with Bob's \"activated\" call order / margin call but would have before BSIP74.\n    *    **Bob's margin call should not be affected.\n    * 6. (Order 3: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT at a price\n    *    that will overlap with Bob's \"activated\" call order / margin call.\n    *    **Bob should be charged as a maker, and Alice as a taker.**\n    *    Alice's limit order should be (partially or completely) filled, but Bob's order should be completely filled,\n    *    and the debt position should be closed.\n    *\n    * Summary: The offer price of the taker limit order affects whether it matches the margin call order.\n    *          The offer price of the taker limit order DOES NOT affect the filling.\n    *          Filling of a maker margin call / taker limit order is based on the the call order's match price.\n    */\n   BOOST_AUTO_TEST_CASE(complete_fill_of_call_order_as_maker_2) {\n      try {\n         //////\n         // 0. Advance to activate hardfork\n         //////\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n\n         //////\n         // 1. Initialize actors and a smart asset called SMARTBIT\n         //////\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob)(charlie));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         // CORE asset exists by default\n         const asset_object &core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n         const int64_t CORE_UNIT = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n\n         // Create the SMARTBIT asset\n         const int16_t SMARTBIT_UNIT = 10000; // 10000 satoshi SMARTBIT in 1 SMARTBIT\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const uint16_t smartbit_margin_call_fee_ratio = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         // Define the margin call fee ratio\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent, charge_market_fee, 4, core_id,\n                         GRAPHENE_MAX_SHARE_SUPPLY, {}, smartbit_margin_call_fee_ratio);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object smartbit = get_asset(\"SMARTBIT\");\n         const asset_id_type smartbit_id = smartbit.id;\n         update_feed_producers(smartbit, {feedproducer_id});\n\n         // Initialize token balance of actors\n         // Alice should start with 5,000,000 CORE\n         const asset alice_initial_core = asset(5000000 * CORE_UNIT);\n         transfer(committee_account, alice_id, alice_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id, core_id), alice_initial_core.amount.value);\n\n         // Bob should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 2x\n         const price initial_feed_price =\n                 smartbit.amount(1) / core.amount(20); // 1 satoshi SMARTBIT for 20 satoshi CORE\n         const asset bob_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset bob_initial_core = core.amount(\n                 2 * (bob_initial_smart * initial_feed_price).amount); // 80,000,000 satoshi CORE\n         transfer(committee_account, bob_id, bob_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, core), 80000000);\n\n         // Charlie should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 3x\n         const asset charlie_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset charlie_initial_core = core.amount(\n                 3 * (bob_initial_smart * initial_feed_price).amount); // 120,000,000 satoshi CORE\n         transfer(committee_account, charlie_id, charlie_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, core), 120000000);\n\n\n         //////\n         // 2. Publish feed\n         //////\n         price_feed current_feed;\n         current_feed.settlement_price = initial_feed_price;\n         current_feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         current_feed.maximum_short_squeeze_ratio = 1500; // MSSR of 1.50x\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // 3. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n         //    Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n         //    because his debt position is what will be tracked.\n         //////\n         call_order_id_type bob_call_id = (*borrow(bob, bob_initial_smart, bob_initial_core)).id;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price bob_initial_cr = bob_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(bob_initial_cr.base.amount.value, 80000000); // Collateral of 80,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(bob_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n\n         //////\n         // 4. The feed price is updated to indicate that the collateral drops enough to trigger a margin call\n         //    **but not enough** to trigger a global settlement.\n         //    Bob's activated margin call cannot be matched against any existing limit order's price.\n         //////\n         // Adjust the price such that the initial CR of Bob's position (CR_0) drops to 1.7x = (17/10)x\n         // Want new price = 1.7 / CR_0 = (17/10) / CR_0\n         //\n         // Collateral ratios are defined as collateral / debt\n         // BitShares prices are conventionally defined as debt / collateral\n         // The new price can be expressed with the available codebase as\n         // = (17/10) * ~CR_0 = ~CR_0 * (17/10)\n         const price intermediate_feed_price = ~bob_initial_cr * ratio_type(17, 10); // Units of debt / collateral\n         // Reduces to (2000000 * 17) / (80000000 * 10) = (17) / (40 * 10) = 17 / 400\n         BOOST_CHECK(intermediate_feed_price < initial_feed_price);\n         BOOST_CHECK_EQUAL(intermediate_feed_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(intermediate_feed_price.quote.amount.value, 400); // satoshi CORE\n\n         current_feed.settlement_price = intermediate_feed_price;\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n\n         BOOST_CHECK(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n\n         // Check Bob's debt to the blockchain\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value, bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value, bob_initial_core.amount.value);\n\n         // Check Bob's balances\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 0);\n\n\n         //////\n         // 5. (Order 2: Limit order) Charlie places a **\"large\"** limit order to sell SMARTBIT at a price\n         //    that SHOULD NOT overlap with Bob's \"activated\" call order / margin call but would have before BSIP74.\n         //    **Bob's margin call SHOULD NOT be affected.**\n         //////\n         // Charlie obtains his SMARTBIT by borrowing it from the blockchain\n         call_order_id_type charlie_call_id = (*borrow(charlie, charlie_initial_smart, charlie_initial_core)).id;\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, smartbit), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price charlie_initial_cr = charlie_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(charlie_initial_cr.base.amount.value, 120000000); // Collateral of 120,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(charlie_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n         // Check Charlie's liquid balance\n         BOOST_CHECK_EQUAL(get_balance(charlie_id(db), core_id(db)), 0);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id(db), smartbit_id(db)), charlie_initial_smart.amount.value);\n\n         // The margin call match price should be the settlement_price/(MSSR-MCFR) = settlement_price/(MSSR-MCFR)\n         const uint16_t ratio_numerator = current_feed.maximum_short_squeeze_ratio - smartbit_margin_call_fee_ratio;\n         BOOST_REQUIRE_EQUAL(ratio_numerator,\n                             1450); // GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO - smartbit_margin_call_fee_ratio\n         const price expected_match_price = intermediate_feed_price * ratio_type(GRAPHENE_COLLATERAL_RATIO_DENOM,\n                                                                                 ratio_numerator);\n         // Reduces to (17 satoshi SMARTBIT / 400 satoshi CORE) * (1000 / 1450)\n         // = (17 satoshi SMARTBIT / 400 satoshi CORE) * (100 / 145)\n         // = (17 satoshi SMARTBIT / 4 satoshi CORE) * (1 / 145)\n         // = 17 satoshi SMARTBIT / 580 satoshi CORE\n         BOOST_CHECK_EQUAL(expected_match_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(expected_match_price.quote.amount.value, 580); // satoshi CORE\n\n         // Charlie create a \"large\" sell order SLIGHTLY BELOW the match_price\n         // This price should ensure that the order is NOT matched against Bob's margin call\n         // The margin call should be priced at settlement_price / (MSSR-MCFR)\n         // where settlement_price is expressed as debt / collateral\n         const price charlie_order_price = price(smartbit.amount(17), core.amount(580));\n         BOOST_CHECK(charlie_order_price == expected_match_price); // Exactly at the edge\n\n         const asset charlie_debt_to_sell = smartbit.amount(get_balance(charlie_id(db), smartbit_id(db)));\n         // multiply_and_round_up() handles inverting the price so that the output is in correct collateral units\n         const asset charlie_collateral_to_buy = charlie_debt_to_sell.multiply_and_round_up(charlie_order_price);\n         limit_order_create_operation charlie_sell_op = create_sell_operation(charlie_id, charlie_debt_to_sell,\n                                                                            charlie_collateral_to_buy);\n         // The limit order's price should be slightly below the expected match price\n         // due to multiply_and_round_up() which increases the collateral\n         // thereby decreasing the ratio of debt / collateral\n         BOOST_CHECK(charlie_sell_op.get_price() < expected_match_price);\n\n         trx.clear();\n         trx.operations.push_back(charlie_sell_op);\n         // asset charlie_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, charlie_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type charlie_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check Charlies's limit order is still open\n         BOOST_CHECK(db.find(charlie_order_id));\n\n         // Check Charlies's limit order is NOT CHANGED\n         const limit_order_object charlie_limit_order = charlie_order_id(db);\n         BOOST_CHECK(charlie_limit_order.amount_for_sale() == charlie_debt_to_sell);\n         BOOST_CHECK(charlie_limit_order.amount_to_receive() == charlie_collateral_to_buy);\n\n         // Check Bob's debt position is still open\n         BOOST_CHECK(db.find(bob_call_id));\n\n         // Check Bob's debt to the blockchain is NOT CHANGED\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value, bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value, bob_initial_core.amount.value);\n\n\n         //////\n         // 6. (Order 2: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT at a price\n         //    that will overlap with Bob's \"activated\" call order / margin call.\n         //    **Bob should be charged as a maker, and Alice as a taker.**\n         //    Alice's limit order should be (partially or completely) filled,\n         //    but Bob's order should be completely filled,\n         //    and the debt position should be closed.\n         //////\n         // Alice obtains her SMARTBIT from Bob\n         transfer(bob_id, alice_id, bob_initial_smart);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), bob_initial_smart.amount.value);\n\n         // The margin call should be priced at settlement_price / (MSSR-MCFR)\n         // where settlement_price is expressed as debt / collateral\n         // Create a \"large\" sell order at JUST above the expected match price\n         const price alice_order_price_implied = intermediate_feed_price * ratio_type(11, 10);\n         const price alice_order_price = price(smartbit.amount(17 + 1), core.amount(580)); // Barely matching\n         // const price alice_order_price = price(smartbit.amount(17), core.amount(580 - 1)); // Barely matching\n         BOOST_CHECK(alice_order_price > expected_match_price);\n\n         const asset alice_debt_to_sell = smartbit.amount(get_balance(alice_id(db), smartbit_id(db)));\n         // multiply_and_round_up() handles inverting the price so that the output is in correct collateral units\n         const asset alice_collateral_to_buy = alice_debt_to_sell.multiply_and_round_up(alice_order_price_implied);\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice_id, alice_debt_to_sell,\n                                                                            alice_collateral_to_buy);\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         // asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Margin call should exchange all of the available debt (X) for X*(MSSR-MCFR)/settlement_price\n         // Payment to limit order = X*(MSSR-MCFR)/settlement_price\n         // = 2000000 satoshi SMARTBIT * (580 satoshi CORE / 17 satoshi SMARTBIT)\n         // = 68235294.1176 satoshi CORE rounded up to 68235295 satoshi CORE = 682.35295 CORE\n         const asset expected_payment_to_alice_core = core.amount(68235295);\n\n         // Expected payment by call order: filled_debt * (MSSR / settlement_price) = filled_debt * (MSSR / settlement_price)\n         //\n         // (MSSR / settlement_price) = (1500 / 1000) / (17 satoshi SMARTBIT / 400 satoshi CORE)\n         // = (15 / 10) / (17 satoshi SMARTBIT / 400 satoshi CORE)\n         // = (15 / 1) / (17 satoshi SMARTBIT / 40 satoshi CORE)\n         // = (15 * 40 satoshi CORE) / (17 satoshi SMARTBIT)\n         // = (15 * 40 satoshi CORE) / (17 satoshi SMARTBIT)\n         // = 600 satoshi CORE / 17 satoshi SMARTBIT\n         //\n         // Expected payment by call order = 2000000 satoshi SMARTBIT * (600 satoshi CORE / 17 satoshi SMARTBIT)\n         // = 2000000 * 600 satoshi CORE / 17\n         // = 70588235.2941 satoshi CORE rounding up to 70588236 satoshi CORE = 705.88236 CORE\n         const asset expected_payment_from_bob_core = core.amount(70588236);\n\n         // Expected fee = payment by call order - payment to limit order\n         // fee = (70588236 - 68235295) satoshi CORE = 2352941 satoshi CORE = 23.52941 CORE\n         const asset expected_margin_call_fee =\n                 expected_payment_from_bob_core - expected_payment_to_alice_core; // core.amount(2352941);\n\n         // Check Alice's balances\n         BOOST_CHECK_EQUAL(get_balance(alice, smartbit), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice, core),\n                           alice_initial_core.amount.value + expected_payment_to_alice_core.amount.value);\n\n         // Check Alice's limit order is close\n         BOOST_CHECK(!db.find(alice_order_id));\n\n         // Check Bob's debt position is closed\n         BOOST_CHECK(!db.find(bob_call_id));\n\n         // Check Bob's balances\n         // Bob should have no debt asset\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0);\n         // Bob should have collected the balance of his collateral after the margin call\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)),\n                           bob_initial_core.amount.value - expected_payment_from_bob_core.amount.value);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees.value,\n                           expected_margin_call_fee.amount.value);\n\n         // Check the fee of the fill operations for Alice and Bob\n         generate_block(); // To trigger db_notify() and record pending operations into histories\n         graphene::app::history_api hist_api(app);\n         vector <operation_history_object> histories;\n         const int fill_order_op_id = operation::tag<fill_order_operation>::value;\n\n         // Check Alice's history\n         histories = hist_api.get_account_history_operations(\n                 \"alice\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Alice's fill order for her limit order should have zero fee\n         fill_order_operation alice_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(alice_fill_op.fee == asset(0));\n         // Alice's fill order's fill price should equal the expected match price\n         BOOST_CHECK(~alice_fill_op.fill_price == expected_match_price);\n\n         // Check Bob's history\n         histories = hist_api.get_account_history_operations(\n                 \"bob\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Bob's fill order for his margin call should have a fee equal to the margin call fee\n         fill_order_operation bob_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(bob_fill_op.fee == expected_margin_call_fee);\n         // Bob's fill order's fill price should equal the expected match price\n         BOOST_CHECK(~bob_fill_op.fill_price == expected_match_price);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test a scenario of a partial Filling of a Call Order as a Maker after HF\n    * where the partial filling is due to call order defining a target collateral ratio (TCR) (BSIP38)\n    *\n    * 0. Advance to HF\n    * 1. Initialize actors and a smart asset called SMARTBIT\n    * 2. Publish feed\n    * 3. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n    *     Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n    *     because his debt position is what will be tracked.\n    * 4. The feed price is updated to indicate that the collateral drops enough to trigger a margin call\n    *    **but not enough** to trigger a global settlement.\n    *    Bob's activated margin call cannot be matched against any existing limit order's price.\n    * 5. (Order 2: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT at a price\n    *    that will overlap with Bob's \"activated\" call order / margin call.\n    *    **Bob should be charged as a maker, and Alice as a taker.**\n    *    Alice's limit order should be (partially or completely) filled,\n    *    but Bob's order will also only be partially filled because the TCR will sell just enough collateral\n    *    so that the remaining CR of the debt position >= TCR.\n    *    Bob's debt position should remain open.\n    */\n   BOOST_AUTO_TEST_CASE(target_cr_partial_fill_of_call_order_as_maker) {\n      try {\n         //////\n         // 0. Advance to activate hardfork\n         //////\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n\n         //////\n         // 1. Initialize actors and a smart asset called SMARTBIT\n         //////\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         // CORE asset exists by default\n         const asset_object &core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n         const int64_t CORE_UNIT = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n\n         // Create the SMARTBIT asset\n         const int16_t SMARTBIT_UNIT = 10000; // 10000 satoshi SMARTBIT in 1 SMARTBIT\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const uint16_t smartbit_margin_call_fee_ratio = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         // Define the margin call fee ratio\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent, charge_market_fee, 4, core_id,\n                         GRAPHENE_MAX_SHARE_SUPPLY, {}, smartbit_margin_call_fee_ratio);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object smartbit = get_asset(\"SMARTBIT\");\n         const asset_id_type smartbit_id = smartbit.id;\n         update_feed_producers(smartbit, {feedproducer_id});\n\n         // Initialize token balance of actors\n         // Alice should start with 5,000,000 CORE\n         const asset alice_initial_core = asset(5000000 * CORE_UNIT);\n         transfer(committee_account, alice_id, alice_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id, core_id), alice_initial_core.amount.value);\n\n         // Bob should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 2x\n         const price initial_feed_price =\n                 smartbit.amount(1) / core.amount(20); // 1 satoshi SMARTBIT for 20 satoshi CORE\n         const asset bob_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset bob_initial_core = core.amount(\n                 2 * (bob_initial_smart * initial_feed_price).amount); // 80,000,000 satoshi CORE\n         transfer(committee_account, bob_id, bob_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, core), 80000000);\n\n\n         //////\n         // 2. Publish feed\n         //////\n         price_feed current_feed;\n         current_feed.settlement_price = initial_feed_price;\n         current_feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         current_feed.maximum_short_squeeze_ratio = 1500; // MSSR of 1.50x\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // 3. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n         //    Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n         //    because his debt position is what will be tracked.\n         //////\n         const uint16_t tcr = 2200; // Bob's target collateral ratio (TCR) 220% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         call_order_id_type bob_call_id = (*borrow(bob, bob_initial_smart, bob_initial_core, tcr)).id;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price bob_initial_cr = bob_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(bob_initial_cr.base.amount.value, 80000000); // Collateral of 80,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(bob_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n\n         //////\n         // 4. The feed price is updated to indicate that the collateral drops enough to trigger a margin call\n         //    **but not enough** to trigger a global settlement.\n         //    Bob's activated margin call cannot be matched against any existing limit order's price.\n         //////\n         // Adjust the price such that the initial CR of Bob's position (CR_0) drops to 1.7x = (17/10)x\n         // Want new price = 1.7 / CR_0 = (17/10) / CR_0\n         //\n         // Collateral ratios are defined as collateral / debt\n         // BitShares prices are conventionally defined as debt / collateral\n         // The new price can be expressed with the available codebase as\n         // = (17/10) * ~CR_0 = ~CR_0 * (17/10)\n         const price intermediate_feed_price = ~bob_initial_cr * ratio_type(17, 10); // Units of debt / collateral\n         // Reduces to (2000000 * 17) / (80000000 * 10) = (17) / (40 * 10) = 17 / 400\n         BOOST_CHECK(intermediate_feed_price < initial_feed_price);\n         BOOST_CHECK_EQUAL(intermediate_feed_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(intermediate_feed_price.quote.amount.value, 400); // satoshi CORE\n\n         current_feed.settlement_price = intermediate_feed_price;\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n\n         BOOST_CHECK(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n\n         // Check Bob's debt to the blockchain\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value, bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value, bob_initial_core.amount.value);\n\n         // Check Bob's balances\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 0);\n\n\n\n         //////\n         // 5. (Order 2: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT at a price\n         //    that will overlap with Bob's \"activated\" call order / margin call.\n         //    **Bob should be charged as a maker, and Alice as a taker.**\n         //    Alice's limit order should be (partially or completely) filled,\n         //    but Bob's order will also only be partially filled because the TCR will sell just enough collateral\n         //    so that the remaining CR of the debt position >= TCR.\n         //    Bob's debt position should remain open.\n         //////\n         // Alice obtains her SMARTBIT from Bob\n         transfer(bob_id, alice_id, bob_initial_smart);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), bob_initial_smart.amount.value);\n\n         // The margin call should be priced at settlement_price / (MSSR-MCFR)\n         // where settlement_price is expressed as debt / collateral\n         // Create a \"large\" sell order at a \"high\" price of settlement_price * 1.1 = settlement_price * (11/10)\n         const price alice_order_price_implied = intermediate_feed_price * ratio_type(11, 10);\n\n\n         const asset alice_debt_to_sell = smartbit.amount(get_balance(alice_id(db), smartbit_id(db)));\n         // multiply_and_round_up() handles inverting the price so that the output is in correct collateral units\n         const asset alice_collateral_to_buy = alice_debt_to_sell.multiply_and_round_up(alice_order_price_implied);\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice_id, alice_debt_to_sell,\n                                                                            alice_collateral_to_buy);\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         // asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // The match price **as maker** should be the settlement_price/(MSSR-MCFR) = settlement_price/(MSSR-MCFR)\n         const uint16_t ratio_numerator = current_feed.maximum_short_squeeze_ratio - smartbit_margin_call_fee_ratio;\n         BOOST_REQUIRE_EQUAL(ratio_numerator,\n                             1450); // GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO - smartbit_margin_call_fee_ratio\n         const price expected_match_price = intermediate_feed_price * ratio_type(GRAPHENE_COLLATERAL_RATIO_DENOM,\n                                                                                 ratio_numerator);\n         // Reduces to (17 satoshi SMARTBIT / 400 satoshi CORE) * (1000 / 1450)\n         // = (17 satoshi SMARTBIT / 400 satoshi CORE) * (100 / 145)\n         // = (17 satoshi SMARTBIT / 4 satoshi CORE) * (1 / 145)\n         // = 17 satoshi SMARTBIT / 580 satoshi CORE\n         BOOST_CHECK_EQUAL(expected_match_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(expected_match_price.quote.amount.value, 580); // satoshi CORE\n\n         // When a TCR is set for a call order, the ideal is to not sell all of the collateral\n         // but only enough collateral so that the remaining collateral and the remaining debt in the debt position\n         // has a resulting CR >= TCR.  The specifications are described in BSIP38.\n         //\n         // Per BSIP38, the expected amount to sell from the call order is\n         // max_amount_to_sell = (debt * target_CR - collateral * settlement_price)\n         //                    / (target_CR * match_price - settlement_price)\n         //\n         // HOWEVER, the match price that is used in this calculation\n         // NEEDS TO BE ADJUSTED to account for the extra MCFR > 0 that will be paid by the call order.\n         //\n         // Rather than using a match price of settlement_price/(MSSR-MCFR) **AS A MAKER**,\n         // the call_pays_price of settlement_price/(MSSR-MCFR+MCFR) = settlement_price/MSSR should be used\n         // when determining the amount of collateral and debt that will removed from the debt position.\n         // The limit order will still be compensated based on the normal match price of settlement_price/(MSSR-MCFR)\n         // but the calculation from BSIP38 should use the call_pays_price which reflects that the call order\n         // will actually pay more collateral\n         // (it can be considered as a higher effective price when denominated in collateral / debt,\n         // or equivalently a lower effective price when denominated in debt / collateral).\n\n         // Therefore, the call_pays_price, WHEN THE CALL ORDER IS MAKER,\n         //                   = feed_price / MSSR reduces to\n         // feed_price / MSSR = (17 satoshi SMARTBIT / 400 satoshi CORE) * (1000 / 1500)\n         //                   =  (17 satoshi SMARTBIT / 400 satoshi CORE) * (10 / 15)\n         //                   =  17 satoshi SMARTBIT / 600 satoshi CORE\n\n         // Returning to the formula for the TCR amount to sell from the call order\n         // max_amount_to_sell = (debt * target_CR - collateral * feed_price) / (target_CR * call_pays_price - feed_price)\n         //\n         // = (2000000 satoshi SMARTBIT * [2200 / 1000] - 80000000 satoshi CORE * [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //   / ([2200 / 1000] *  [17 satoshi SMARTBIT / 600 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (2000000 satoshi SMARTBIT * [22 / 10] - 80000000 satoshi SMARTBIT * [17 / 400])\n         //   / ([22 / 10] *  [17 satoshi SMARTBIT / 600 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (200000 satoshi SMARTBIT * [22] - 200000 satoshi SMARTBIT * [17])\n         //   / ([22 / 10] *  [17 satoshi SMARTBIT / 600 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (200000 satoshi SMARTBIT * [22 - 17])\n         //   / ([22 / 10] *  [17 satoshi SMARTBIT / 600 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (200000 satoshi CORE * [5]) / ([22 / 10] *  [17 / 600] - [17 / 400])\n         //\n         // = (1000000 satoshi CORE) / ([22 / 10] *  [17 / 600] - [17 / 400])\n         //\n         // ~= (1000000 satoshi CORE) / (0.0198333333333) ~= 50420168.0757 satoshi CORE\n         //\n         // ~= rounded up to 50420169 satoshi CORE = 504.20169 CORE\n         // const asset expected_max_amount_to_sell = core.amount(50420169);\n         // match() is calculating 50420189 CORE\n\n         // Per BSIP38, the expected amount to cover from the call order\n         //\n         // max_debt_to_cover = max_amount_to_sell * match_price\n         //\n         // which is adjusted to\n         //\n         // max_debt_to_cover = max_amount_to_sell * call_pays_price\n\n         // Therefore the\n         //\n         // = (1000000 satoshi CORE) / ([22 / 10] *  [17 / 600] - [17 / 400]) * (17 satoshi SMARTBIT / 600 satoshi CORE)\n         //\n         // ~= 50420168.0757 satoshi CORE * (17 satoshi SMARTBIT / 600 satoshi CORE)\n         //\n         // ~= 1428571.42881 satoshi SMARTBIT rounded down to 1428571 satoshi SMARTBIT = 142.8571 SMARTBIT\n         // ~= 1428571.42881 satoshi SMARTBIT rounded up to 1428572 satoshi SMARTBIT = 142.8572 SMARTBIT\n         const asset expected_max_debt_to_cover = smartbit.amount(1428572);\n\n         // WHEN THE CALL ORDER IS MAKER, the match_price is settlement_price/(MSSR-MCFR)\n         // Payment to limit order = X/match_price = X*(MSSR-MCFR)/settlement_price\n         // = 1428572 satoshi SMARTBIT * (580 satoshi CORE / 17 satoshi SMARTBIT)\n         // = 48739515.2941 satoshi CORE rounded up to 48739516 satoshi CORE = 487.39516 CORE\n         // Margin call should exchange the filled debt (X) for X*(MSSR-MCFR)/settlement_price\n         const asset expected_payment_to_alice_core = core.amount(48739516);\n\n         // Caluclate the expected payment in collateral by the call order\n         // to fulfill the (complete or partial) filling of the margin call.\n         //\n         // The expected payment is not necessarily equal to BSIP38's max_amount_to_sell.\n         // It should be calculated base on the amount paid to the limit order (X), the settlement price,\n         // and the MSSR.\n         //\n         // Expected payment by call order = X*MSSR/settlement_price\n         // Expected payment by call order = 1428572 satoshi SMARTBIT * (600 satoshi CORE / 17 satoshi SMARTBIT)\n         // = 1428572 * 600 satoshi CORE / 17\n         // = 50420188.2353 satoshi CORE rounding up to 50420189 satoshi CORE = 504.20189 CORE\n         const asset expected_payment_from_bob_core = core.amount(50420189);\n\n         // The call order MUST ALSO pay the margin call fee\n         // Expected fee = payment by call order - payment to limit order\n         const asset expected_margin_call_fee = expected_payment_from_bob_core - expected_payment_to_alice_core;\n\n         // Check Alice's balances\n         BOOST_CHECK_EQUAL(get_balance(alice, smartbit), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice, core),\n                           alice_initial_core.amount.value + expected_payment_to_alice_core.amount.value);\n\n         // Alice's limit order should be open because of its partial filling\n         BOOST_CHECK(db.find(alice_order_id));\n\n         // Check Alice's limit order\n         // The amount of smart asset available for sale should be reduced by the amount paid to Bob's margin call\n         limit_order_object alice_limit_order = alice_order_id(db);\n         asset expected_alice_remaining_smart_for_sale = alice_debt_to_sell - expected_max_debt_to_cover;\n         BOOST_CHECK_EQUAL(alice_limit_order.amount_for_sale().amount.value,\n                           expected_alice_remaining_smart_for_sale.amount.value);\n         // Alice's limit order's price should be unchanged by the margin call\n         BOOST_CHECK(alice_limit_order.sell_price == alice_sell_op.get_price());\n\n         // Bob's debt position should be open because of its partial filling\n         BOOST_CHECK(db.find(bob_call_id));\n\n         // Check Bob's debt position\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value,\n                           bob_initial_smart.amount.value - expected_max_debt_to_cover.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value,\n                           bob_initial_core.amount.value - expected_payment_to_alice_core.amount.value -\n                           expected_margin_call_fee.amount.value);\n\n         // Check Bob's balances\n         // Bob should have no debt asset\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0);\n         // Bob should NOT have collected the balance of his collateral after the margin call\n         // because the debt position is still open\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 0);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees.value,\n                           expected_margin_call_fee.amount.value);\n\n         // Check the fee of the fill operations for Alice and Bob\n         generate_block(); // To trigger db_notify() and record pending operations into histories\n         graphene::app::history_api hist_api(app);\n         vector <operation_history_object> histories;\n         const int fill_order_op_id = operation::tag<fill_order_operation>::value;\n\n         // Check Alice's history\n         histories = hist_api.get_account_history_operations(\n                 \"alice\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Alice's fill order for her limit order should have zero fee\n         fill_order_operation alice_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(alice_fill_op.fee == asset(0));\n         // Alice's fill order's fill price should equal the expected match price\n         BOOST_CHECK(~alice_fill_op.fill_price == expected_match_price);\n\n         // Check Bob's history\n         histories = hist_api.get_account_history_operations(\n                 \"bob\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Bob's fill order for his margin call should have a fee equal to the margin call fee\n         fill_order_operation bob_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(bob_fill_op.fee == expected_margin_call_fee);\n         // Bob's fill order's fill price should equal the expected match price\n         BOOST_CHECK(~bob_fill_op.fill_price == expected_match_price);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test a simple scenario of a Complete Fill of a Call Order as a Taker after HF\n    *\n    * 0. Advance to HF\n    * 1. Initialize actors and a smart asset called SMARTBIT\n    * 2. Publish feed\n    * 3. (Order 1: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT\n    * 4. (Order 2: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n    *     Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n    *     because his debt position is what will be tracked.\n    * 5. The feed price indicates that the collateral drops enough to trigger a margin call\n    *    **and** enough to be matched against Alice's limit order.\n    *    (Global settlement is not at risk because Bob's small order should be matched\n    *    and completely filled by Alice's large order).\n    *    Alice's limit order should be matched against Bob's \"activated\" call order.\n    *    **Alice should be charged as a maker, and Bob as a taker.**\n    *    Alice's limit order should be partially filled,\n    *    but Bob's order should be completely filled and removed from the book.\n    */\n   BOOST_AUTO_TEST_CASE(complete_fill_of_call_order_as_taker) {\n      try {\n         //////\n         // 0. Advance to activate hardfork\n         //////\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n\n         //////\n         // 1. Initialize actors and a smart asset called SMARTBIT\n         //////\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob)(charlie));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         // CORE asset exists by default\n         const asset_object &core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n         const int64_t CORE_UNIT = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n\n         // Create the SMARTBIT asset\n         const int16_t SMARTBIT_UNIT = 10000; // 10000 satoshi SMARTBIT in 1 SMARTBIT\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const uint16_t smartbit_margin_call_fee_ratio = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         // Define the margin call fee ratio\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent, charge_market_fee, 4, core_id,\n                         GRAPHENE_MAX_SHARE_SUPPLY, {}, smartbit_margin_call_fee_ratio);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object smartbit = get_asset(\"SMARTBIT\");\n         const asset_id_type smartbit_id = smartbit.id;\n         update_feed_producers(smartbit, {feedproducer_id});\n\n         // Initialize token balance of actors\n\n         // Alice should start with enough CORE to back 5000 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 4x\n         const price initial_feed_price =\n                 smartbit.amount(1) / core.amount(20); // 1 satoshi SMARTBIT for 20 satoshi CORE\n         const asset alice_initial_smart = smartbit.amount(500 * SMARTBIT_UNIT); // 5,000,000 satoshi SMARTBIT\n         const asset alice_initial_core = core.amount(\n                 4 * (alice_initial_smart * initial_feed_price).amount); // 400,000,000 satoshi CORE\n         transfer(committee_account, alice_id, alice_initial_core);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), alice_initial_core.amount.value);\n\n         // Bob should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 2x\n         const asset bob_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset bob_initial_core = core.amount(\n                 2 * (bob_initial_smart * initial_feed_price).amount); // 80,000,000 satoshi CORE\n         transfer(committee_account, bob_id, bob_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, core), 80000000);\n\n         // In Step 5, the feed price will be adjusted such that\n         // the initial CR of Bob's position (CR_0) drops to 1.7x = (17/10)x\n         // Want new price = 1.7 / CR_0 = (17/10) / CR_0\n         //\n         // Collateral ratios are defined as collateral / debt\n         // BitShares prices are conventionally defined as debt / collateral\n         // The new price can be expressed with the available codebase as\n         // = (17/10) * ~CR_0 = ~CR_0 * (17/10)\n         const price expected_bob_initial_cr =\n                 core.amount(2 * 20) / smartbit.amount(1); // 1 satoshi SMARTBIT for 40 satoshi CORE\n         const price intermediate_feed_price =\n                 ~expected_bob_initial_cr * ratio_type(17, 10); // Units of debt / collateral\n         // Reduces to (2000000 * 17) / (80000000 * 10) = (17) / (40 * 10) = 17 satoshi SMARTBIT / 400 satoshi CORE\n         BOOST_CHECK_EQUAL(intermediate_feed_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(intermediate_feed_price.quote.amount.value, 400); // satoshi CORE\n         BOOST_CHECK(intermediate_feed_price < initial_feed_price);\n\n\n         //////\n         // 2. Publish feed\n         //////\n         price_feed current_feed;\n         current_feed.settlement_price = initial_feed_price;\n         current_feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         current_feed.maximum_short_squeeze_ratio = 1500; // MSSR of 1.50x\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // 3. (Order 1: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT.\n         //////\n         // Alice borrows SMARTBIT\n         call_order_id_type alice_call_id = (*borrow(alice, alice_initial_smart, alice_initial_core)).id;\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 500 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n\n         // Alice offer to sell the SMARTBIT\n         // Create a \"large\" sell order at a \"high\" price of settlement_price * 1.1 = settlement_price * (11/10)\n         const price alice_order_price_implied = intermediate_feed_price * ratio_type(11, 10);\n         // = (17 satoshi SMARTBIT / 400 satoshi CORE) * (11/10)\n         // = 187 satoshi SMARTBIT / 4000 satoshi CORE\n         BOOST_CHECK_EQUAL(alice_order_price_implied.base.amount.value, 187); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(alice_order_price_implied.quote.amount.value, 4000); // satoshi CORE\n\n         const asset alice_debt_to_sell = smartbit.amount(get_balance(alice_id(db), smartbit_id(db)));\n         // multiply_and_round_up() handles inverting the price so that the output is in correct collateral units\n         const asset alice_collateral_to_buy = alice_debt_to_sell.multiply_and_round_up(alice_order_price_implied);\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice_id, alice_debt_to_sell,\n                                                                            alice_collateral_to_buy);\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         // asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Alice should have no balance\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n\n\n         //////\n         // 4. (Order 2: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n         //    Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n         //    because his debt position is what will be tracked.\n         //////\n         const asset bob_initial_debt_smart = bob_initial_smart;\n         const asset bob_initial_debt_collateral = bob_initial_core;\n         call_order_id_type bob_call_id = (*borrow(bob, bob_initial_debt_smart, bob_initial_debt_collateral)).id;\n\n         // Bobs's balances should reflect that CORE was used to create SMARTBIT\n         BOOST_CHECK_EQUAL(get_balance(bob_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, core_id), 0);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price bob_initial_cr = bob_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK(bob_initial_cr == expected_bob_initial_cr);\n         BOOST_CHECK_EQUAL(bob_initial_cr.base.amount.value, 80000000); // Collateral of 80,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(bob_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n         // Alice's balances should not have changed\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n\n         // Alice should not have been margin called\n         price alice_initial_cr = alice_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(alice_initial_cr.base.amount.value, 400000000); // Collateral of 400,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(alice_initial_cr.quote.amount.value, 5000000); // Debt of 5,000,000 satoshi SMARTBIT\n\n         //////\n         // Bob transfers his SMARTBIT to Charlie to clarify the accounting\n         //////\n         transfer(bob_id, charlie_id, bob_initial_smart);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, smartbit_id), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, core_id), 0 * CORE_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, core_id), 0 * CORE_UNIT);\n\n\n         //////\n         // 5. The feed price indicates that the collateral drops enough to trigger a margin call\n         //    **and** enough to be matched against Alice's limit order.\n         //    (Global settlement is not at risk because Bob's small order should be matched\n         //    and completely filled by Alice's large order).\n         //    Alice's limit order should be matched against Bob's \"activated\" call order.\n         //    **Alice should be charged as a maker, and Bob as a taker.**\n         //    Alice's limit order should be partially filled,\n         //     but Bob's order should be completely filled and removed from the book.\n         //////\n         current_feed.settlement_price = intermediate_feed_price;\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         // Confirm the updated feed\n         BOOST_CHECK(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n         // Confirm no global settlement\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement());\n\n\n         // The margin call of Bob's position should have closed the debt of bob_initial_smart\n         // Bob's margin call should been matched against Alice's limit order\n         // Bob's debt position should have paid collateral = bob_initial_smart / limit_order_price\n         // 200 SMARTBIT / (187 satoshi SMARTBIT / 4000 satoshi CORE)\n         // = 2,000,000 satoshi SMARTBIT / (187 satoshi SMARTBIT / 4000 satoshi CORE)\n         // = 2,000,000 satoshi SMARTBIT * (4000 satoshi CORE / 187 satoshi SMARTBIT)\n         // = 2,000,000 satoshi CORE / (4000 / 187)\n         // = 42,780,748.6631 satoshi CORE rounded up to 42,780,749 satoshi CORE\n         const asset expected_margin_call_from_bob_debt_core = core.amount(42780749);\n\n         // Bob's margin call fee, which is paid in collateral, should be charged as a taker\n         // The margin call fee debt = filled_debt * MCFR/(MSSR-MCFR) / limit_order_price\n         // 200 SMARTBIT * (50 / (1500 - 50)) / (187 satoshi SMARTBIT / 4000 satoshi CORE)\n         // = 2,000,000 satoshi SMARTBIT * (50 / 1450) / (187 satoshi SMARTBIT / 4000 satoshi CORE)\n         // = 2,000,000 satoshi CORE * (1 / 29) * (4000 / 187)\n         // = 1475198.22976 satoshi CORE rounded up to 1475199 satoshi CORE\n         const asset expected_margin_call_fee_from_bob_debt_core = core.amount(1475199);\n\n         // The balance of Bob's debt position\n         const asset expected_return_from_bob_debt_core = bob_initial_core\n                                                          - expected_margin_call_from_bob_debt_core\n                                                          - expected_margin_call_fee_from_bob_debt_core;\n\n         // Check Bob's debt position is closed\n         BOOST_CHECK(!db.find(bob_call_id));\n\n         // Check Bob's balances\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), expected_return_from_bob_debt_core.amount.value);\n\n         // Charlie's balances should not have changed\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, core_id), 0 * CORE_UNIT);\n\n         // Alice's balances should have changed because her limit order was partially filled by the margin call\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), expected_margin_call_from_bob_debt_core.amount.value);\n\n         // Check Alice's debt\n         // Alice's debt position should not be NOT closed\n         BOOST_CHECK(db.find(alice_call_id));\n         // Alice's debt should NOT have changed because its CR > MCR\n         alice_initial_cr = alice_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(alice_initial_cr.base.amount.value, 400000000); // Collateral of 400,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(alice_initial_cr.quote.amount.value, 5000000); // Debt of 5,000,000 satoshi SMARTBIT\n\n         // Check Alice's limit order\n         // The amount of smart asset available for sale should be reduced by the amount paid to Bob's margin call\n         limit_order_object alice_limit_order = alice_order_id(db);\n         asset expected_alice_remaining_smart_for_sale = alice_debt_to_sell - bob_initial_debt_smart;\n         asset expected_alice_remaining_core_to_receive =\n                 alice_collateral_to_buy - expected_margin_call_from_bob_debt_core;\n         BOOST_CHECK(alice_limit_order.amount_for_sale() == expected_alice_remaining_smart_for_sale);\n         BOOST_CHECK(alice_limit_order.amount_to_receive() == expected_alice_remaining_core_to_receive);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_fees.value, 0);\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees.value,\n                           expected_margin_call_fee_from_bob_debt_core.amount.value);\n\n         // Check the fee of the fill operations for Alice and Bob\n         generate_block(); // To trigger db_notify() and record pending operations into histories\n         graphene::app::history_api hist_api(app);\n         vector <operation_history_object> histories;\n         const int fill_order_op_id = operation::tag<fill_order_operation>::value;\n\n         // Check Alice's history\n         histories = hist_api.get_account_history_operations(\n                 \"alice\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Alice's fill order for her limit order should have zero fee\n         fill_order_operation alice_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(alice_fill_op.fee == asset(0));\n         // Alice's fill order's fill price should equal the expected match price\n         // Alice's alice_order_price_implied differs slightly from alice_sell_op.get_price()\n         // due to rounding in this test while creating the parameters for the limit order\n         const price expected_match_price = alice_sell_op.get_price();\n         BOOST_CHECK(alice_fill_op.fill_price == expected_match_price);\n\n         // Check Bob's history\n         histories = hist_api.get_account_history_operations(\n                 \"bob\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Bob's fill order for his margin call should have a fee equal to the margin call fee\n         fill_order_operation bob_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(bob_fill_op.fee == expected_margin_call_fee_from_bob_debt_core);\n         // Bob's fill order's fill price should equal the expected match price\n         BOOST_CHECK(bob_fill_op.fill_price == expected_match_price);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test a scenario of a partial Filling of a Call Order as a Taker after HF\n    * where the partial filling is due to call order defining a target collateral ratio (TCR) (BSIP38)\n    *\n    * 0. Advance to HF\n    * 1. Initialize actors and a smart asset called SMARTBIT\n    * 2. Publish feed\n    * 3. (Order 1: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT\n    * 4. (Order 2: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n    *     Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n    *     because his debt position is what will be tracked.\n    * 5. The feed price indicates that the collateral drops enough to trigger a margin call\n    *    **and** enough to be matched against Alice's limit order.\n    *    Alice's limit order should be matched against Bob's \"activated\" call order.\n    *    **Alice should be charged as a maker, and Bob as a taker.**\n    *    Alice's limit order should be (partially or completely) filled,\n    *    but Bob's order will also only be partially filled because the TCR will sell just enough collateral\n    *    so that the remaining CR of the debt position >= TCR.\n    *    Bob's debt position should remain open.\n    */\n   BOOST_AUTO_TEST_CASE(target_cr_partial_fill_of_call_order_as_taker) {\n      try {\n         //////\n         // 0. Advance to activate hardfork\n         //////\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n\n         //////\n         // 1. Initialize actors and a smart asset called SMARTBIT\n         //////\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob)(charlie));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         // CORE asset exists by default\n         const asset_object &core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n         const int64_t CORE_UNIT = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n\n         // Create the SMARTBIT asset\n         const int16_t SMARTBIT_UNIT = 10000; // 10000 satoshi SMARTBIT in 1 SMARTBIT\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const uint16_t smartbit_margin_call_fee_ratio = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         // Define the margin call fee ratio\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent, charge_market_fee, 4, core_id,\n                         GRAPHENE_MAX_SHARE_SUPPLY, {}, smartbit_margin_call_fee_ratio);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object smartbit = get_asset(\"SMARTBIT\");\n         const asset_id_type smartbit_id = smartbit.id;\n         update_feed_producers(smartbit, {feedproducer_id});\n\n         // Initialize token balance of actors\n\n         // Alice should start with enough CORE to back 5000 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 4x\n         const price initial_feed_price =\n                 smartbit.amount(1) / core.amount(20); // 1 satoshi SMARTBIT for 20 satoshi CORE\n         const asset alice_initial_smart = smartbit.amount(500 * SMARTBIT_UNIT); // 5,000,000 satoshi SMARTBIT\n         const asset alice_initial_core = core.amount(\n                 4 * (alice_initial_smart * initial_feed_price).amount); // 400,000,000 satoshi CORE\n         transfer(committee_account, alice_id, alice_initial_core);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), alice_initial_core.amount.value);\n\n         // Bob should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 2x\n         const asset bob_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset bob_initial_core = core.amount(\n                 2 * (bob_initial_smart * initial_feed_price).amount); // 80,000,000 satoshi CORE\n         transfer(committee_account, bob_id, bob_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, core), 80000000);\n\n         // In Step 5, the feed price will be adjusted such that\n         // the initial CR of Bob's position (CR_0) drops to 1.7x = (17/10)x\n         // Want new price = 1.7 / CR_0 = (17/10) / CR_0\n         //\n         // Collateral ratios are defined as collateral / debt\n         // BitShares prices are conventionally defined as debt / collateral\n         // The new price can be expressed with the available codebase as\n         // = (17/10) * ~CR_0 = ~CR_0 * (17/10)\n         const price expected_bob_initial_cr =\n                 core.amount(2 * 20) / smartbit.amount(1); // 1 satoshi SMARTBIT for 40 satoshi CORE\n         const price intermediate_feed_price =\n                 ~expected_bob_initial_cr * ratio_type(17, 10); // Units of debt / collateral\n         // Reduces to (2000000 * 17) / (80000000 * 10) = (17) / (40 * 10) = 17 satoshi SMARTBIT / 400 satoshi CORE\n         BOOST_CHECK_EQUAL(intermediate_feed_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(intermediate_feed_price.quote.amount.value, 400); // satoshi CORE\n         BOOST_CHECK(intermediate_feed_price < initial_feed_price);\n\n\n         //////\n         // 2. Publish feed\n         //////\n         price_feed current_feed;\n         current_feed.settlement_price = initial_feed_price;\n         current_feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         current_feed.maximum_short_squeeze_ratio = 1500; // MSSR of 1.50x\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // 3. (Order 1: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT.\n         //////\n         // Alice borrows SMARTBIT\n         borrow(alice, alice_initial_smart, alice_initial_core);\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 500 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n\n         // Alice offer to sell the SMARTBIT\n         // Create a \"large\" sell order at a \"high\" price of settlement_price * 1.1 = settlement_price * (11/10)\n         const price alice_order_price_implied = intermediate_feed_price * ratio_type(11, 10);\n         // = (17 satoshi SMARTBIT / 400 satoshi CORE) * (11/10)\n         // = 187 satoshi SMARTBIT / 4000 satoshi CORE\n         BOOST_CHECK_EQUAL(alice_order_price_implied.base.amount.value, 187); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(alice_order_price_implied.quote.amount.value, 4000); // satoshi CORE\n\n         const asset alice_debt_to_sell = smartbit.amount(get_balance(alice_id(db), smartbit_id(db)));\n         // multiply_and_round_up() handles inverting the price so that the output is in correct collateral units\n         const asset alice_collateral_to_buy = alice_debt_to_sell.multiply_and_round_up(alice_order_price_implied);\n         //\n         // NOTE: The calculated limit order price is 5000000 satoshi SMARTBIT / 106951872 satoshi CORE\n         //\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice_id, alice_debt_to_sell,\n                                                                            alice_collateral_to_buy);\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         // asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Alice should have no balance\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n\n\n         //////\n         // 4. (Order 1: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n         //    Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n         //    because his debt position is what will be tracked.\n         //////\n         const uint16_t tcr = 2200; // Bob's target collateral ratio (TCR) 220% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         call_order_id_type bob_call_id = (*borrow(bob, bob_initial_smart, bob_initial_core, tcr)).id;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price bob_initial_cr = bob_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(bob_initial_cr.base.amount.value, 80000000); // Collateral of 80,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(bob_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n         //////\n         // Bob transfers his SMARTBIT to Charlie to clarify the accounting\n         //////\n         transfer(bob_id, charlie_id, bob_initial_smart);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, smartbit_id), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, core_id), 0 * CORE_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, core_id), 0 * CORE_UNIT);\n\n\n         //////\n         // 5. The feed price indicates that the collateral drops enough to trigger a margin call\n         //    **and** enough to be matched against Alice's limit order.\n         //    Alice's limit order should be matched against Bob's \"activated\" call order.\n         //    **Alice should be charged as a maker, and Bob as a taker.**\n         //    Alice's limit order should be (partially or completely) filled,\n         //    but Bob's order will also only be partially filled because the TCR will sell just enough collateral\n         //    so that the remaining CR of the debt position >= TCR.\n         //    Bob's debt position should remain open.\n         //////\n         current_feed.settlement_price = intermediate_feed_price;\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         // Confirm the updated feed\n         BOOST_CHECK(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n         // Confirm no global settlement\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement());\n\n         // When a TCR is set for a call order, the ideal is to not sell all of the collateral\n         // but only enough collateral so that the remaining collateral and the remaining debt in the debt position\n         // has a resulting CR >= TCR.  The specifications are described in BSIP38.\n         //\n         // Per BSIP38, the expected amount to sell from the call order is\n         // max_amount_to_sell = (debt * target_CR - collateral * feed_price) / (target_CR * match_price - feed_price)\n         //\n         // HOWEVER, the match price that is used in this calculation\n         // NEEDS TO BE ADJUSTED to account for the extra MCFR > 0 that will be paid by the call order.\n         //\n         // Rather than using a match price of limit_order_price **AS A TAKER**,\n         // the call_pays_price of limit_order_price * (MSSR-MCFR) / MSSR should be used\n         // when determining the amount of collateral and debt that will removed from the debt position.\n         //\n         // The limit order will still be compensated based on the quoted match price of limit_order_price\n         // but the calculation from BSIP38 should use the call_pays_price which reflects that the call order\n         // will actually pay more collateral\n         // (it can be considered as a higher effective price when denominated in collateral / debt,\n         // or equivalently a lower effective price when denominated in debt / collateral).\n\n         // Therefore, the call_pays_price, WHEN THE CALL ORDER IS TAKER,\n         //                 = limit_order_price*(MSSR-MCFR)/MSSR reduces to\n         //\n         // call_pays_price = (5000000 satoshi SMARTBIT / 106951872 satoshi CORE) * ([1500-50] / 1500)\n         //                 = (5000000 satoshi SMARTBIT / 106951872 satoshi CORE) * (1450 / 1500)\n         //                 = (5000000 satoshi SMARTBIT / 106951872 satoshi CORE) * (29 / 30)\n         //                 = (500000 satoshi SMARTBIT / 106951872 satoshi CORE) * (29 / 3)\n         //                 = (14500000 satoshi SMARTBIT / 320855616 satoshi CORE)\n         //                 = (453125 satoshi SMARTBIT / 10026738 satoshi CORE)\n\n         // Returning to the formula for the TCR amount to sell from the call order\n         // max_amount_to_sell = (debt * target_CR - collateral * feed_price) / (target_CR * call_pays_price - feed_price)\n         //\n         // = (2000000 satoshi SMARTBIT * [2200 / 1000] - 80000000 satoshi CORE * [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //   / ([2200 / 1000] *  [453125 satoshi SMARTBIT / 10026738 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (2000000 satoshi SMARTBIT * [22 / 10] - 80000000 satoshi SMARTBIT * [17 / 400])\n         //   / ([22 / 10] *  [453125 satoshi SMARTBIT / 10026738 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (200000 satoshi SMARTBIT * [22] - 200000 satoshi SMARTBIT * [17])\n         //   / ([22 / 10] *  [453125 satoshi SMARTBIT / 10026738 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (200000 satoshi SMARTBIT * [22 - 17])\n         //   / ([22 / 10] *  [453125 satoshi SMARTBIT / 10026738 satoshi CORE] - [17 satoshi SMARTBIT / 400 satoshi CORE])\n         //\n         // = (200000 satoshi CORE * [5]) / ([22 / 10] *  [453125 / 10026738] - [17 / 400])\n         //\n         // = (1000000 satoshi CORE) / ([22 / 10] *  [453125 / 10026738] - [17 / 400])\n         //\n         // ~= (1000000 satoshi CORE) / (0.0569216663485) ~= 17568002.9117 satoshi CORE\n         //\n         // ~= rounded up to 17568003 satoshi CORE = 175.68003 CORE\n         // const asset expected_max_amount_to_sell = core.amount(17568003);\n         // match() is calculating ???? CORE\n\n         // Per BSIP38, the expected amount to cover from the call order\n         //\n         // max_debt_to_cover = max_amount_to_sell * match_price\n         //\n         // which is adjusted to\n         //\n         // max_debt_to_cover = max_amount_to_sell * call_pays_price\n\n         // Therefore the\n         //\n         // = (1000000 satoshi CORE) / ([22 / 10] *  [17 / 600] - [17 / 400])\n         //   * (453125 satoshi SMARTBIT / 10026738 satoshi CORE)\n         //\n         // ~= 17568002.9117 satoshi CORE * (453125 satoshi SMARTBIT / 10026738 satoshi CORE)\n         //\n         // ~= 793927.329044 satoshi SMARTBIT rounded down to 793927 satoshi SMARTBIT = 79.3927 SMARTBIT\n         // ~= 793927.329044 satoshi SMARTBIT rounded up to 793928 satoshi SMARTBIT = 79.3928 SMARTBIT\n         const asset expected_max_debt_to_cover = smartbit.amount(793928);\n\n\n         // WHEN THE CALL ORDER IS TAKER, the match_price is the limit_order price\n         // Payment to limit order = X/match_price = X/limit_order_price\n         // = 793928 satoshi SMARTBIT * (106951872 satoshi CORE / 5000000 satoshi SMARTBIT)\n         // = 16982417.1666 satoshi CORE rounded up to 16982418 satoshi CORE = 169.82418 CORE\n         // Margin call should exchange the filled debt (X) for X/limit_order_price\n         const asset expected_payment_to_alice_core = core.amount(16982418);\n\n         // Caluclate the expected payment in collateral by the call order\n         // to fulfill the (complete or partial) filling of the margin call.\n         //\n         // The expected payment is not necessarily equal to BSIP38's max_amount_to_sell.\n         // It should be calculated base on the amount paid to the limit order (X), the settlement price,\n         // and the MSSR.\n         //\n         // Expected payment by call order = X/fill_price\n         // Expected payment by call order = X/[settlement_price*(MSSR-MCFR)/MSSR]\n         // Expected payment by call order\n         // = 793928 satoshi SMARTBIT / (453125 satoshi SMARTBIT / 10026738 satoshi CORE)\n         // = 17568017.7586 satoshi CORE rounding up to 17568018 satoshi CORE = 175.68018 CORE\n         const asset expected_payment_from_bob_core = core.amount(17568018);\n\n         // The call order MUST ALSO pay the margin call fee\n         // Expected fee = payment by call order - payment to limit order\n         const asset expected_margin_call_fee = expected_payment_from_bob_core - expected_payment_to_alice_core;\n\n         // Check Alice's balances\n         BOOST_CHECK_EQUAL(get_balance(alice, smartbit), 0);\n         BOOST_CHECK_EQUAL(get_balance(alice, core), 0 + expected_payment_to_alice_core.amount.value);\n\n         // Alice's limit order should be open because of its partial filling\n         BOOST_CHECK(db.find(alice_order_id));\n\n         // Check Alice's limit order\n         // The amount of smart asset available for sale should be reduced by the amount paid to Bob's margin call\n         limit_order_object alice_limit_order = alice_order_id(db);\n         asset expected_alice_remaining_smart_for_sale = alice_debt_to_sell - expected_max_debt_to_cover;\n         BOOST_CHECK_EQUAL(alice_limit_order.amount_for_sale().amount.value,\n                           expected_alice_remaining_smart_for_sale.amount.value);\n         // Alice's limit order's price should be unchanged by the margin call\n         BOOST_CHECK(alice_limit_order.sell_price == alice_sell_op.get_price());\n\n         // Bob's debt position should be open because of its partial filling\n         BOOST_CHECK(db.find(bob_call_id));\n\n         // Check Bob's debt position\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value,\n                           bob_initial_smart.amount.value - expected_max_debt_to_cover.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value,\n                           bob_initial_core.amount.value - expected_payment_to_alice_core.amount.value -\n                           expected_margin_call_fee.amount.value);\n\n         // Bob's balances should not have changed because his debt position should remain open\n         // because the debt position is still open\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 0 * CORE_UNIT);\n\n         // Charlie's balances should not have changed\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, core_id), 0 * CORE_UNIT);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == 0);\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees.value,\n                           expected_margin_call_fee.amount.value);\n\n         // Check the fee of the fill operations for Alice and Bob\n         generate_block(); // To trigger db_notify() and record pending operations into histories\n         graphene::app::history_api hist_api(app);\n         vector <operation_history_object> histories;\n         const int fill_order_op_id = operation::tag<fill_order_operation>::value;\n\n         // Check Alice's history\n         histories = hist_api.get_account_history_operations(\n                 \"alice\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Alice's fill order for her limit order should have zero fee\n         fill_order_operation alice_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(alice_fill_op.fee == asset(0));\n         // Alice's fill order's fill price should equal the expected match price\n         // Alice's alice_order_price_implied differs slightly from alice_sell_op.get_price()\n         // due to rounding in this test while creating the parameters for the limit order\n         const price expected_match_price = alice_sell_op.get_price();\n         BOOST_CHECK(alice_fill_op.fill_price == expected_match_price);\n\n         // Check Bob's history\n         histories = hist_api.get_account_history_operations(\n                 \"bob\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Bob's fill order for his margin call should have a fee equal to the margin call fee\n         fill_order_operation bob_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(bob_fill_op.fee == expected_margin_call_fee);\n         // Bob's fill order's fill price should equal the expected match price\n         BOOST_CHECK(bob_fill_op.fill_price == expected_match_price);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test a scenario of a Complete Fill of a Call Order as a Taker after HF\n    * where the matching to an existing limit order becomes possible\n    * after the MCFR is reduced and without any change to the feed price.\n    * This is made possible by the reduction of the MCFR changing the margin call order price.\n    *\n    * 0. Advance to HF\n    * 1. Initialize actors and a smart asset called SMARTBIT\n    * 2. Publish feed\n    * 3. (Order 1: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT\n    * 4. (Order 2: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n    *     Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n    *     because his debt position is what will be tracked.\n    * 5. The feed price indicates that the collateral drops enough to trigger a margin call\n    *    **but** the margin call order price (denominated in debt/collateral) is less than\n    *    than Alice's limit order price, resulting in no match.\n    * 6. The asset owner reduces the MCFR enough such that Alice's offer price SHOULD overlap\n    *    with the margin call order price.\n    *    Alice's limit order should be matched against Bob's \"activated\" call order.\n    *    **Alice should be charged as a maker, and Bob as a taker.**\n    *    Alice's limit order should be partially filled,\n    *    but Bob's order should be completely filled and removed from the book.\n    */\n   BOOST_AUTO_TEST_CASE(mcfr_reduction_triggers_matching_of_margin_call_order) {\n      try {\n         //////\n         // 0. Advance to activate hardfork\n         //////\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n\n\n         //////\n         // 1. Initialize actors and a smart asset called SMARTBIT\n         //////\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob)(charlie));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         // CORE asset exists by default\n         const asset_object &core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n         const int64_t CORE_UNIT = asset::scaled_precision(core.precision).value; // 100000 satoshi CORE in 1 CORE\n\n         // Create the SMARTBIT asset\n         const int16_t SMARTBIT_UNIT = 10000; // 10000 satoshi SMARTBIT in 1 SMARTBIT\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const uint16_t initial_mcfr = 400; // 40% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         const uint16_t final_mcfr = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         // Define the margin call fee ratio\n         create_bitasset(\"SMARTBIT\", smartissuer_id, smartbit_market_fee_percent, charge_market_fee, 4, core_id,\n                         GRAPHENE_MAX_SHARE_SUPPLY, {}, initial_mcfr);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object smartbit = get_asset(\"SMARTBIT\");\n         const asset_id_type smartbit_id = smartbit.id;\n         update_feed_producers(smartbit, {feedproducer_id});\n\n         // Initialize token balance of actors\n\n         // Alice should start with enough CORE to back 5000 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 4x\n         const price initial_feed_price =\n                 smartbit.amount(1) / core.amount(20); // 1 satoshi SMARTBIT for 20 satoshi CORE\n         const asset alice_initial_smart = smartbit.amount(500 * SMARTBIT_UNIT); // 5,000,000 satoshi SMARTBIT\n         const asset alice_initial_core = core.amount(\n                 4 * (alice_initial_smart * initial_feed_price).amount); // 400,000,000 satoshi CORE\n         transfer(committee_account, alice_id, alice_initial_core);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), alice_initial_core.amount.value);\n\n         // Bob should start with enough CORE to back 200 SMARTBIT subject to\n         // (a) to an initial price feed of 1 satoshi SMARTBIT for 20 satoshi CORE\n         // = 0.0001 SMARTBIT for 0.00020 CORE = 1 SMARTBIT for 2 CORE\n         // (b) an initial collateral ratio of 2x\n         const asset bob_initial_smart = smartbit.amount(200 * SMARTBIT_UNIT); // 2,000,000 satoshi SMARTBIT\n         const asset bob_initial_core = core.amount(\n                 2 * (bob_initial_smart * initial_feed_price).amount); // 80,000,000 satoshi CORE\n         transfer(committee_account, bob_id, bob_initial_core);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, core), 80000000);\n\n         // In Step 5, the feed price will be adjusted such that\n         // the initial CR of Bob's position (CR_0) drops to 1.7x = (17/10)x\n         // Want new price = 1.7 / CR_0 = (17/10) / CR_0\n         //\n         // Collateral ratios are defined as collateral / debt\n         // BitShares prices are conventionally defined as debt / collateral\n         // The new price can be expressed with the available codebase as\n         // = (17/10) * ~CR_0 = ~CR_0 * (17/10)\n         const price expected_bob_initial_cr =\n                 core.amount(2 * 20) / smartbit.amount(1); // 1 satoshi SMARTBIT for 40 satoshi CORE\n         const price intermediate_feed_price =\n                 ~expected_bob_initial_cr * ratio_type(17, 10); // Units of debt / collateral\n         // Reduces to (2000000 * 17) / (80000000 * 10) = (17) / (40 * 10) = 17 satoshi SMARTBIT / 400 satoshi CORE\n         BOOST_CHECK_EQUAL(intermediate_feed_price.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(intermediate_feed_price.quote.amount.value, 400); // satoshi CORE\n         BOOST_CHECK(intermediate_feed_price < initial_feed_price);\n\n         // Pre-calculate the planned initial margin call order price (MCOP)\n         const uint16_t mssr = 1500;\n         const uint16_t initial_ratio_numerator = mssr - initial_mcfr;\n         BOOST_REQUIRE_EQUAL(initial_ratio_numerator, 1100);\n         const price planned_initial_mcop = intermediate_feed_price * ratio_type(GRAPHENE_COLLATERAL_RATIO_DENOM,\n                                                                                 initial_ratio_numerator);\n         // The initial MCOP should = 17 satoshi SMARTBIT / 400 satoshi CORE / (1100 / 1000)\n         //                         = 17 satoshi SMARTBIT / 400 satoshi CORE * (1000 / 1100)\n         //                         = 17 satoshi SMARTBIT / 4 satoshi CORE * (10 / 1100)\n         //                         = 17 satoshi SMARTBIT / 4 satoshi CORE * (1 / 110)\n         //                         = 17 satoshi SMARTBIT / 440 satoshi CORE\n         //                        ~= 0.0386 satoshi SMARTBIT / satoshi CORE\n         BOOST_CHECK_EQUAL(planned_initial_mcop.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(planned_initial_mcop.quote.amount.value, 440); // satoshi CORE\n\n         // Pre-calculate the planned final margin call order price (MCOP)\n         const uint16_t final_ratio_numerator = mssr - final_mcfr;\n         BOOST_REQUIRE_EQUAL(final_ratio_numerator, 1450);\n         const price planned_final_mcop = intermediate_feed_price * ratio_type(GRAPHENE_COLLATERAL_RATIO_DENOM,\n                                                                               final_ratio_numerator);\n         // The final MCOP should   = 17 satoshi SMARTBIT / 400 satoshi CORE / (1450 / 1000)\n         //                         = 17 satoshi SMARTBIT / 400 satoshi CORE * (1000 / 1450)\n         //                         = 17 satoshi SMARTBIT / 4 satoshi CORE * (10 / 1450)\n         //                         = 17 satoshi SMARTBIT / 4 satoshi CORE * (1 / 145)\n         //                         = 17 satoshi SMARTBIT / 580 satoshi CORE\n         //                        ~= 0.0293 satoshi SMARTBIT / satoshi CORE\n         BOOST_CHECK_EQUAL(planned_final_mcop.base.amount.value, 17); // satoshi SMARTBIT\n         BOOST_CHECK_EQUAL(planned_final_mcop.quote.amount.value, 580); // satoshi CORE\n\n\n         //////\n         // 2. Publish feed\n         //////\n         price_feed current_feed;\n         current_feed.settlement_price = initial_feed_price;\n         current_feed.maintenance_collateral_ratio = 1750; // MCR of 1.75x\n         current_feed.maximum_short_squeeze_ratio = mssr; // MSSR of 1.50x\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // 3. (Order 1: Limit order) Alice places a **\"large\"** limit order to sell SMARTBIT.\n         //////\n         // Alice borrows SMARTBIT\n         call_order_id_type alice_call_id = (*borrow(alice, alice_initial_smart, alice_initial_core)).id;\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 500 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n\n         // Alice offer to sell the SMARTBIT\n         const asset alice_debt_to_sell = smartbit.amount(500 * SMARTBIT_UNIT);\n         const asset alice_collateral_to_buy = core.amount(1500 * CORE_UNIT); // 150,000,000 satoshi CORE\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice_id, alice_debt_to_sell,\n                                                                            alice_collateral_to_buy);\n\n         // Check the new price relative to the planned initial and final MCOP\n         // The implied resulting price = 5,000,000 satoshi SMARTBIT / 150,000,000 satoshi CORE\n         //                             = 1 satoshi SMARTBIT / 30 satoshi CORE\n         //                            ~= 0.033 satoshi SMARTBIT / satoshi CORE\n         const price alice_order_price_implied = price(smartbit.amount(1), core.amount(30));\n         BOOST_REQUIRE(alice_sell_op.get_price() == alice_order_price_implied);\n         // Alice's offer price should be less than the intermediate MCOP\n         BOOST_REQUIRE(alice_sell_op.get_price() < planned_initial_mcop);\n         // Alice's offer price should be more than the final MCOP\n         BOOST_REQUIRE(alice_sell_op.get_price() > planned_final_mcop);\n\n         // Submit the limit order\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         // asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Alice should have no balance\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n\n\n\n         //////\n         // 4. (Order 2: Call order) Bob borrows a **\"small\"** amount of SMARTBIT into existence.\n         //    Bob retains the asset in his own balances, or transfers it, or sells it is not critical\n         //    because his debt position is what will be tracked.\n         //////\n         const asset bob_initial_debt_smart = bob_initial_smart;\n         const asset bob_initial_debt_collateral = bob_initial_core;\n         call_order_id_type bob_call_id = (*borrow(bob, bob_initial_debt_smart, bob_initial_debt_collateral)).id;\n\n         // Bobs's balances should reflect that CORE was used to create SMARTBIT\n         BOOST_CHECK_EQUAL(get_balance(bob_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, core_id), 0);\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement()); // No global settlement\n         const price bob_initial_cr = bob_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK(bob_initial_cr == expected_bob_initial_cr);\n         BOOST_CHECK_EQUAL(bob_initial_cr.base.amount.value, 80000000); // Collateral of 80,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(bob_initial_cr.quote.amount.value, 2000000); // Debt of 2,000,000 satoshi SMARTBIT\n\n         // Alice's balances should not have changed\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice_id, core_id), 0 * CORE_UNIT);\n\n         // Alice should not have been margin called\n         price alice_initial_cr = alice_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(alice_initial_cr.base.amount.value, 400000000); // Collateral of 400,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(alice_initial_cr.quote.amount.value, 5000000); // Debt of 5,000,000 satoshi SMARTBIT\n\n         //////\n         // Bob transfers his SMARTBIT to Charlie to clarify the accounting\n         //////\n         transfer(bob_id, charlie_id, bob_initial_smart);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, smartbit_id), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, core_id), 0 * CORE_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, core_id), 0 * CORE_UNIT);\n\n\n         //////\n         // 5. The feed price indicates that the collateral drops enough to trigger a margin call\n         //    **but** the margin call order price (denominated in debt/collateral) is less than\n         //    than Alice's limit order price, resulting in no match.\n         //////\n         current_feed.settlement_price = intermediate_feed_price;\n         publish_feed(smartbit, feedproducer_id(db), current_feed);\n         // Confirm the updated feed\n         BOOST_CHECK(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n         // Confirm no global settlement\n         BOOST_CHECK(!smartbit.bitasset_data(db).has_settlement());\n         // Verify the margin call order price is as planned\n         BOOST_CHECK(smartbit_id(db).bitasset_data(db).current_feed.margin_call_order_price(initial_mcfr)\n                     == planned_initial_mcop);\n\n         // Alice's limit order should be open\n         BOOST_CHECK(db.find(alice_order_id));\n\n         // Alice's limit order should not be affected\n         BOOST_CHECK_EQUAL(alice_order_id(db).amount_for_sale().amount.value,\n                           alice_debt_to_sell.amount.value);\n\n         // Bob's debt position should be open\n         BOOST_CHECK(db.find(bob_call_id));\n\n         // Bob's debt to the blockchain should not have changed\n         BOOST_CHECK_EQUAL(bob_call_id(db).debt.value, bob_initial_smart.amount.value);\n         BOOST_CHECK_EQUAL(bob_call_id(db).collateral.value, bob_initial_core.amount.value);\n\n         // Bob's balances should not have changed\n         BOOST_CHECK_EQUAL(get_balance(bob_id, smartbit_id), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id, core_id), 0 * CORE_UNIT);\n\n\n         //////\n         // 6. The asset owner reduces the MCFR enough such that Alice's offer price SHOULD overlap\n         //    with the margin call order price.\n         //    Alice's limit order should be matched against Bob's \"activated\" call order.\n         //    **Alice should be charged as a maker, and Bob as a taker.**\n         //    Alice's limit order should be partially filled,\n         //     but Bob's order should be completely filled and removed from the book.\n         //////\n         asset_update_bitasset_operation uop;\n         uop.issuer = smartissuer_id;\n         uop.asset_to_update = smartbit_id;\n         uop.new_options = smartbit_id(db).bitasset_data(db).options;\n         uop.new_options.extensions.value.margin_call_fee_ratio = final_mcfr;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx);\n\n         // Check MCFR is updated\n         BOOST_CHECK(smartbit_id(db).bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n         BOOST_CHECK_EQUAL(*smartbit_id(db).bitasset_data(db).options.extensions.value.margin_call_fee_ratio,\n                           final_mcfr);\n\n         // Verify the margin call order price is as planned\n         BOOST_CHECK(smartbit_id(db).bitasset_data(db).current_feed.margin_call_order_price(final_mcfr)\n                     == planned_final_mcop);\n\n         //////\n         // Bob's margin call should have been matched with Alice's limit order\n         //////\n\n         // The margin call of Bob's position should have closed the debt of bob_initial_smart\n         // Bob's margin call should been matched against Alice's limit order\n         // Bob's debt position should have paid collateral = bob_initial_smart / limit_order_price\n         // 200 SMARTBIT / (1 satoshi SMARTBIT / 30 satoshi CORE)\n         // = 2,000,000 satoshi SMARTBIT / (1 satoshi SMARTBIT / 30 satoshi CORE)\n         // = 2,000,000 satoshi CORE / (1 / 30)\n         // = 60,000,000 satoshi CORE\n         const asset expected_margin_call_from_bob_debt_core = core.amount(60000000);\n\n         // Bob's margin call fee, which is paid in collateral, should be charged as a taker\n         // The margin call fee debt = filled_debt * MCFR/(MSSR-MCFR) / limit_order_price\n         // 200 SMARTBIT * (50 / (1500 - 50)) / (1 satoshi SMARTBIT / 30 satoshi CORE)\n         // = 2,000,000 satoshi SMARTBIT * (50 / 1450) / (1 satoshi SMARTBIT / 30 satoshi CORE)\n         // = 2,000,000 satoshi CORE * (1 / 29) * (30 / 1)\n         // = 2068965.51724 satoshi CORE rounded up to 2068966 satoshi CORE\n         const asset expected_margin_call_fee_from_bob_debt_core = core.amount(2068966);\n\n         // The balance of Bob's debt position\n         const asset expected_return_from_bob_debt_core = bob_initial_core\n                                                          - expected_margin_call_from_bob_debt_core\n                                                          - expected_margin_call_fee_from_bob_debt_core;\n\n         // Check Bob's debt position is closed\n         BOOST_CHECK(!db.find(bob_call_id));\n\n         // Check Bob's balances\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), expected_return_from_bob_debt_core.amount.value);\n\n         // Charlie's balances should not have changed\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, smartbit_id), 200 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(charlie_id, core_id), 0 * CORE_UNIT);\n\n         // Alice's balances should have changed because her limit order was partially filled by the margin call\n         BOOST_CHECK_EQUAL(get_balance(alice_id(db), smartbit_id(db)), 0 * SMARTBIT_UNIT);\n         BOOST_CHECK_EQUAL(get_balance(alice_id, core_id), expected_margin_call_from_bob_debt_core.amount.value);\n\n         // Check Alice's debt\n         // Alice's debt position should not be NOT closed\n         BOOST_CHECK(db.find(alice_call_id));\n         // Alice's debt should NOT have changed because its CR > MCR\n         alice_initial_cr = alice_call_id(db).collateralization(); // Units of collateral / debt\n         BOOST_CHECK_EQUAL(alice_initial_cr.base.amount.value, 400000000); // Collateral of 400,000,000 satoshi CORE\n         BOOST_CHECK_EQUAL(alice_initial_cr.quote.amount.value, 5000000); // Debt of 5,000,000 satoshi SMARTBIT\n\n         // Check Alice's limit order\n         // The amount of smart asset available for sale should be reduced by the amount paid to Bob's margin call\n         limit_order_object alice_limit_order = alice_order_id(db);\n         asset expected_alice_remaining_smart_for_sale = alice_debt_to_sell - bob_initial_debt_smart;\n         asset expected_alice_remaining_core_to_receive =\n                 alice_collateral_to_buy - expected_margin_call_from_bob_debt_core;\n         BOOST_CHECK(alice_limit_order.amount_for_sale() == expected_alice_remaining_smart_for_sale);\n         BOOST_CHECK(alice_limit_order.amount_to_receive() == expected_alice_remaining_core_to_receive);\n\n         // Check the asset owner's accumulated asset fees\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_fees.value, 0);\n         BOOST_CHECK_EQUAL(smartbit.dynamic_asset_data_id(db).accumulated_collateral_fees.value,\n                           expected_margin_call_fee_from_bob_debt_core.amount.value);\n\n         // Check the fee of the fill operations for Alice and Bob\n         generate_block(); // To trigger db_notify() and record pending operations into histories\n         graphene::app::history_api hist_api(app);\n         vector<operation_history_object> histories;\n         const int fill_order_op_id = operation::tag<fill_order_operation>::value;\n\n         // Check Alice's history\n         histories = hist_api.get_account_history_operations(\n                 \"alice\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Alice's fill order for her limit order should have zero fee\n         fill_order_operation alice_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(alice_fill_op.fee == asset(0));\n         // Alice's fill order's fill price should equal the expected match price\n         // Alice's alice_order_price_implied differs slightly from alice_sell_op.get_price()\n         // due to rounding in this test while creating the parameters for the limit order\n         const price expected_match_price = alice_sell_op.get_price();\n         BOOST_CHECK(alice_fill_op.fill_price == expected_match_price);\n\n         // Check Bob's history\n         histories = hist_api.get_account_history_operations(\n                 \"bob\", fill_order_op_id, operation_history_id_type(), operation_history_id_type(), 100);\n         // There should be one fill order operation\n         BOOST_CHECK_EQUAL(histories.size(), 1);\n         // Bob's fill order for his margin call should have a fee equal to the margin call fee\n         fill_order_operation bob_fill_op = histories.front().op.get<fill_order_operation>();\n         BOOST_CHECK(bob_fill_op.fee == expected_margin_call_fee_from_bob_debt_core);\n         // Bob's fill order's fill price should equal the expected match price\n         BOOST_CHECK(bob_fill_op.fill_price == expected_match_price);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test the ability to create and update assets with a margin call fee ratio (MCFR) before HARDFORK_CORE_BSIP74_TIME\n    *\n    *\n    * Before HARDFORK_CORE_BSIP74_TIME\n    *\n    * 1. Asset owner fails to create the smart coin called USDBIT with a MCFR\n    * 2. Asset owner fails to create the smart coin called USDBIT with a MCFR in a proposal\n    * 3. Asset owner succeeds to create the smart coin called USDBIT without a MCFR\n    *\n    * 4. Asset owner fails to update the smart coin with a MCFR\n    * 5. Asset owner fails to update the smart coin with a MCFR in a proposal\n    *\n    *\n    * 6. Activate HARDFORK_CORE_BSIP74_TIME\n    *\n    *\n    * After HARDFORK_CORE_BSIP74_TIME\n    *\n    * 7. Asset owner succeeds to create the smart coin called CNYBIT with a MCFR\n    * 8. Asset owner succeeds to create the smart coin called RUBBIT with a MCFR in a proposal\n    *\n    * 9. Asset owner succeeds to update the smart coin called CNYBIT with a MCFR\n    * 10. Asset owner succeeds to update the smart coin called RUBBIT with a MCFR in a proposal\n    *\n    * 11. Asset owner succeeds to create the smart coin called YENBIT without a MCFR\n    * 12. Asset owner succeeds to update the smart coin called RUBBIT without a MCFR in a proposal\n    */\n   BOOST_AUTO_TEST_CASE(prevention_before_hardfork_test) {\n      try {\n         ///////\n         // Initialize the scenario\n         ///////\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Create actors\n         ACTORS((assetowner));\n\n         // CORE asset exists by default\n         asset_object core = asset_id_type()(db);\n         const asset_id_type core_id = core.id;\n\n         // Fund actors\n         uint64_t initial_balance_core = 10000000;\n         transfer(committee_account, assetowner_id, asset(initial_balance_core));\n\n         // Confirm before hardfork activation\n         BOOST_CHECK(db.head_block_time() < HARDFORK_CORE_BSIP74_TIME);\n\n\n         ///////\n         // 1. Asset owner fails to create the smart coin called bitUSD with a MCFR\n         ///////\n         const uint16_t market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const optional<uint16_t> icr_opt = {}; // Initial collateral ratio\n         const uint16_t mcfr_5 = 50; // 5% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         optional<uint16_t> mcfr_opt = mcfr_5;\n\n         // Attempt to create the smart asset with a MCFR\n         // The attempt should fail because it is before HARDFORK_CORE_BSIP74_TIME\n         {\n            const asset_create_operation create_op = make_bitasset(\"USDBIT\", assetowner_id, market_fee_percent,\n                                                                   charge_market_fee, 4, core_id,\n                                                                   GRAPHENE_MAX_SHARE_SUPPLY, icr_opt, mcfr_opt);\n            trx.clear();\n            trx.operations.push_back(create_op);\n            sign(trx, assetowner_private_key);\n            REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP74\");\n         }\n\n         ///////\n         // 2. Asset owner fails to create the smart coin called bitUSD with a MCFR in a proposal\n         ///////\n         {\n            const asset_create_operation create_op = make_bitasset(\"USDBIT\", assetowner_id, market_fee_percent,\n                                                                   charge_market_fee, 4, core_id,\n                                                                   GRAPHENE_MAX_SHARE_SUPPLY, icr_opt, mcfr_opt);\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(create_op);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP74\");\n         }\n\n\n         ///////\n         // 3. Asset owner succeeds to create the smart coin called bitUSD without a MCFR\n         ///////\n         const optional<uint16_t> mcfr_null_opt = {};\n         {\n            const asset_create_operation create_op = make_bitasset(\"USDBIT\", assetowner_id, market_fee_percent,\n                                                                   charge_market_fee, 4, core_id,\n                                                                   GRAPHENE_MAX_SHARE_SUPPLY, icr_opt, mcfr_null_opt);\n\n            trx.clear();\n            trx.operations.push_back(create_op);\n            sign(trx, assetowner_private_key);\n            PUSH_TX(db, trx); // No exception should be thrown\n         }\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const asset_object &bitusd = get_asset(\"USDBIT\");\n         core = core_id(db);\n\n         // The force MCFR should not be set\n         BOOST_CHECK(!bitusd.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n\n\n         ///////\n         // 4. Asset owner fails to update the smart coin with a MCFR\n         ///////\n         const uint16_t mcfr_3 = 30; // 3% MCFR (BSIP74)\n         asset_update_bitasset_operation uop;\n         uop.issuer = assetowner_id;\n         uop.asset_to_update = bitusd.get_id();\n         uop.new_options = bitusd.bitasset_data(db).options;\n         uop.new_options.extensions.value.margin_call_fee_ratio = mcfr_3;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, assetowner_private_key);\n         REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP74\");\n\n         // The MCFR should not be set\n         BOOST_CHECK(!bitusd.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n\n\n         ///////\n         // 5. Asset owner fails to update the smart coin with a MCFR in a proposal\n         ///////\n         {\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx), \"cannot be set before Hardfork BSIP74\");\n\n            // The MCFR should not be set\n            BOOST_CHECK(!bitusd.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n         }\n\n\n         ///////\n         // 6. Activate HARDFORK_CORE_BSIP74_TIME\n         ///////\n         BOOST_CHECK(db.head_block_time() < HARDFORK_CORE_BSIP74_TIME); // Confirm still before hardfork activation\n         BOOST_TEST_MESSAGE(\"Advancing past Hardfork BSIP74\");\n         generate_blocks(HARDFORK_CORE_BSIP74_TIME);\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n\n         ///////\n         // 7. Asset owner succeeds to create the smart coin called CNYBIT with a MCFR\n         ///////\n         {\n            mcfr_opt = mcfr_3;\n            const asset_create_operation create_op = make_bitasset(\"CNYBIT\", assetowner_id, market_fee_percent,\n                                                                   charge_market_fee, 4, core_id,\n                                                                   GRAPHENE_MAX_SHARE_SUPPLY, icr_opt, mcfr_opt);\n\n            trx.clear();\n            trx.operations.push_back(create_op);\n            sign(trx, assetowner_private_key);\n            PUSH_TX(db, trx); // No exception should be thrown\n         }\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bitcny = get_asset(\"CNYBIT\");\n\n         // The MCFR should be set\n         BOOST_CHECK(bitcny.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n         BOOST_CHECK_EQUAL(*bitcny.bitasset_data(db).options.extensions.value.margin_call_fee_ratio, mcfr_3);\n\n\n         ///////\n         // 8. Asset owner succeeds to create the smart coin called RUBBIT with a MCFR in a proposal\n         ///////\n         const uint16_t mcfr_1 = 10; // 1% expressed in terms of GRAPHENE_COLLATERAL_RATIO_DENOM\n         {\n            // Create the proposal\n            mcfr_opt = mcfr_1;\n            const asset_create_operation create_op = make_bitasset(\"RUBBIT\", assetowner_id, market_fee_percent,\n                                                                   charge_market_fee, 4, core_id,\n                                                                   GRAPHENE_MAX_SHARE_SUPPLY, icr_opt, mcfr_opt);\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(create_op);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            processed_transaction processed = PUSH_TX(db, trx);\n\n\n            // Approve the proposal\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = assetowner_id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(assetowner_id);\n            trx.clear();\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, assetowner_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n\n            // Advance to the activation of the proposal\n            generate_blocks(cop.expiration_time);\n            set_expiration(db, trx);\n         }\n         const auto &bitrub = get_asset(\"RUBBIT\");\n\n         // The MCFR should be set\n         BOOST_CHECK(bitrub.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n         BOOST_CHECK_EQUAL(*bitrub.bitasset_data(db).options.extensions.value.margin_call_fee_ratio, mcfr_1);\n\n\n         ///////\n         // 9. Asset owner succeeds to update the smart coin called CNYBIT with a MCFR\n         ///////\n         uop = asset_update_bitasset_operation();\n         uop.issuer = assetowner_id;\n         uop.asset_to_update = bitcny.get_id();\n         uop.new_options = bitcny.bitasset_data(db).options;\n         uop.new_options.extensions.value.margin_call_fee_ratio = mcfr_5;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, assetowner_private_key);\n         PUSH_TX(db, trx);\n\n         // The MCFR should be set\n         BOOST_CHECK(bitcny.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n         BOOST_CHECK_EQUAL(*bitcny.bitasset_data(db).options.extensions.value.margin_call_fee_ratio, mcfr_5);\n\n\n         ///////\n         // 10. Asset owner succeeds to update the smart coin called RUBBIT with a MCFR in a proposal\n         ///////\n         {\n            // Create the proposal\n            uop = asset_update_bitasset_operation();\n            uop.issuer = assetowner_id;\n            uop.asset_to_update = bitrub.get_id();\n            uop.new_options = bitrub.bitasset_data(db).options;\n            uop.new_options.extensions.value.margin_call_fee_ratio = mcfr_5;\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            processed_transaction processed = PUSH_TX(db, trx);\n\n\n            // Approve the proposal\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = assetowner_id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(assetowner_id);\n            trx.clear();\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, assetowner_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n            // Advance to the activation of the proposal\n            generate_blocks(cop.expiration_time);\n            set_expiration(db, trx);\n         }\n\n         // The MCFR should be set\n         BOOST_CHECK(bitrub.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n         BOOST_CHECK_EQUAL(*bitrub.bitasset_data(db).options.extensions.value.margin_call_fee_ratio, mcfr_5);\n\n\n         ///////\n         // 11. Asset owner succeeds to create the smart coin called YENBIT without a MCFR\n         ///////\n         {\n            const asset_create_operation create_op = make_bitasset(\"YENBIT\", assetowner_id, market_fee_percent,\n                                                                   charge_market_fee, 4, core_id,\n                                                                   GRAPHENE_MAX_SHARE_SUPPLY, icr_opt, mcfr_null_opt);\n\n            trx.clear();\n            trx.operations.push_back(create_op);\n            sign(trx, assetowner_private_key);\n            PUSH_TX(db, trx); // No exception should be thrown\n         }\n\n         generate_block();\n         set_expiration(db, trx);\n         trx.clear();\n\n         const auto &bityen = get_asset(\"YENBIT\");\n\n         // The MCFR should be set\n         BOOST_CHECK(!bityen.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n\n\n         ///////\n         // 12. Asset owner succeeds to update the smart coin called RUBBIT without a MCFR in a proposal\n         ///////\n         {\n            // Create the proposal\n            uop = asset_update_bitasset_operation();\n            uop.issuer = assetowner_id;\n            uop.asset_to_update = bitrub.get_id();\n            uop.new_options = bitrub.bitasset_data(db).options;\n            uop.new_options.extensions.value.margin_call_fee_ratio = mcfr_null_opt;\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.clear();\n            trx.operations.push_back(cop);\n            // sign(trx, assetowner_private_key);\n            processed_transaction processed = PUSH_TX(db, trx);\n\n\n            // Approve the proposal\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = assetowner_id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(assetowner_id);\n            trx.clear();\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, assetowner_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n            // Advance to the activation of the proposal\n            generate_blocks(cop.expiration_time);\n            set_expiration(db, trx);\n         }\n\n         // The MCFR should NOT be set\n         BOOST_CHECK(!bitrub.bitasset_data(db).options.extensions.value.margin_call_fee_ratio.valid());\n\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/market_fee_sharing_tests.cpp",
    "content": "#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/app/database_api.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nnamespace fc\n{\n   template<typename Ch, typename T>\n   std::basic_ostream<Ch>& operator<<(std::basic_ostream<Ch>& os, safe<T> const& sf)\n   {\n      os << sf.value;\n      return os;\n   }\n}\n\nstruct reward_database_fixture : database_fixture\n{\n   using whitelist_market_fee_sharing_t = fc::optional<flat_set<account_id_type>>;\n\n   reward_database_fixture()\n      : database_fixture()\n   {\n   }\n\n   void update_asset( const account_id_type& issuer_id,\n                      const fc::ecc::private_key& private_key,\n                      const asset_id_type& asset_id,\n                      uint16_t reward_percent,\n                      const whitelist_market_fee_sharing_t &whitelist_market_fee_sharing = whitelist_market_fee_sharing_t{},\n                      const flat_set<account_id_type> &blacklist = flat_set<account_id_type>())\n   {\n      asset_update_operation op;\n      op.issuer = issuer_id;\n      op.asset_to_update = asset_id;\n      op.new_options = asset_id(db).options;\n      op.new_options.extensions.value.reward_percent = reward_percent;\n      op.new_options.extensions.value.whitelist_market_fee_sharing = whitelist_market_fee_sharing;\n      op.new_options.blacklist_authorities = blacklist;\n\n      signed_transaction tx;\n      tx.operations.push_back( op );\n      db.current_fee_schedule().set_fee( tx.operations.back() );\n      set_expiration( db, tx );\n      sign( tx, private_key );\n      PUSH_TX( db, tx );\n   }\n\n   void asset_update_blacklist_authority(const account_id_type& issuer_id,\n                                         const asset_id_type& asset_id,\n                                         const account_id_type& authority_account_id,\n                                         const fc::ecc::private_key& issuer_private_key)\n   {\n      asset_update_operation uop;\n      uop.issuer = issuer_id;\n      uop.asset_to_update = asset_id;\n      uop.new_options = asset_id(db).options;\n      uop.new_options.blacklist_authorities.insert(authority_account_id);\n\n      signed_transaction tx;\n      tx.operations.push_back( uop );\n      db.current_fee_schedule().set_fee( tx.operations.back() );\n      set_expiration( db, tx );\n      sign( tx, issuer_private_key );\n      PUSH_TX( db, tx );\n   }\n\n   void add_account_to_blacklist(const account_id_type& authorizing_account_id,\n                                 const account_id_type& blacklisted_account_id,\n                                 const fc::ecc::private_key& authorizing_account_private_key)\n   {\n      account_whitelist_operation wop;\n      wop.authorizing_account = authorizing_account_id;\n      wop.account_to_list = blacklisted_account_id;\n      wop.new_listing = account_whitelist_operation::black_listed;\n\n      signed_transaction tx;\n      tx.operations.push_back( wop );\n      db.current_fee_schedule().set_fee( tx.operations.back() );\n      set_expiration( db, tx );\n      sign( tx, authorizing_account_private_key );\n      PUSH_TX( db, tx);\n   }\n\n   void generate_blocks_past_hf1774()\n   {\n      generate_blocks( HARDFORK_1774_TIME );\n      generate_block();\n      set_expiration(db, trx);\n   }\n\n   void generate_blocks_past_hf1800()\n   {\n      database_fixture::generate_blocks( HARDFORK_CORE_1800_TIME );\n      database_fixture::generate_block();\n      set_expiration(db, trx);\n   }\n\n   asset core_asset(int64_t x )\n   {\n       return asset( x*core_precision );\n   };\n\n   const share_type core_precision = asset::scaled_precision( asset_id_type()(db).precision );\n\n   void create_vesting_balance_object(const account_id_type& account_id, vesting_balance_type balance_type )\n   {\n      db.create<vesting_balance_object>([&account_id, balance_type] (vesting_balance_object &vbo) {\n         vbo.owner = account_id;\n         vbo.balance_type = balance_type;\n      });\n   };\n};\n\nBOOST_FIXTURE_TEST_SUITE( fee_sharing_tests, reward_database_fixture )\n\nBOOST_AUTO_TEST_CASE(cannot_create_asset_with_reward_percent_of_100_before_hf1774)\n{\n   try\n   {\n      ACTOR(issuer);\n\n      uint16_t reward_percent = GRAPHENE_100_PERCENT + 1; // 100.01%\n      flat_set<account_id_type> whitelist = {issuer_id};\n      price price(asset(1, asset_id_type(1)), asset(1));\n      uint16_t market_fee_percent = 100;\n\n      additional_asset_options_t options;\n      options.value.reward_percent = reward_percent;\n      options.value.whitelist_market_fee_sharing = whitelist;\n\n      GRAPHENE_CHECK_THROW(create_user_issued_asset(\"USD\",\n                                                    issuer,\n                                                    charge_market_fee,\n                                                    price,\n                                                    2,\n                                                    market_fee_percent,\n                                                    options),\n                           fc::assert_exception);\n\n      reward_percent = GRAPHENE_100_PERCENT; // 100%\n      options.value.reward_percent = reward_percent;\n      GRAPHENE_CHECK_THROW(create_user_issued_asset(\"USD\",\n                                                    issuer,\n                                                    charge_market_fee,\n                                                    price,\n                                                    2,\n                                                    market_fee_percent,\n                                                    options),\n                           fc::assert_exception);\n\n      reward_percent = GRAPHENE_100_PERCENT - 1; // 99.99%\n      options.value.reward_percent = reward_percent;\n      asset_object usd_asset = create_user_issued_asset(\"USD\",\n                                                        issuer,\n                                                        charge_market_fee,\n                                                        price,\n                                                        2,\n                                                        market_fee_percent,\n                                                        options);\n\n      additional_asset_options usd_options = usd_asset.options.extensions.value;\n      BOOST_CHECK_EQUAL(reward_percent, *usd_options.reward_percent);\n      BOOST_CHECK(whitelist == *usd_options.whitelist_market_fee_sharing);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(cannot_set_reward_percent_to_100_before_hf1774)\n{\n   try\n   {\n      ACTOR(issuer);\n\n      asset_object usd_asset = create_user_issued_asset(\"USD\", issuer, charge_market_fee);\n\n      uint16_t reward_percent = GRAPHENE_100_PERCENT + 1; // 100.01%\n      flat_set<account_id_type> whitelist = {issuer_id};\n      GRAPHENE_CHECK_THROW(\n                  update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), reward_percent, whitelist),\n                  fc::assert_exception );\n\n      reward_percent = GRAPHENE_100_PERCENT; // 100%\n      GRAPHENE_CHECK_THROW(\n                  update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), reward_percent, whitelist),\n                  fc::assert_exception );\n\n      reward_percent = GRAPHENE_100_PERCENT - 1; // 99.99%\n      update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), reward_percent, whitelist);\n\n      asset_object updated_asset = usd_asset.get_id()(db);\n      additional_asset_options options = updated_asset.options.extensions.value;\n      BOOST_CHECK_EQUAL(reward_percent, *options.reward_percent);\n      BOOST_CHECK(whitelist == *options.whitelist_market_fee_sharing);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(create_asset_with_reward_percent_of_100_after_hf1774)\n{\n   try\n   {\n      generate_blocks_past_hf1774();\n\n      ACTOR(issuer);\n\n      uint16_t reward_percent = GRAPHENE_100_PERCENT; // 100.00%\n      flat_set<account_id_type> whitelist = {issuer_id};\n      price price(asset(1, asset_id_type(1)), asset(1));\n      uint16_t market_fee_percent = 100;\n\n      additional_asset_options_t options;\n      options.value.reward_percent = reward_percent;\n      options.value.whitelist_market_fee_sharing = whitelist;\n\n      asset_object usd_asset = create_user_issued_asset(\"USD\",\n                                                        issuer,\n                                                        charge_market_fee,\n                                                        price,\n                                                        2,\n                                                        market_fee_percent,\n                                                        options);\n\n      additional_asset_options usd_options = usd_asset.options.extensions.value;\n      BOOST_CHECK_EQUAL(reward_percent, *usd_options.reward_percent);\n      BOOST_CHECK(whitelist == *usd_options.whitelist_market_fee_sharing);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(set_reward_percent_to_100_after_hf1774)\n{\n   try\n   {\n      ACTOR(issuer);\n\n      asset_object usd_asset = create_user_issued_asset(\"USD\", issuer, charge_market_fee); // make a copy\n\n      generate_blocks_past_hf1774();\n\n      uint16_t reward_percent = GRAPHENE_100_PERCENT; // 100.00%\n      flat_set<account_id_type> whitelist = {issuer_id};\n      update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), reward_percent, whitelist);\n\n      additional_asset_options options = usd_asset.get_id()(db).options.extensions.value;\n      BOOST_CHECK_EQUAL(reward_percent, *options.reward_percent);\n      BOOST_CHECK(whitelist == *options.whitelist_market_fee_sharing);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(asset_rewards_test)\n{\n   try\n   {\n      // handle small percentages\n      generate_blocks(HARDFORK_453_TIME + 10);\n      set_expiration(db, trx);\n\n      ACTORS((registrar)(alicereferrer)(bobreferrer)(izzy)(jill));\n\n      auto register_account = [&](const string& name, const account_object& referrer) -> const account_object&\n      {\n         uint16_t referrer_percent = GRAPHENE_1_PERCENT;\n         fc::ecc::private_key _private_key = generate_private_key(name);\n         public_key_type _public_key = _private_key.get_public_key();\n         return create_account(name, registrar, referrer, referrer_percent, _public_key);\n      };\n\n      // Izzy issues asset to Alice\n      // Jill issues asset to Bob\n      // Alice and Bob trade in the market and pay fees\n      // Bob's and Alice's referrers can get reward\n      upgrade_to_lifetime_member(registrar);\n      upgrade_to_lifetime_member(alicereferrer);\n      upgrade_to_lifetime_member(bobreferrer);\n\n      auto alice = register_account(\"alice\", alicereferrer);\n      auto bob = register_account(\"bob\", bobreferrer);\n\n      transfer( committee_account, alice.id, core_asset(1000000) );\n      transfer( committee_account, bob.id, core_asset(1000000) );\n      transfer( committee_account, izzy_id, core_asset(1000000) );\n      transfer( committee_account, jill_id, core_asset(1000000) );\n\n      constexpr auto izzycoin_reward_percent = 10*GRAPHENE_1_PERCENT;\n      constexpr auto jillcoin_reward_percent = 20*GRAPHENE_1_PERCENT;\n\n      constexpr auto izzycoin_market_percent = 10*GRAPHENE_1_PERCENT;\n      constexpr auto jillcoin_market_percent = 20*GRAPHENE_1_PERCENT;\n\n      asset_id_type izzycoin_id = create_bitasset( \"IZZYCOIN\", izzy_id, izzycoin_market_percent ).id;\n      asset_id_type jillcoin_id = create_bitasset( \"JILLCOIN\", jill_id, jillcoin_market_percent ).id;\n\n      update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent);\n      update_asset(jill_id, jill_private_key, jillcoin_id, jillcoin_reward_percent);\n\n      const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision );\n      const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision );\n\n      auto _izzy = [&]( int64_t x ) -> asset\n      {   return asset( x*izzy_prec, izzycoin_id );   };\n      auto _jill = [&]( int64_t x ) -> asset\n      {   return asset( x*jill_prec, jillcoin_id );   };\n\n      update_feed_producers( izzycoin_id(db), { izzy_id } );\n      update_feed_producers( jillcoin_id(db), { jill_id } );\n\n      // Izzycoin is worth 100 BTS\n      price_feed feed;\n      feed.settlement_price = price( _izzy(1), core_asset(100) );\n      feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      publish_feed( izzycoin_id(db), izzy, feed );\n\n      // Jillcoin is worth 30 BTS\n      feed.settlement_price = price( _jill(1), core_asset(30) );\n      feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n      publish_feed( jillcoin_id(db), jill, feed );\n\n      enable_fees();\n\n      // Alice and Bob create some coins\n      borrow( alice.id, _izzy( 1500), core_asset( 600000) );\n      borrow( bob.id, _jill(2000), core_asset(180000) );\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice.id, _izzy(1000), _jill(1500) ); // Alice is willing to sell her 1000 Izzy's for 1.5 Jill\n      create_sell_order( bob.id, _jill(1500), _izzy(1000) );   // Bob is buying up to 1500 Izzy's for up to 0.6 Jill\n\n      // 1000 Izzys and 1500 Jills are matched, so the fees should be\n      //   100 Izzy (10%) and 300 Jill (20%).\n      // Bob's and Alice's referrers should get rewards\n      share_type bob_refereer_reward = get_market_fee_reward( bob.referrer, izzycoin_id );\n      share_type alice_refereer_reward = get_market_fee_reward( alice.referrer, jillcoin_id );\n\n      // Bob's and Alice's registrars should get rewards\n      share_type bob_registrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id );\n      share_type alice_registrar_reward = get_market_fee_reward( alice.registrar, jillcoin_id );\n\n      auto calculate_percent = [](const share_type& value, uint16_t percent)\n      {\n         auto a(value.value);\n         a *= percent;\n         a /= GRAPHENE_100_PERCENT;\n         return a;\n      };\n\n      BOOST_CHECK_GT( bob_refereer_reward, 0 );\n      BOOST_CHECK_GT( alice_refereer_reward, 0 );\n      BOOST_CHECK_GT( bob_registrar_reward, 0 );\n      BOOST_CHECK_GT( alice_registrar_reward, 0 );\n\n      const auto izzycoin_market_fee = calculate_percent(_izzy(1000).amount, izzycoin_market_percent);\n      const auto izzycoin_reward = calculate_percent(izzycoin_market_fee, izzycoin_reward_percent);\n      BOOST_CHECK_EQUAL( izzycoin_reward, bob_refereer_reward + bob_registrar_reward );\n      BOOST_CHECK_EQUAL( calculate_percent(izzycoin_reward, bob.referrer_rewards_percentage), bob_refereer_reward );\n\n      const auto jillcoin_market_fee = calculate_percent(_jill(1500).amount, jillcoin_market_percent);\n      const auto jillcoin_reward = calculate_percent(jillcoin_market_fee, jillcoin_reward_percent);\n      BOOST_CHECK_EQUAL( jillcoin_reward, alice_refereer_reward + alice_registrar_reward );\n      BOOST_CHECK_EQUAL( calculate_percent(jillcoin_reward, alice.referrer_rewards_percentage), alice_refereer_reward );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(asset_claim_reward_test)\n{\n   try\n   {\n      ACTORS((jill)(izzy));\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n\n      upgrade_to_lifetime_member(izzy);\n\n      price price(asset(1, asset_id_type(1)), asset(1));\n      uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT;\n      const asset_object jillcoin = create_user_issued_asset( \"JCOIN\", jill,  charge_market_fee, price, 2, market_fee_percent );\n\n      const account_object alice = create_account(\"alice\", izzy, izzy, 50/*0.5%*/);\n      const account_object bob   = create_account(\"bob\",   izzy, izzy, 50/*0.5%*/);\n\n      // prepare users' balance\n      issue_uia( alice, jillcoin.amount( 20000000 ) );\n\n      transfer( committee_account, alice.get_id(), core_asset(1000) );\n      transfer( committee_account, bob.get_id(),   core_asset(1000) );\n      transfer( committee_account, izzy.get_id(),  core_asset(1000) );\n\n      // update_asset: set referrer percent\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent);\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice, jillcoin.amount(200000), core_asset(1) );\n      create_sell_order( bob, core_asset(1), jillcoin.amount(100000) );\n\n      const int64_t izzy_reward = get_market_fee_reward( izzy, jillcoin );\n      const int64_t izzy_balance = get_balance( izzy, jillcoin );\n\n      BOOST_CHECK_GT(izzy_reward, 0);\n\n      auto claim_reward = [&]( account_object referrer, asset amount_to_claim, fc::ecc::private_key private_key )\n      {\n        vesting_balance_withdraw_operation op;\n        op.vesting_balance = vesting_balance_id_type(0);\n        op.owner = referrer.get_id();\n        op.amount = amount_to_claim;\n\n        signed_transaction tx;\n        tx.operations.push_back( op );\n        db.current_fee_schedule().set_fee( tx.operations.back() );\n        set_expiration( db, tx );\n        sign( tx, private_key );\n        PUSH_TX( db, tx );\n      };\n\n      const int64_t amount_to_claim = 3;\n      claim_reward( izzy, jillcoin.amount(amount_to_claim), izzy_private_key );\n\n      BOOST_CHECK_EQUAL(get_balance( izzy, jillcoin ), izzy_balance + amount_to_claim);\n      BOOST_CHECK_EQUAL(get_market_fee_reward( izzy, jillcoin ), izzy_reward - amount_to_claim);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(create_actors)\n{\n   try\n   {\n      ACTORS((jill)(izzyregistrar)(izzyreferrer)(tempregistrar));\n\n      upgrade_to_lifetime_member(izzyregistrar);\n      upgrade_to_lifetime_member(izzyreferrer);\n      upgrade_to_lifetime_member(tempregistrar);\n\n      price price(asset(1, asset_id_type(1)), asset(1));\n      uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT;\n      const asset_object jillcoin = create_user_issued_asset( \"JCOIN\", jill, charge_market_fee,\n                                                              price, 2, market_fee_percent );\n\n      const account_object alice = create_account(\"alice\", izzyregistrar, izzyreferrer, 50/*0.5%*/);\n      const account_object bob   = create_account(\"bob\",   izzyregistrar, izzyreferrer, 50/*0.5%*/);\n      const account_object old   = create_account(\"old\",   GRAPHENE_TEMP_ACCOUNT(db),\n                                                           GRAPHENE_COMMITTEE_ACCOUNT(db), 50u);\n      const account_object tmp   = create_account(\"tmp\",   tempregistrar,\n                                                           GRAPHENE_TEMP_ACCOUNT(db), 50u);\n\n      // prepare users' balance\n      issue_uia( alice, jillcoin.amount( 20000000 ) );\n\n      transfer( committee_account, alice.get_id(), core_asset(1000) );\n      transfer( committee_account, bob.get_id(),   core_asset(1000) );\n      transfer( committee_account, old.get_id(),   core_asset(1000) );\n      transfer( committee_account, tmp.get_id(),   core_asset(1000) );\n      transfer( committee_account, izzyregistrar.get_id(),  core_asset(1000) );\n      transfer( committee_account, izzyreferrer.get_id(),  core_asset(1000) );\n      transfer( committee_account, tempregistrar.get_id(),  core_asset(1000) );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(fee_shares_between_temp_acc_and_committee_acc_before_hf_1800)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      flat_set<account_id_type> whitelist;\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n\n      GET_ACTOR(alice);\n      GET_ACTOR(old);\n\n      create_sell_order( alice, jillcoin.amount(100000), core_asset(1) );\n      create_sell_order( old, core_asset(1), jillcoin.amount(100000) );\n\n      BOOST_CHECK_GT( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_GT( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n\n   }\n   FC_LOG_AND_RETHROW()\n\n}\n\nBOOST_AUTO_TEST_CASE(fee_do_not_share_between_temp_acc_and_committee_acc_after_hf_1800)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      generate_blocks_past_hf1800();\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      flat_set<account_id_type> whitelist;\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n\n      GET_ACTOR(alice);\n      GET_ACTOR(old);\n\n      create_sell_order( alice, jillcoin.amount(100000), core_asset(1) );\n      create_sell_order( old, core_asset(1), jillcoin.amount(100000) );\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_GT( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n\n   }\n   FC_LOG_AND_RETHROW()\n\n}\n\nBOOST_AUTO_TEST_CASE(fee_shares_to_temp_referrer_before_hf_1800)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      GET_ACTOR(jill);\n      GET_ACTOR(tempregistrar);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      flat_set<account_id_type> whitelist;\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( tempregistrar, jillcoin), 0);\n\n      GET_ACTOR(alice);\n      GET_ACTOR(tmp);\n\n      create_sell_order( alice, jillcoin.amount(100000), core_asset(1) );\n      create_sell_order( tmp, core_asset(1), jillcoin.amount(100000) );\n\n      BOOST_CHECK_GT( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_GT( get_market_fee_reward( tempregistrar, jillcoin), 0);\n\n   }\n   FC_LOG_AND_RETHROW()\n\n}\n\nBOOST_AUTO_TEST_CASE(fee_do_not_share_to_temp_referrer_after_hf_1800)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      generate_blocks_past_hf1800();\n      GET_ACTOR(jill);\n      GET_ACTOR(tempregistrar);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      flat_set<account_id_type> whitelist;\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( tempregistrar, jillcoin), 0);\n\n      GET_ACTOR(alice);\n      GET_ACTOR(tmp);\n\n      create_sell_order( alice, jillcoin.amount(100000), core_asset(1) );\n      create_sell_order( tmp, core_asset(1), jillcoin.amount(100000) );\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( GRAPHENE_TEMP_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_GT( get_market_fee_reward( GRAPHENE_COMMITTEE_ACCOUNT(db), jillcoin), 0);\n      BOOST_CHECK_GT( get_market_fee_reward( tempregistrar, jillcoin), 0);\n\n   }\n   FC_LOG_AND_RETHROW()\n\n}\n\nBOOST_AUTO_TEST_CASE(white_list_is_empty_test)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      flat_set<account_id_type> whitelist;\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      GET_ACTOR(izzyregistrar);\n      GET_ACTOR(izzyreferrer);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 );\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n      // Alice and Bob place orders which match\n      create_sell_order( alice, jillcoin.amount(200000), core_asset(1) );\n      create_sell_order( bob, core_asset(1), jillcoin.amount(100000) );\n\n      const auto izzyregistrar_reward = get_market_fee_reward( izzyregistrar, jillcoin );\n      const auto izzyreferrer_reward = get_market_fee_reward( izzyreferrer, jillcoin );\n      BOOST_CHECK_GT(izzyregistrar_reward , 0);\n      BOOST_CHECK_GT(izzyreferrer_reward , 0);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(white_list_contains_registrar_test)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      GET_ACTOR(izzyregistrar);\n      GET_ACTOR(izzyreferrer);\n      flat_set<account_id_type> whitelist = {jill_id, izzyregistrar_id};\n\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 );\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n      // Alice and Bob place orders which match\n      create_sell_order( alice, jillcoin.amount(200000), core_asset(1) );\n      create_sell_order( bob, core_asset(1), jillcoin.amount(100000) );\n\n      const auto izzyregistrar_reward = get_market_fee_reward( izzyregistrar, jillcoin );\n      const auto izzyreferrer_reward = get_market_fee_reward( izzyreferrer, jillcoin );\n      BOOST_CHECK_GT(izzyregistrar_reward , 0);\n      BOOST_CHECK_GT(izzyreferrer_reward , 0);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(white_list_contains_referrer_test)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      GET_ACTOR(izzyregistrar);\n      GET_ACTOR(izzyreferrer);\n      flat_set<account_id_type> whitelist = {jill_id, izzyreferrer_id};\n\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 );\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n      // Alice and Bob place orders which match\n      create_sell_order( alice, jillcoin.amount(200000), core_asset(1) );\n      create_sell_order( bob, core_asset(1), jillcoin.amount(100000) );\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(white_list_doesnt_contain_registrar_test)\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      GET_ACTOR(alice);\n      flat_set<account_id_type> whitelist = {jill_id, alice_id};\n\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist);\n\n      GET_ACTOR(izzyregistrar);\n      GET_ACTOR(izzyreferrer);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 );\n\n      GET_ACTOR(bob);\n      // Alice and Bob place orders which match\n      create_sell_order( alice, jillcoin.amount(200000), core_asset(1) );\n      create_sell_order( bob, core_asset(1), jillcoin.amount(100000) );\n\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(create_asset_via_proposal_test)\n{\n   try\n   {\n      ACTOR(issuer);\n      price core_exchange_rate(asset(1, asset_id_type(1)), asset(1));\n\n      asset_create_operation create_op;\n      create_op.issuer = issuer.id;\n      create_op.fee = asset();\n      create_op.symbol = \"ASSET\";\n      create_op.common_options.max_supply = 0;\n      create_op.precision = 2;\n      create_op.common_options.core_exchange_rate = core_exchange_rate;\n      create_op.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      create_op.common_options.flags = charge_market_fee;\n\n      additional_asset_options_t options;\n      options.value.reward_percent = 100;\n      options.value.whitelist_market_fee_sharing = flat_set<account_id_type>{issuer_id};\n      create_op.common_options.extensions = std::move(options);;\n\n      const auto& curfees = *db.get_global_properties().parameters.current_fees;\n      const auto& proposal_create_fees = curfees.get<proposal_create_operation>();\n      proposal_create_operation prop;\n      prop.fee_paying_account = issuer_id;\n      prop.proposed_ops.emplace_back( create_op );\n      prop.expiration_time =  db.head_block_time() + fc::days(1);\n      prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n\n      {\n         prop.expiration_time =  db.head_block_time() + fc::days(1);\n         signed_transaction tx;\n         tx.operations.push_back( prop );\n         db.current_fee_schedule().set_fee( tx.operations.back() );\n         set_expiration( db, tx );\n         sign( tx, issuer_private_key );\n         PUSH_TX( db, tx );\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(update_asset_via_proposal_test)\n{\n   try\n   {\n      ACTOR(issuer);\n      asset_object usd_asset = create_user_issued_asset(\"USD\", issuer, charge_market_fee);\n\n      additional_asset_options_t options;\n      options.value.reward_percent = 100;\n      options.value.whitelist_market_fee_sharing = flat_set<account_id_type>{issuer_id};\n\n      asset_update_operation update_op;\n      update_op.issuer = issuer_id;\n      update_op.asset_to_update = usd_asset.get_id();\n      asset_options new_options;\n      update_op.new_options = usd_asset.options;\n      update_op.new_options.extensions = std::move(options);\n\n      const auto& curfees = *db.get_global_properties().parameters.current_fees;\n      const auto& proposal_create_fees = curfees.get<proposal_create_operation>();\n      proposal_create_operation prop;\n      prop.fee_paying_account = issuer_id;\n      prop.proposed_ops.emplace_back( update_op );\n      prop.expiration_time =  db.head_block_time() + fc::days(1);\n      prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n\n      {\n         prop.expiration_time =  db.head_block_time() + fc::days(1);\n         signed_transaction tx;\n         tx.operations.push_back( prop );\n         db.current_fee_schedule().set_fee( tx.operations.back() );\n         set_expiration( db, tx );\n         sign( tx, issuer_private_key );\n         PUSH_TX( db, tx );\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(issue_asset){\n   try\n   {\n       ACTORS((alice)(bob)(izzy)(jill));\n      // Izzy issues asset to Alice  (Izzycoin market percent - 10%)\n      // Jill issues asset to Bob    (Jillcoin market percent - 20%)\n\n      fund( alice, core_asset(1000000) );\n      fund( bob, core_asset(1000000) );\n      fund( izzy, core_asset(1000000) );\n      fund( jill, core_asset(1000000) );\n\n      price price(asset(1, asset_id_type(1)), asset(1));\n      constexpr auto izzycoin_market_percent = 10*GRAPHENE_1_PERCENT;\n      asset_object izzycoin = create_user_issued_asset( \"IZZYCOIN\", izzy,  charge_market_fee, price, 2, izzycoin_market_percent );\n\n      constexpr auto jillcoin_market_percent = 20*GRAPHENE_1_PERCENT;\n      asset_object jillcoin = create_user_issued_asset( \"JILLCOIN\", jill,  charge_market_fee, price, 2, jillcoin_market_percent );\n\n      // Alice and Bob create some coins\n      issue_uia( alice, izzycoin.amount( 100000 ) );\n      issue_uia( bob, jillcoin.amount( 100000 ) );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(accumulated_fees_before_hf_test)\n{\n   try\n   {\n      INVOKE(issue_asset);\n\n      const asset_object &jillcoin = get_asset(\"JILLCOIN\");\n      const asset_object &izzycoin = get_asset(\"IZZYCOIN\");\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice_id, izzycoin.amount(100), jillcoin.amount(300) );   // Alice is willing to sell her Izzy's for 3 Jill\n      create_sell_order(   bob_id, jillcoin.amount(700), izzycoin.amount(200) );   // Bob is buying up to 200 Izzy's for up to 3.5 Jill\n\n      // 100 Izzys and 300 Jills are matched, so the fees should be\n      // 10 Izzy (10%) and 60 Jill (20%).\n      BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == izzycoin.amount(10).amount );\n      BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == jillcoin.amount(60).amount );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(accumulated_fees_after_hf_test)\n{\n   try\n   {\n      INVOKE(issue_asset);\n\n      const asset_object &jillcoin = get_asset(\"JILLCOIN\");\n      const asset_object &izzycoin = get_asset(\"IZZYCOIN\");\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice_id, izzycoin.amount(100), jillcoin.amount(300) );   // Alice is willing to sell her Izzy's for 3 Jill\n      create_sell_order(   bob_id, jillcoin.amount(700), izzycoin.amount(200) );   // Bob is buying up to 200 Izzy's for up to 3.5 Jill\n\n      // 100 Izzys and 300 Jills are matched, so the fees should be\n      // 10 Izzy (10%) and 60 Jill (20%).\n      BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == izzycoin.amount(10).amount );\n      BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == jillcoin.amount(60).amount );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(accumulated_fees_with_additional_options_after_hf_test)\n{\n   try\n   {\n      INVOKE(issue_asset);\n\n      GET_ACTOR(jill);\n      GET_ACTOR(izzy);\n\n      const asset_object &jillcoin = get_asset(\"JILLCOIN\");\n      const asset_object &izzycoin = get_asset(\"IZZYCOIN\");\n\n      uint16_t reward_percent = 0;\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), reward_percent);\n      update_asset(izzy_id, izzy_private_key, izzycoin.get_id(), reward_percent);\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice_id, izzycoin.amount(100), jillcoin.amount(300) );   // Alice is willing to sell her Izzy's for 3 Jill\n      create_sell_order(   bob_id, jillcoin.amount(700), izzycoin.amount(200) );   // Bob is buying up to 200 Izzy's for up to 3.5 Jill\n\n      // 100 Izzys and 300 Jills are matched, so the fees should be\n      // 10 Izzy (10%) and 60 Jill (20%).\n      BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == izzycoin.amount(10).amount );\n      BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == jillcoin.amount(60).amount );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_test )\n{ try {\n\n   ACTOR(alice);\n   fund(alice);\n\n   const asset_object& core = asset_id_type()(db);\n\n   vesting_balance_create_operation op;\n   op.fee = core.amount( 0 );\n   op.creator = alice_id;\n   op.owner = alice_id;\n   op.amount = core.amount( 100 );\n   op.policy = instant_vesting_policy_initializer{};\n\n   trx.operations.push_back(op);\n   set_expiration( db, trx );\n\n   processed_transaction ptx = PUSH_TX( db, trx, ~0 );\n   const vesting_balance_id_type& vbid = ptx.operation_results.back().get<object_id_type>();\n\n   auto withdraw = [&](const asset& amount) {\n      vesting_balance_withdraw_operation withdraw_op;\n      withdraw_op.vesting_balance = vbid;\n      withdraw_op.owner = alice_id;\n      withdraw_op.amount = amount;\n\n      signed_transaction withdraw_tx;\n      withdraw_tx.operations.push_back( withdraw_op );\n      set_expiration( db, withdraw_tx );\n      sign(withdraw_tx, alice_private_key);\n      PUSH_TX( db, withdraw_tx );\n   };\n   // try to withdraw more then it is on the balance\n   GRAPHENE_REQUIRE_THROW(withdraw(op.amount.amount + 1), fc::exception);\n   //to withdraw all that is on the balance\n   withdraw(op.amount);\n   // try to withdraw more then it is on the balance\n   GRAPHENE_REQUIRE_THROW(withdraw( core.amount(1) ), fc::exception);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_via_proposal_test )\n{ try {\n\n   ACTOR(actor);\n   fund(actor);\n\n   const asset_object& core = asset_id_type()(db);\n\n   vesting_balance_create_operation create_op;\n   create_op.fee = core.amount( 0 );\n   create_op.creator = actor_id;\n   create_op.owner = actor_id;\n   create_op.amount = core.amount( 100 );\n   create_op.policy = instant_vesting_policy_initializer{};\n\n   const auto& curfees = *db.get_global_properties().parameters.current_fees;\n   const auto& proposal_create_fees = curfees.get<proposal_create_operation>();\n   proposal_create_operation prop;\n   prop.fee_paying_account = actor_id;\n   prop.proposed_ops.emplace_back( create_op );\n   prop.expiration_time =  db.head_block_time() + fc::days(1);\n   prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n\n   {\n      prop.expiration_time =  db.head_block_time() + fc::days(1);\n      signed_transaction tx;\n      tx.operations.push_back( prop );\n      db.current_fee_schedule().set_fee( tx.operations.back() );\n      set_expiration( db, tx );\n      sign( tx, actor_private_key );\n      PUSH_TX( db, tx );\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(white_list_asset_rewards_test)\n{\n   try\n   {\n      ACTORS((aliceregistrar)(bobregistrar)(alicereferrer)(bobreferrer)(izzy)(jill));\n\n      // Izzy issues white_list asset to Alice\n      // Jill issues white_list asset to Bob\n      // Bobreferrer added to blacklist for izzycoin asset\n      // Aliceregistrar added to blacklist for jillcoin asset\n      // Alice and Bob trade in the market and pay fees\n      // Check registrar/referrer rewards\n      upgrade_to_lifetime_member(aliceregistrar);\n      upgrade_to_lifetime_member(alicereferrer);\n      upgrade_to_lifetime_member(bobregistrar);\n      upgrade_to_lifetime_member(bobreferrer);\n      upgrade_to_lifetime_member(izzy);\n      upgrade_to_lifetime_member(jill);\n\n      const account_object alice = create_account(\"alice\", aliceregistrar, alicereferrer, 20*GRAPHENE_1_PERCENT);\n      const account_object bob   = create_account(\"bob\", bobregistrar, bobreferrer, 20*GRAPHENE_1_PERCENT);\n\n      fund( alice, core_asset(1000000) );\n      fund( bob, core_asset(1000000) );\n      fund( izzy, core_asset(1000000) );\n      fund( jill, core_asset(1000000) );\n\n      price price(asset(1, asset_id_type(1)), asset(1));\n      constexpr auto izzycoin_market_percent = 10*GRAPHENE_1_PERCENT;\n      constexpr auto jillcoin_market_percent = 20*GRAPHENE_1_PERCENT;\n      const asset_id_type izzycoin_id = create_user_issued_asset( \"IZZYCOIN\", izzy, charge_market_fee|white_list, price, 0, izzycoin_market_percent ).id;\n      const asset_id_type jillcoin_id = create_user_issued_asset( \"JILLCOIN\", jill, charge_market_fee|white_list, price, 0, jillcoin_market_percent ).id;\n\n      // Alice and Bob create some coins\n      issue_uia( alice, izzycoin_id(db).amount( 200000 ) );\n      issue_uia( bob, jillcoin_id(db).amount( 200000 ) );\n\n      constexpr auto izzycoin_reward_percent = 50*GRAPHENE_1_PERCENT;\n      constexpr auto jillcoin_reward_percent = 50*GRAPHENE_1_PERCENT;\n\n      update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent);\n      update_asset(jill_id, jill_private_key, jillcoin_id, jillcoin_reward_percent);\n\n      BOOST_TEST_MESSAGE( \"Attempting to blacklist bobreferrer for izzycoin asset\" );\n      asset_update_blacklist_authority(izzy_id, izzycoin_id, izzy_id, izzy_private_key);\n      add_account_to_blacklist(izzy_id, bobreferrer_id, izzy_private_key);\n      BOOST_CHECK( !(is_authorized_asset( db, bobreferrer_id(db), izzycoin_id(db) )) );\n\n      BOOST_TEST_MESSAGE( \"Attempting to blacklist aliceregistrar for jillcoin asset\" );\n      asset_update_blacklist_authority(jill_id, jillcoin_id, jill_id, jill_private_key);\n      add_account_to_blacklist(jill_id, aliceregistrar_id, jill_private_key);\n      BOOST_CHECK( !(is_authorized_asset( db, aliceregistrar_id(db), jillcoin_id(db) )) );\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice.id, izzycoin_id(db).amount(1000), jillcoin_id(db).amount(1500) ); // Alice is willing to sell her 1000 Izzy's for 1.5 Jill\n      create_sell_order(   bob.id, jillcoin_id(db).amount(1500), izzycoin_id(db).amount(1000) );   // Bob is buying up to 1500 Izzy's for up to 0.6 Jill\n\n      // 1000 Izzys and 1500 Jills are matched, so the fees should be\n      //   100 Izzy (10%) and 300 Jill (20%).\n\n      // Only Bob's registrar should get rewards\n      share_type bob_registrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id );\n      BOOST_CHECK_GT( bob_registrar_reward, 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( bob.referrer, izzycoin_id ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( alice.registrar, jillcoin_id ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( alice.referrer, jillcoin_id ), 0 );\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( create_vesting_balance_object_test )\n{\n   /**\n    * Test checks that an account could have duplicates VBO (with the same asset_type)\n    * for any type of vesting_balance_type\n    * except vesting_balance_type::market_fee_sharing\n   */\n   try {\n\n      ACTOR(actor);\n\n      create_vesting_balance_object(actor_id, vesting_balance_type::unspecified);\n      create_vesting_balance_object(actor_id, vesting_balance_type::unspecified);\n\n      create_vesting_balance_object(actor_id, vesting_balance_type::cashback);\n      create_vesting_balance_object(actor_id, vesting_balance_type::cashback);\n\n      create_vesting_balance_object(actor_id, vesting_balance_type::witness);\n      create_vesting_balance_object(actor_id, vesting_balance_type::witness);\n\n      create_vesting_balance_object(actor_id, vesting_balance_type::worker);\n      create_vesting_balance_object(actor_id, vesting_balance_type::worker);\n\n      create_vesting_balance_object(actor_id, vesting_balance_type::market_fee_sharing);\n      GRAPHENE_CHECK_THROW(create_vesting_balance_object(actor_id, vesting_balance_type::market_fee_sharing), fc::exception);\n\n} FC_LOG_AND_RETHROW() }\n\n\nBOOST_AUTO_TEST_CASE( possibility_to_set_100_reward_percent_after_hf1774 )\n{\n   try\n   {\n      INVOKE(create_actors);\n\n      generate_blocks_past_hf1774();\n      GET_ACTOR(jill);\n\n      constexpr auto jillcoin_reward_percent = 100*GRAPHENE_1_PERCENT;\n      const asset_object &jillcoin = get_asset(\"JCOIN\");\n\n      update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent);\n\n      GET_ACTOR(izzyregistrar);\n      GET_ACTOR(izzyreferrer);\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 );\n      BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 );\n\n      GET_ACTOR(alice);\n      GET_ACTOR(bob);\n\n      const share_type sell_amount = 100000;\n\n      // Alice and Bob place orders which match\n      create_sell_order( alice, jillcoin.amount(sell_amount), core_asset(1) );\n      create_sell_order( bob, core_asset(1), jillcoin.amount(sell_amount) );\n\n      const auto izzyregistrar_reward = get_market_fee_reward( izzyregistrar, jillcoin );\n      const auto izzyreferrer_reward = get_market_fee_reward( izzyreferrer, jillcoin );\n\n      BOOST_CHECK_GT(izzyregistrar_reward , 0);\n      BOOST_CHECK_GT(izzyreferrer_reward , 0);\n\n      auto calculate_percent = [](const share_type& value, uint16_t percent)\n      {\n         auto a(value.value);\n         a *= percent;\n         a /= GRAPHENE_100_PERCENT;\n         return a;\n      };\n      //jillcoin has 20% market fee percent, see create_actors\n      //all market fees are distributed between registrar and referrer\n      auto acc_rewards = izzyregistrar_reward + izzyreferrer_reward;\n      BOOST_CHECK_EQUAL( calculate_percent(sell_amount, 20*GRAPHENE_1_PERCENT), acc_rewards);\n\n      //check referrer reward\n      BOOST_CHECK_EQUAL( calculate_percent(acc_rewards, alice.referrer_rewards_percentage), izzyreferrer_reward);\n   }\n   FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/market_rounding_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and other contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/market.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE(market_rounding_tests, database_fixture)\n\n/**\n *  Create an order such that when the trade executes at the\n *  requested price the resulting payout to one party is 0\n *  ( Reproduces https://github.com/bitshares/bitshares-core/issues/184 )\n */\nBOOST_AUTO_TEST_CASE( trade_amount_equals_zero )\n{\n   try {\n      generate_blocks( HARDFORK_555_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      const asset_object& test = create_user_issued_asset( \"UIATEST\" );\n      const asset_id_type test_id = test.id;\n      const asset_object& core = get_asset( GRAPHENE_SYMBOL );\n      const asset_id_type core_id = core.id;\n      const account_object& core_seller = create_account( \"seller1\" );\n      const account_object& core_buyer = create_account(\"buyer1\");\n\n      transfer( committee_account(db), core_seller, asset( 100000000 ) );\n\n      issue_uia( core_buyer, asset( 10000000, test_id ) );\n\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, test), 10000000);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, test), 0);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, core), 100000000);\n\n      create_sell_order(core_seller, core.amount(1), test.amount(2));\n      create_sell_order(core_seller, core.amount(1), test.amount(2));\n      create_sell_order(core_buyer, test.amount(3), core.amount(1));\n\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, core), 1);\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, test), 9999997);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, core), 99999998);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, test), 3);\n\n      generate_block();\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      auto result = get_market_order_history(core_id, test_id);\n      BOOST_CHECK_EQUAL(result.size(), 4u);\n      BOOST_CHECK(result[0].op.pays == core_id(db).amount(0));\n      BOOST_CHECK(result[0].op.receives == test_id(db).amount(1));\n      BOOST_CHECK(result[1].op.pays == test_id(db).amount(1));\n      BOOST_CHECK(result[1].op.receives == core_id(db).amount(0));\n      BOOST_CHECK(result[2].op.pays == core_id(db).amount(1));\n      BOOST_CHECK(result[2].op.receives == test_id(db).amount(2));\n      BOOST_CHECK(result[3].op.pays == test_id(db).amount(2));\n      BOOST_CHECK(result[3].op.receives == core_id(db).amount(1));\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/**\n *  The something-for-nothing bug should be fixed https://github.com/bitshares/bitshares-core/issues/184\n */\nBOOST_AUTO_TEST_CASE( trade_amount_equals_zero_after_hf_184 )\n{\n   try {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_184_TIME - mi);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      set_expiration( db, trx );\n\n      const asset_object& test = create_user_issued_asset( \"UIATEST\" );\n      const asset_id_type test_id = test.id;\n      const asset_object& core = get_asset( GRAPHENE_SYMBOL );\n      const asset_id_type core_id = core.id;\n      const account_object& core_seller = create_account( \"seller1\" );\n      const account_object& core_buyer = create_account(\"buyer1\");\n\n      transfer( committee_account(db), core_seller, asset( 100000000 ) );\n\n      issue_uia( core_buyer, asset( 10000000, test_id ) );\n\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, test), 10000000);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, test), 0);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, core), 100000000);\n\n      create_sell_order(core_seller, core.amount(1), test.amount(2));\n      create_sell_order(core_seller, core.amount(1), test.amount(2));\n      create_sell_order(core_buyer, test.amount(3), core.amount(1));\n\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, core), 1);\n      BOOST_CHECK_EQUAL(get_balance(core_buyer, test), 9999998);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, core), 99999998);\n      BOOST_CHECK_EQUAL(get_balance(core_seller, test), 2);\n\n      generate_block();\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      auto result = get_market_order_history(core_id, test_id);\n      BOOST_CHECK_EQUAL(result.size(), 2u);\n      BOOST_CHECK(result[0].op.pays == core_id(db).amount(1));\n      BOOST_CHECK(result[0].op.receives == test_id(db).amount(2));\n      BOOST_CHECK(result[1].op.pays == test_id(db).amount(2));\n      BOOST_CHECK(result[1].op.receives == core_id(db).amount(1));\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * This test case reproduces one of the scenarios described in bitshares-core issue #342:\n *   when matching a limit order with another limit order, a small taker order will pay more than minimum required.\n */\nBOOST_AUTO_TEST_CASE( limit_limit_rounding_test1 )\n{\n   try {\n      generate_blocks( HARDFORK_555_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS( (seller)(buyer) );\n\n      const asset_object& test = create_user_issued_asset( \"UIATEST\" );\n      const asset_id_type test_id = test.id;\n      const asset_object& core = get_asset( GRAPHENE_SYMBOL );\n      const asset_id_type core_id = core.id;\n\n      transfer( committee_account(db), seller, asset( 100000000 ) );\n\n      issue_uia( buyer, asset( 10000000, test_id ) );\n\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 10000000);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 0);\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 100000000);\n\n      // seller sells 3 core for 31 test, price 10.33 test per core\n      limit_order_id_type sell_id = create_sell_order( seller, core.amount(3), test.amount(31) )->id;\n\n      // buyer buys 2 core with 25 test, price 12.5 test per core\n      // the order is filled immediately\n      BOOST_CHECK( !create_sell_order( buyer, test.amount(25), core.amount(2) ) );\n\n      BOOST_CHECK_EQUAL( sell_id(db).for_sale.value, 1 ); // 2 core sold, 1 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 99999997);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 25); // seller got 25 test\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 2); // buyer got 2 core\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 9999975); // buyer paid 25 test,\n                                                            // effective price is 25/2 which is much higher than 31/3\n\n      generate_block();\n\n      // buyer buys 2 core with 25 test, price 12.5 test per core\n      limit_order_id_type buy_id = create_sell_order( buyer_id, asset(25,test_id), asset(2,core_id) )->id;\n\n      generate_block();\n\n      BOOST_CHECK( !db.find_object( sell_id ) ); // sell order is filled\n      BOOST_CHECK_EQUAL( buy_id(db).for_sale.value, 15 ); // 10 test sold, 15 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(seller_id, core_id), 99999997);\n      BOOST_CHECK_EQUAL(get_balance(seller_id, test_id), 35); // seller got 10 more test\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, core_id), 3); // buyer got 1 more core\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, test_id), 9999950);\n\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * This test case tests one of the scenarios described in bitshares-core issue #342 after hard fork:\n *   when matching a limit order with another limit order,\n *   a small taker order will only pay minimum required amount, and the rest will be returned.\n */\nBOOST_AUTO_TEST_CASE( limit_limit_rounding_test1_after_hf_342 )\n{\n   try {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_342_TIME - mi);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      set_expiration( db, trx );\n\n      ACTORS( (seller)(buyer) );\n\n      const asset_object& test = create_user_issued_asset( \"UIATEST\" );\n      const asset_id_type test_id = test.id;\n      const asset_object& core = get_asset( GRAPHENE_SYMBOL );\n      const asset_id_type core_id = core.id;\n\n      transfer( committee_account(db), seller, asset( 100000000 ) );\n\n      issue_uia( buyer, asset( 10000000, test_id ) );\n\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 10000000);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 0);\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 100000000);\n\n      // seller sells 3 core for 31 test, price 10.33 test per core\n      limit_order_id_type sell_id = create_sell_order( seller, core.amount(3), test.amount(31) )->id;\n\n      // buyer buys 2 core with 25 test, price 12.5 test per core\n      // the order is filled immediately\n      BOOST_CHECK( !create_sell_order( buyer, test.amount(25), core.amount(2) ) );\n\n      BOOST_CHECK_EQUAL( sell_id(db).for_sale.value, 1 ); // 2 core sold, 1 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 2); // buyer got 2 core\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 9999979); // buyer actually paid 21 test according to price 10.33\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 99999997);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 21); // seller got 21 test\n\n      generate_block();\n      set_expiration( db, trx );\n\n      // buyer buys 2 core with 25 test, price 12.5 test per core\n      limit_order_id_type buy_id = create_sell_order( buyer_id, asset(25,test_id), asset(2,core_id) )->id;\n\n      generate_block();\n\n      BOOST_CHECK( !db.find_object( sell_id ) ); // sell order is filled\n      BOOST_CHECK_EQUAL( buy_id(db).for_sale.value, 15 ); // 10 test sold according to price 10.33, and 15 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, core_id), 3); // buyer got 1 more core\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, test_id), 9999954);\n      BOOST_CHECK_EQUAL(get_balance(seller_id, core_id), 99999997);\n      BOOST_CHECK_EQUAL(get_balance(seller_id, test_id), 31); // seller got 10 more test, in total 31 as expected\n\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * This test case reproduces one of the scenarios described in bitshares-core issue #342:\n *   when matching a limit order with another limit order, a small maker order will pay more than minimum required.\n */\nBOOST_AUTO_TEST_CASE( limit_limit_rounding_test2 )\n{\n   try {\n      generate_blocks( HARDFORK_555_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS( (seller)(buyer) );\n\n      const asset_object& test = create_user_issued_asset( \"UIATEST\" );\n      const asset_id_type test_id = test.id;\n      const asset_object& core = get_asset( GRAPHENE_SYMBOL );\n      const asset_id_type core_id = core.id;\n\n      transfer( committee_account(db), seller, asset( 100000000 ) );\n\n      issue_uia( buyer, asset( 10000000, test_id ) );\n\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 10000000);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 0);\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 100000000);\n\n      // buyer buys 17 core with 3 test, price 3/17 = 0.176 test per core\n      limit_order_id_type tmp_buy_id = create_sell_order( buyer, test.amount(3), core.amount(17) )->id;\n      // seller sells 33 core for 5 test, price 5/33 = 0.1515 test per core\n      limit_order_id_type sell_id = create_sell_order( seller, core.amount(33), test.amount(5) )->id;\n\n      BOOST_CHECK( !db.find_object( tmp_buy_id ) ); // buy order is filled\n      BOOST_CHECK_EQUAL( sell_id(db).for_sale.value, 16 ); // 17 core sold, 16 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 99999967);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 3); // seller got 3 test\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 17); // buyer got 17 core\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 9999997); // buyer paid 3 test\n\n      generate_block();\n      set_expiration( db, trx );\n\n      // buyer buys 15 core with 3 test, price 3/15 = 0.2 test per core\n      // even 15 < 16, since it's taker, we'll check with maker's price, then turns out the buy order is bigger\n      limit_order_id_type buy_id = create_sell_order( buyer_id, asset(3,test_id), asset(15,core_id) )->id;\n\n      generate_block();\n\n      BOOST_CHECK( !db.find_object( sell_id ) ); // sell order is filled\n      BOOST_CHECK_EQUAL( buy_id(db).for_sale.value, 1 ); // 2 test sold, 1 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(seller_id, core_id), 99999967); // seller paid the 16 core which was remaining in the order\n      BOOST_CHECK_EQUAL(get_balance(seller_id, test_id), 5); // seller got 2 more test\n                                                             // effective price 16/2 which is much higher than 33/5\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, core_id), 33); // buyer got 16 more core\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, test_id), 9999994);\n\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * This test case tests one of the scenarios described in bitshares-core issue #342 after hard fork:\n *   when matching a limit order with another limit order,\n *   a small maker order will only pay minimum required amount, and the rest will be returned.\n */\nBOOST_AUTO_TEST_CASE( limit_limit_rounding_test2_after_hf_342 )\n{\n   try {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_342_TIME - mi);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      set_expiration( db, trx );\n\n      ACTORS( (seller)(buyer) );\n\n      const asset_object& test = create_user_issued_asset( \"UIATEST\" );\n      const asset_id_type test_id = test.id;\n      const asset_object& core = get_asset( GRAPHENE_SYMBOL );\n      const asset_id_type core_id = core.id;\n\n      transfer( committee_account(db), seller, asset( 100000000 ) );\n\n      issue_uia( buyer, asset( 10000000, test_id ) );\n\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 10000000);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 0);\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 100000000);\n\n      // buyer buys 17 core with 3 test, price 3/17 = 0.176 test per core\n      limit_order_id_type tmp_buy_id = create_sell_order( buyer, test.amount(3), core.amount(17) )->id;\n      // seller sells 33 core for 5 test, price 5/33 = 0.1515 test per core\n      limit_order_id_type sell_id = create_sell_order( seller, core.amount(33), test.amount(5) )->id;\n\n      BOOST_CHECK( !db.find_object( tmp_buy_id ) ); // buy order is filled\n      BOOST_CHECK_EQUAL( sell_id(db).for_sale.value, 16 ); // 17 core sold, 16 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(seller, core), 99999967);\n      BOOST_CHECK_EQUAL(get_balance(seller, test), 3); // seller got 3 test\n      BOOST_CHECK_EQUAL(get_balance(buyer, core), 17); // buyer got 17 core\n      BOOST_CHECK_EQUAL(get_balance(buyer, test), 9999997); // buyer paid 3 test\n\n      generate_block();\n      set_expiration( db, trx );\n\n      // buyer buys 15 core with 3 test, price 3/15 = 0.2 test per core\n      // even 15 < 16, since it's taker, we'll check with maker's price, then turns out the buy order is bigger\n      limit_order_id_type buy_id = create_sell_order( buyer_id, asset(3,test_id), asset(15,core_id) )->id;\n\n      generate_block();\n\n      BOOST_CHECK( !db.find_object( sell_id ) ); // sell order is filled\n      BOOST_CHECK_EQUAL( buy_id(db).for_sale.value, 1 ); // 2 test sold, 1 remaining\n\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, core_id), 31); // buyer got 14 more core according to price 0.1515\n      BOOST_CHECK_EQUAL(get_balance(buyer_id, test_id), 9999994);\n      BOOST_CHECK_EQUAL(get_balance(seller_id, core_id), 99999967+16-14); // seller got refunded 2 core\n      BOOST_CHECK_EQUAL(get_balance(seller_id, test_id), 5); // seller got 2 more test, effective price 14/2 which is close to 33/5\n\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * Reproduces bitshares-core issue #132: something for nothing when matching a limit order with a call order.\n * Also detects the cull_small issue in check_call_orders.\n */\nBOOST_AUTO_TEST_CASE( issue_132_limit_and_call_test1 )\n{ try { // matching a limit order with call order\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(10), asset(1));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/175 CORE/USD = 62/700\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(100000), asset(15500));\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(10));\n   transfer(borrower2, seller, bitusd.amount(100000));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 10, call.debt.value );\n   BOOST_CHECK_EQUAL( 1, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 200010, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at price 11 USD / 1 CORE, but call only owes 10 USD,\n   //   so the seller will pay 10 USD but get nothing.\n   // The remaining 1 USD is too little to get any CORE, so the limit order will be cancelled\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(11), core.amount(1)) );\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 200000, get_balance(seller, bitusd) ); // the seller paid 10 USD\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) ); // the seller got nothing\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Another test case\n * reproduces bitshares-core issue #132: something for nothing when matching a limit order with a call order.\n * Also detects the cull_small issue in check_call_orders.\n *\n * In this test case, the limit order is taker.\n */\nBOOST_AUTO_TEST_CASE( issue_132_limit_and_call_test2 )\n{ try {\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(10), asset(1));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(10));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 10, call.debt.value );\n   BOOST_CHECK_EQUAL( 1, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100010, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at price 33 USD / 3 CORE, but call only owes 10 USD,\n   //   so the seller will pay 10 USD but get nothing.\n   // The remaining USD will be left in the order on the market\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100010-33, get_balance(seller, bitusd) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) ); // the seller got nothing\n   BOOST_CHECK_EQUAL( 33-10, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Yet another test case\n * reproduces bitshares-core issue #132: something for nothing when matching a limit order with a call order.\n * Also detects the cull_small issue in check_call_orders.\n *\n * In this test case, the limit order is maker.\n */\nBOOST_AUTO_TEST_CASE( issue_132_limit_and_call_test3 )\n{ try {\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   const asset_id_type bitusd_id = bitusd.id;\n   const asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(10), asset(1));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(10));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 10, call.debt.value );\n   BOOST_CHECK_EQUAL( 1, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100010, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   // create a limit order which will be matched later\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK_EQUAL( 33, sell_id(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 100010-33, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   generate_block();\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd_id(db).amount( 120 ) / core_id(db).amount(10);\n   publish_feed( bitusd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // the limit order will match with call at price 33 USD / 3 CORE, but call only owes 10 USD,\n   //   so the seller will pay 10 USD but get nothing.\n   // The remaining USD will be in the order on the market\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100010-33, get_balance(seller_id, bitusd_id) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 0, get_balance(seller_id, core_id) ); // the seller got nothing\n   BOOST_CHECK_EQUAL( 33-10, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, bitusd_id) );\n   BOOST_CHECK_EQUAL( init_balance, get_balance(borrower_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #132: something for nothing when matching a limit order with a call order.\n */\nBOOST_AUTO_TEST_CASE( issue_132_limit_and_call_test1_after_hardfork )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_184_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(10), asset(1));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/175 CORE/USD = 62/700\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(100000), asset(15500));\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(10));\n   transfer(borrower2, seller, bitusd.amount(100000));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 10, call.debt.value );\n   BOOST_CHECK_EQUAL( 1, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 200010, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at price 120 USD / 11 CORE (assume hard fork core-342 and hard fork core-338 occur at same time),\n   //   but call only owes 10 USD,\n   // Since the call would pay off all debt, let it pay 1 CORE from collateral\n   // The remaining 1 USD is too little to get any CORE, so the limit order will be cancelled\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(11), core.amount(1)) );\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 200000, get_balance(seller, bitusd) ); // the seller paid 10 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Another test case\n * for fixed bitshares-core issue #132: something for nothing when matching a limit order with a call order.\n *\n * In this test case, the limit order is taker.\n */\nBOOST_AUTO_TEST_CASE( issue_132_limit_and_call_test2_after_hardfork )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_184_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(10), asset(1));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(10));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 10, call.debt.value );\n   BOOST_CHECK_EQUAL( 1, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100010, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at price 120 USD / 11 CORE (assume hard fork core-342 and hard fork core-338 occur at same time),\n   //   but call only owes 10 USD,\n   // Since the call would pay off all debt, let it pay 1 CORE from collateral\n   // The remaining USD will be left in the order on the market\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100010-33, get_balance(seller, bitusd) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 33-10, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Yet another test case\n * for fixed bitshares-core issue #132: something for nothing when matching a limit order with a call order.\n * Also detects the cull_small issue in check_call_orders.\n *\n * In this test case, the limit order is maker.\n */\nBOOST_AUTO_TEST_CASE( issue_132_limit_and_call_test3_after_hardfork )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_184_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   const asset_id_type bitusd_id = bitusd.id;\n   const asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(10), asset(1));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(10));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 10, call.debt.value );\n   BOOST_CHECK_EQUAL( 1, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100010, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   // create a limit order which will be matched later\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK_EQUAL( 33, sell_id(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 100010-33, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   generate_block();\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd_id(db).amount( 120 ) / core_id(db).amount(10);\n   publish_feed( bitusd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // the limit order will match with call at price 33 USD / 3 CORE, but call only owes 10 USD,\n   // Since the call would pay off all debt, let it pay 1 CORE from collateral\n   // The remaining USD will be in the order on the market\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100010-33, get_balance(seller_id, bitusd_id) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller_id, core_id) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 33-10, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, bitusd_id) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * This test case reproduces one of the scenarios described in bitshares-core issue #342:\n *   when matching a big taker limit order with a small maker call order,\n *   rounding was in favor of the small call order.\n */\nBOOST_AUTO_TEST_CASE( limit_call_rounding_test1 )\n{ try {\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(20), asset(2));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(20));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 20, call.debt.value );\n   BOOST_CHECK_EQUAL( 2, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100020, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at limit order's price 33 USD / 3 CORE, but call only owes 20 USD,\n   //   so the seller will pay the whole 20 USD and get 1 CORE, since 20 USD doesn't worth 2 CORE according to price 33/3,\n   //   effective price is 20/1 which is worse than the limit order's desired 33/3.\n   // The remaining USD will be left in the order on the market\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100020-33, get_balance(seller, bitusd) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 33-20, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * This test case tests one of the scenarios described in bitshares-core issue #342 after hard fork:\n *   when matching a big taker limit order with a small maker call order,\n *   rounding in favor of the big limit order.\n */\nBOOST_AUTO_TEST_CASE( limit_call_rounding_test1_after_hf_342 )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_342_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(20), asset(2));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(20));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 20, call.debt.value );\n   BOOST_CHECK_EQUAL( 2, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100020, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at price 120 USD / 11 CORE (assume hard fork core-342 and hard fork core-338 occur at same time),\n   //   but call only owes 20 USD,\n   //   so the seller will pay 20 USD and get 2 CORE, since 20 USD worths a little more than 1 CORE according to price 120/11,\n   //   effective price is 20/2 which is not worse than the limit order's desired 33/3.\n   // The remaining USD will be left in the order on the market\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100020-33, get_balance(seller, bitusd) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 2, get_balance(seller, core) ); // the seller got 2 CORE\n   BOOST_CHECK_EQUAL( 33-20, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Due to #338, when matching a smaller taker limit order with a big maker call order,\n *   the small order will be filled at its own price.\n * So unable or no need to reproduce one of the scenarios described in bitshares-core issue #342:\n *   when matching a small taker limit order with a big maker call order,\n *   the small limit order would be paying too much.\n * But we'll just write the test case for #338 here.\n */\nBOOST_AUTO_TEST_CASE( limit_call_rounding_test2 )\n{ try {\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(20), asset(2));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(20));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 20, call.debt.value );\n   BOOST_CHECK_EQUAL( 2, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100020, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at limit order's price 15 USD / 1 CORE,\n   //   so the seller will pay 15 USD and get 1 CORE,\n   //   effective price is 15/1.\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(15), core.amount(1)) ); // the sell order is filled\n   BOOST_CHECK( db.find<call_order_object>( call_id ) != nullptr ); // the first call order did not get filled\n   BOOST_CHECK_EQUAL( 20-15, call.debt.value ); // call paid 15 USD\n   BOOST_CHECK_EQUAL( 2-1, call.collateral.value ); // call got 1 CORE\n   BOOST_CHECK_EQUAL( 100020-15, get_balance(seller, bitusd) ); // the seller paid 15 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n\n/***\n * This test case tests one of the scenarios described in bitshares-core issue #342 after hard fork:\n *   when matching a small taker limit order with a big maker call order,\n *   the small limit order would be paying minimum required.\n */\nBOOST_AUTO_TEST_CASE( limit_call_rounding_test2_after_hf_342 )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_342_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(20), asset(2));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(20));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 20, call.debt.value );\n   BOOST_CHECK_EQUAL( 2, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100020, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 120 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // This would match with call at price 120 USD / 11 CORE (assume hard fork core-342 and hard fork core-338 occur at same time),\n   //   so the seller will get 1 CORE, and pay 11 USD since 1 CORE worths a little more than 10 USD according to price 120/11,\n   //     and the extra 4 USD will be returned but not overpaid,\n   //     effective price is 11/1 which is close to 120/11.\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(15), core.amount(1)) ); // the sell order is filled\n   BOOST_CHECK( db.find<call_order_object>( call_id ) != nullptr ); // the first call order did not get filled\n   BOOST_CHECK_EQUAL( 20-11, call.debt.value ); // call paid 11 USD\n   BOOST_CHECK_EQUAL( 2-1, call.collateral.value ); // call got 1 CORE\n   BOOST_CHECK_EQUAL( 100020-11, get_balance(seller, bitusd) ); // the seller paid 11 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * This test case reproduces one of the scenarios described in bitshares-core issue #342:\n *   when matching a small taker call order with a big maker limit order,\n *   rounding was in favor of the small call order.\n */\nBOOST_AUTO_TEST_CASE( call_limit_rounding_test1 )\n{ try {\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   const asset_id_type bitusd_id = bitusd.id;\n   const asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(20), asset(2));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(20));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 20, call.debt.value );\n   BOOST_CHECK_EQUAL( 2, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100020, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   // create a limit order which will be matched later\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK_EQUAL( 33, sell_id(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 100020-33, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   generate_block();\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd_id(db).amount( 120 ) / core_id(db).amount(10);\n   publish_feed( bitusd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // The limit would match with call at limit order's price 33 USD / 3 CORE, but call only owes 20 USD,\n   //   so the seller will pay the whole 20 USD and get 1 CORE, since 20 USD doesn't worth 2 CORE according to price 33/3,\n   //   effective price is 20/1 which is worse than the limit order's desired 33/3.\n   // The remaining USD will be left in the order on the market\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100020-33, get_balance(seller_id, bitusd_id) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller_id, core_id) ); // the seller got 1 CORE\n   BOOST_CHECK_EQUAL( 33-20, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, bitusd_id) );\n   BOOST_CHECK_EQUAL( init_balance-1, get_balance(borrower_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * This test case tests one of the scenarios described in bitshares-core issue #342 after hard fork:\n *   when matching a small taker call order with a big maker limit order,\n *   rounding in favor of the big limit order.\n */\nBOOST_AUTO_TEST_CASE( call_limit_rounding_test1_after_hf_342 )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_342_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   const asset_id_type bitusd_id = bitusd.id;\n   const asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(20), asset(2));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(20));\n   transfer(borrower3, seller, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 20, call.debt.value );\n   BOOST_CHECK_EQUAL( 2, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 100020, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower, core) );\n\n   // create a limit order which will be matched later\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(33), core.amount(3))->id;\n   BOOST_CHECK_EQUAL( 33, sell_id(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 100020-33, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   generate_block();\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd_id(db).amount( 120 ) / core_id(db).amount(10);\n   publish_feed( bitusd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // The limit would match with call at limit order's price 33 USD / 3 CORE, but call only owes 20 USD,\n   //   so the seller will pay 20 USD and get 2 CORE, since 20 USD worths a little more than 1 CORE according to price 33/3,\n   //   effective price is 20/2 which is not worse than the limit order's desired 33/3.\n   // The remaining USD will be left in the order on the market\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK_EQUAL( 100020-33, get_balance(seller_id, bitusd_id) ); // the seller paid 33 USD\n   BOOST_CHECK_EQUAL( 2, get_balance(seller_id, core_id) ); // the seller got 2 CORE\n   BOOST_CHECK_EQUAL( 33-20, sell_id(db).for_sale.value ); // the sell order has some USD left\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, bitusd_id) );\n   BOOST_CHECK_EQUAL( init_balance-2, get_balance(borrower_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * This test case reproduces one of the scenarios described in bitshares-core issue #342:\n *   when matching a big taker call order with a small maker limit order,\n *   the small limit order would be paying too much.\n */\nBOOST_AUTO_TEST_CASE( call_limit_rounding_test2 )\n{ try {\n   generate_blocks( HARDFORK_555_TIME );\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(seller2)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   const asset_id_type bitusd_id = bitusd.id;\n   const asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(50), asset(5));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(50));\n   transfer(borrower3, seller2, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 50, call.debt.value );\n   BOOST_CHECK_EQUAL( 5, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 50, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 100000, get_balance(seller2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-5, get_balance(borrower, core) );\n\n   // create a buy order which will be matched\n   limit_order_id_type buy_id = create_sell_order(buyer, core.amount(1), bitusd.amount(10))->id;\n   BOOST_CHECK_EQUAL( 1, buy_id(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 1000000-1, get_balance(buyer, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer, bitusd) );\n\n   // create a limit order to fill the buy order, and remaining amounts will be matched later\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(31), core.amount(2))->id;\n   BOOST_CHECK( !db.find<limit_order_object>( buy_id ) ); // the buy order is filled\n   BOOST_CHECK_EQUAL( 1000000-1, get_balance(buyer, core) );\n   BOOST_CHECK_EQUAL( 10, get_balance(buyer, bitusd) ); // buyer got 10 usd\n   BOOST_CHECK_EQUAL( 21, sell_id(db).for_sale.value ); // remaining amount of sell order is 21\n   BOOST_CHECK_EQUAL( 50-31, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // seller got 1 core\n\n   // create another limit order which will be matched later\n   limit_order_id_type sell_id2 = create_sell_order(seller2, bitusd.amount(14), core.amount(1))->id;\n   BOOST_CHECK_EQUAL( 14, sell_id2(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 100000-14, get_balance(seller2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller2, core) );\n\n   generate_block();\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd_id(db).amount( 120 ) / core_id(db).amount(10);\n   publish_feed( bitusd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // call will match with the limit orders at limit orders' prices,\n   //   firstly, call will match with sell_id, which has 21 USD remaining, with price 31 USD / 2 CORE,\n   //     so the seller will pay 21 USD, get 1 CORE since 21 USD doesn't worth 2 CORE according to price 31/2,\n   //     effective price is 21/1 which is much bigger than 31/2;\n   //   then, call will match with sell_id2, which has 14 USD remaining, with price 14 USD / 1 CORE,\n   //     so the seller will pay 14 USD, get 1 CORE since 14 USD worths just 1 CORE according to price 14/1\n   BOOST_CHECK( !db.find<limit_order_object>( sell_id ) ); // the sell order is filled\n   BOOST_CHECK( !db.find<limit_order_object>( sell_id2 ) ); // the other sell order is filled\n   BOOST_CHECK( db.find<call_order_object>( call_id ) != nullptr ); // the first call order did not get filled\n   BOOST_CHECK_EQUAL( 50-14-21, call_id(db).debt.value ); // call paid 14 USD and 21 USD\n   BOOST_CHECK_EQUAL( 5-1-1, call_id(db).collateral.value ); // call got 1 CORE and 1 CORE\n   BOOST_CHECK_EQUAL( 50-31, get_balance(seller_id, bitusd_id) ); // seller paid 31 USD in total\n   BOOST_CHECK_EQUAL( 1+1, get_balance(seller_id, core_id) ); // seller got 1 more CORE\n   BOOST_CHECK_EQUAL( 100000-14, get_balance(seller2_id, bitusd_id) ); // seller2 paid 14 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller2_id, core_id) ); // seller2 got 1 CORE\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, bitusd_id) );\n   BOOST_CHECK_EQUAL( init_balance-5, get_balance(borrower_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * This test case tests one of the scenarios described in bitshares-core issue #342 after hard fork:\n *   when matching a big taker call order with a small maker limit order,\n *   the small limit order would be paying minimum required.\n */\nBOOST_AUTO_TEST_CASE( call_limit_rounding_test2_after_hf_342 )\n{ try {\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_342_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(seller2)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   const asset_id_type bitusd_id = bitusd.id;\n   const asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount( 5 );\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 200% collateral, call price is 10/175 CORE/USD = 40/700\n   const call_order_object& call = *borrow( borrower, bitusd.amount(50), asset(5));\n   call_order_id_type call_id = call.id;\n   // create yet another position with 350% collateral, call price is 17.5/175 CORE/USD = 77/700\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(100000), asset(17500));\n   transfer(borrower, seller, bitusd.amount(50));\n   transfer(borrower3, seller2, bitusd.amount(100000));\n\n   BOOST_CHECK_EQUAL( 50, call.debt.value );\n   BOOST_CHECK_EQUAL( 5, call.collateral.value );\n   BOOST_CHECK_EQUAL( 100000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n\n   BOOST_CHECK_EQUAL( 50, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 100000, get_balance(seller2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance-5, get_balance(borrower, core) );\n\n   // create a buy order which will be matched\n   limit_order_id_type buy_id = create_sell_order(buyer, core.amount(1), bitusd.amount(10))->id;\n   BOOST_CHECK_EQUAL( 1, buy_id(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 1000000-1, get_balance(buyer, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer, bitusd) );\n\n   // create a limit order to fill the buy order, and remaining amounts will be matched later\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(31), core.amount(2))->id;\n   BOOST_CHECK( !db.find<limit_order_object>( buy_id ) ); // the buy order is filled\n   BOOST_CHECK_EQUAL( 1000000-1, get_balance(buyer, core) );\n   BOOST_CHECK_EQUAL( 10, get_balance(buyer, bitusd) ); // buyer got 10 usd\n   BOOST_CHECK_EQUAL( 21, sell_id(db).for_sale.value ); // remaining amount of sell order is 21\n   BOOST_CHECK_EQUAL( 50-31, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 1, get_balance(seller, core) ); // seller got 1 core\n\n   // create another limit order which will be matched later\n   limit_order_id_type sell_id2 = create_sell_order(seller2, bitusd.amount(14), core.amount(1))->id;\n   BOOST_CHECK_EQUAL( 14, sell_id2(db).for_sale.value );\n   BOOST_CHECK_EQUAL( 100000-14, get_balance(seller2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller2, core) );\n\n   generate_block();\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd_id(db).amount( 120 ) / core_id(db).amount(10);\n   publish_feed( bitusd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 120 USD / 10 CORE, mssp = 120/11 USD/CORE\n\n   // call will match with the limit orders at limit orders' prices,\n   //   firstly, call will match with sell_id, which has 21 USD remaining, with price 31 USD / 2 CORE,\n   //     so the seller will get 1 CORE since 21 USD doesn't work 2 CORE according to price 31/2,\n   //       and the seller will pay 16 USD since 1 CORE worths a little more than 15 USD according to price 31/2,\n   //       and the extra 5 USD will be returned to seller since it doesn't worth 1 CORE,\n   //       effective price is 16/1 which is close to 31/2;\n   //   secondly, call will match with sell_id2, which has 14 USD remaining, with price 14 USD / 1 CORE,\n   //     so the seller will get 1 CORE and pay 14 USD since 14 USD just worths 1 CORE according to price 14/1\n   BOOST_CHECK( !db.find<limit_order_object>( sell_id ) ); // the sell order is filled\n   BOOST_CHECK( !db.find<limit_order_object>( sell_id2 ) ); // the other sell order is filled\n   BOOST_CHECK( db.find<call_order_object>( call_id ) != nullptr ); // the first call order did not get filled\n   BOOST_CHECK_EQUAL( 50-14-16, call_id(db).debt.value ); // call paid 14 USD and 16 USD\n   BOOST_CHECK_EQUAL( 5-1-1, call_id(db).collateral.value ); // call got 1 CORE and 1 CORE\n   BOOST_CHECK_EQUAL( 50-31+(21-16), get_balance(seller_id, bitusd_id) ); // seller paid 31 USD then get refunded 5 USD\n   BOOST_CHECK_EQUAL( 1+1, get_balance(seller_id, core_id) ); // seller got 1 more CORE\n   BOOST_CHECK_EQUAL( 100000-14, get_balance(seller2_id, bitusd_id) ); // seller2 paid 14 USD\n   BOOST_CHECK_EQUAL( 1, get_balance(seller2_id, core_id) ); // seller2 got 1 CORE\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, bitusd_id) );\n   BOOST_CHECK_EQUAL( init_balance-5, get_balance(borrower_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/market_tests.cpp",
    "content": "/*\n * Copyright (c) 2017 Peter Conrad, and other contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/market.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE(market_tests, database_fixture)\n\n/***\n * Reproduce bitshares-core issue #338 #343 #453 #606 #625 #649\n */\nBOOST_AUTO_TEST_CASE(issue_338_etc)\n{ try {\n   generate_blocks(HARDFORK_615_TIME); // get around Graphene issue #615 feed expiration bug\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n   asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 320% collateral, call price is 16/1.75 CORE/USD = 64/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(16000));\n   call_order_id_type call3_id = call3.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This order slightly below the call price will not be matched #606\n   limit_order_id_type sell_low = create_sell_order(seller, bitusd.amount(7), core.amount(59))->id;\n   // This order above the MSSP will not be matched\n   limit_order_id_type sell_high = create_sell_order(seller, bitusd.amount(7), core.amount(78))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_med = create_sell_order(seller, bitusd.amount(7), core.amount(60))->id;\n\n   cancel_limit_order( sell_med(db) );\n   cancel_limit_order( sell_high(db) );\n   cancel_limit_order( sell_low(db) );\n\n   // current implementation: an incoming limit order will be filled at the\n   // requested price #338\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(7), core.amount(60)) );\n   BOOST_CHECK_EQUAL( 993, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 60, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 993, call.debt.value );\n   BOOST_CHECK_EQUAL( 14940, call.collateral.value );\n\n   limit_order_id_type buy_low = create_sell_order(buyer, asset(90), bitusd.amount(10))->id;\n   // margin call takes precedence\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(7), core.amount(60)) );\n   BOOST_CHECK_EQUAL( 986, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 120, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 986, call.debt.value );\n   BOOST_CHECK_EQUAL( 14880, call.collateral.value );\n\n   limit_order_id_type buy_med = create_sell_order(buyer, asset(105), bitusd.amount(10))->id;\n   // margin call takes precedence\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(7), core.amount(70)) );\n   BOOST_CHECK_EQUAL( 979, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 190, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 979, call.debt.value );\n   BOOST_CHECK_EQUAL( 14810, call.collateral.value );\n\n   limit_order_id_type buy_high = create_sell_order(buyer, asset(115), bitusd.amount(10))->id;\n   // margin call still has precedence (!) #625\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(7), core.amount(77)) );\n   BOOST_CHECK_EQUAL( 972, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 267, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 972, call.debt.value );\n   BOOST_CHECK_EQUAL( 14733, call.collateral.value );\n\n   cancel_limit_order( buy_high(db) );\n   cancel_limit_order( buy_med(db) );\n   cancel_limit_order( buy_low(db) );\n\n   // call with more usd\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700), core.amount(7700)) );\n   BOOST_CHECK_EQUAL( 272, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 7967, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 272, call.debt.value );\n   BOOST_CHECK_EQUAL( 7033, call.collateral.value );\n\n   // at this moment, collateralization of call is 7033 / 272 = 25.8\n   // collateralization of call2 is 15500 / 1000 = 15.5\n   // collateralization of call3 is 16000 / 1000 = 16\n\n   // call more, still matches with the first call order #343\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(10), core.amount(110)) );\n   BOOST_CHECK_EQUAL( 262, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 8077, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 262, call.debt.value );\n   BOOST_CHECK_EQUAL( 6923, call.collateral.value );\n\n   // at this moment, collateralization of call is 6923 / 262 = 26.4\n   // collateralization of call2 is 15500 / 1000 = 15.5\n   // collateralization of call3 is 16000 / 1000 = 16\n\n   // force settle\n   force_settle( seller, bitusd.amount(10) );\n   BOOST_CHECK_EQUAL( 252, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 8077, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 262, call.debt.value );\n   BOOST_CHECK_EQUAL( 6923, call.collateral.value );\n\n   // generate blocks to let the settle order execute (price feed will expire after it)\n   generate_blocks( HARDFORK_615_TIME + fc::hours(25) );\n   // call2 get settled #343\n   BOOST_CHECK_EQUAL( 252, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 8177, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 262, call_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 6923, call_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 990, call2_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 15400, call2_id(db).collateral.value );\n\n   set_expiration( db, trx );\n   update_feed_producers( usd_id(db), {feedproducer_id} );\n\n   // at this moment, collateralization of call is 8177 / 252 = 32.4\n   // collateralization of call2 is 15400 / 990 = 15.5\n   // collateralization of call3 is 16000 / 1000 = 16\n\n   // adjust price feed to get call2 into black swan territory, but not the first call order\n   current_feed.settlement_price = asset(1, usd_id) / asset(20, core_id);\n   publish_feed( usd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 1/20, mssp = 1/22\n\n   // black swan event doesn't occur #649\n   BOOST_CHECK( !usd_id(db).bitasset_data(db).has_settlement() );\n\n   // generate a block\n   generate_block();\n\n   set_expiration( db, trx );\n   update_feed_producers( usd_id(db), {feedproducer_id} );\n\n   // adjust price feed back\n   current_feed.settlement_price = asset(1, usd_id) / asset(10, core_id);\n   publish_feed( usd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   transfer(borrower2_id, seller_id, asset(1000, usd_id));\n   transfer(borrower3_id, seller_id, asset(1000, usd_id));\n\n   // Re-create sell_low, slightly below the call price, will not be matched, will expire soon\n   sell_low = create_sell_order(seller_id(db), asset(7, usd_id), asset(59), db.head_block_time()+fc::seconds(300) )->id;\n   // This would match but is blocked by sell_low, it has an amount same as call's debt which will be full filled later\n   sell_med = create_sell_order(seller_id(db), asset(262, usd_id), asset(2620))->id; // 1/10\n   // Another big order above sell_med, blocked\n   limit_order_id_type sell_med2 = create_sell_order(seller_id(db), asset(1200, usd_id), asset(12120))->id; // 1/10.1\n   // Another small order above sell_med2, blocked\n   limit_order_id_type sell_med3 = create_sell_order(seller_id(db), asset(120, usd_id), asset(1224))->id; // 1/10.2\n\n   // generate a block, sell_low will expire\n   BOOST_TEST_MESSAGE( \"Expire sell_low\" );\n   generate_blocks( HARDFORK_615_TIME + fc::hours(26) );\n   BOOST_CHECK( db.find<limit_order_object>( sell_low ) == nullptr );\n\n   // #453 multiple order matching issue occurs\n   BOOST_CHECK( db.find<limit_order_object>( sell_med ) == nullptr ); // sell_med get filled\n   BOOST_CHECK( db.find<limit_order_object>( sell_med2 ) != nullptr ); // sell_med2 is still there\n   BOOST_CHECK( db.find<limit_order_object>( sell_med3 ) == nullptr ); // sell_med3 get filled\n   BOOST_CHECK( db.find<call_order_object>( call_id ) == nullptr ); // the first call order get filled\n   BOOST_CHECK( db.find<call_order_object>( call2_id ) == nullptr ); // the second call order get filled\n   BOOST_CHECK( db.find<call_order_object>( call3_id ) != nullptr ); // the third call order is still there\n\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #338 #343 #606 #625 #649\n */\nBOOST_AUTO_TEST_CASE(hardfork_core_338_test)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n   if(hf1270)\n      generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   else\n      generate_blocks(HARDFORK_CORE_343_TIME - mi);\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n   asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 320% collateral, call price is 16/1.75 CORE/USD = 64/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(16000));\n   call_order_id_type call3_id = call3.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This sell order above MSSP will not be matched with a call\n   BOOST_CHECK( create_sell_order(seller, bitusd.amount(7), core.amount(78)) != nullptr );\n\n   BOOST_CHECK_EQUAL( 2993, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // This buy order is too low will not be matched with a sell order\n   limit_order_id_type buy_low = create_sell_order(buyer, asset(90), bitusd.amount(10))->id;\n   // This buy order at MSSP will be matched only if no margin call (margin call takes precedence)\n   limit_order_id_type buy_med = create_sell_order(buyer, asset(110), bitusd.amount(10))->id;\n   // This buy order above MSSP will be matched with a sell order (limit order with better price takes precedence)\n   limit_order_id_type buy_high = create_sell_order(buyer, asset(111), bitusd.amount(10))->id;\n\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 90 - 110 - 111, get_balance(buyer, core) );\n\n   // This order slightly below the call price will be matched: #606 fixed\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700), core.amount(5900) ) );\n\n   // firstly it will match with buy_high, at buy_high's price: #625 fixed\n   BOOST_CHECK( !db.find<limit_order_object>( buy_high ) );\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_med )->for_sale.value, 110 );\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 90 );\n\n   // buy_high pays 111 CORE, receives 10 USD goes to buyer's balance\n   BOOST_CHECK_EQUAL( 10, get_balance(buyer, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 90 - 110 - 111, get_balance(buyer, core) );\n   // sell order pays 10 USD, receives 111 CORE, remaining 690 USD for sale, still at price 7/59\n\n   // then it will match with call, at mssp: 1/11 = 690/7590 : #338 fixed\n   BOOST_CHECK_EQUAL( 2293, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 7701, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 310, call.debt.value );\n   BOOST_CHECK_EQUAL( 7410, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n\n   // call's call_price will be updated after the match, to 741/31/1.75 CORE/USD = 2964/217\n   // it's above settlement price (10/1) so won't be margin called again\n   if(!hf1270) // can use call price only if we are before hf1270\n      BOOST_CHECK( price(asset(2964),asset(217,usd_id)) == call.call_price );\n\n   // This would match with call before, but would match with call2 after #343 fixed\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700), core.amount(6000) ) );\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_med )->for_sale.value, 110 );\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 90 );\n\n   // fill price would be mssp: 1/11 = 700/7700 : #338 fixed\n   BOOST_CHECK_EQUAL( 1593, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 15401, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 310, call.debt.value );\n   BOOST_CHECK_EQUAL( 7410, call.collateral.value );\n   BOOST_CHECK_EQUAL( 300, call2.debt.value );\n   BOOST_CHECK_EQUAL( 7800, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n   // call2's call_price will be updated after the match, to 78/3/1.75 CORE/USD = 312/21\n   if(!hf1270) // can use call price only if we are before hf1270\n      BOOST_CHECK( price(asset(312),asset(21,usd_id)) == call2.call_price );\n   // it's above settlement price (10/1) so won't be margin called\n\n   // at this moment, collateralization of call is 7410 / 310 = 23.9\n   // collateralization of call2 is 7800 / 300 = 26\n   // collateralization of call3 is 16000 / 1000 = 16\n\n   // force settle\n   force_settle( seller, bitusd.amount(10) );\n\n   BOOST_CHECK_EQUAL( 1583, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 15401, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 310, call.debt.value );\n   BOOST_CHECK_EQUAL( 7410, call.collateral.value );\n   BOOST_CHECK_EQUAL( 300, call2.debt.value );\n   BOOST_CHECK_EQUAL( 7800, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n\n   // generate blocks to let the settle order execute (price feed will expire after it)\n   generate_block();\n   generate_blocks( db.head_block_time() + fc::hours(24) );\n\n   // call3 get settled, at settlement price 1/10: #343 fixed\n   BOOST_CHECK_EQUAL( 1583, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 15501, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 310, call_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 7410, call_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 300, call2_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 7800, call2_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 990, call3_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 15900, call3_id(db).collateral.value );\n\n   set_expiration( db, trx );\n   update_feed_producers( usd_id(db), {feedproducer_id} );\n\n   // at this moment, collateralization of call is 7410 / 310 = 23.9\n   // collateralization of call2 is 7800 / 300 = 26\n   // collateralization of call3 is 15900 / 990 = 16.06\n\n   // adjust price feed to get call3 into black swan territory, but not the other call orders\n   // Note: after hard fork, black swan should occur when callateralization < mssp, but not at < feed\n   current_feed.settlement_price = asset(1, usd_id) / asset(16, core_id);\n   publish_feed( usd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 1/16, mssp = 10/176\n\n   // black swan event will occur: #649 fixed\n   BOOST_CHECK( usd_id(db).bitasset_data(db).has_settlement() );\n   // short positions will be closed\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) );\n   BOOST_CHECK( !db.find<call_order_object>( call2_id ) );\n   BOOST_CHECK( !db.find<call_order_object>( call3_id ) );\n\n   // generate a block\n   generate_block();\n\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #453: multiple limit order filling issue\n */\nBOOST_AUTO_TEST_CASE(hardfork_core_453_test)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n   if(hf1270)\n      generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   else\n      generate_blocks(HARDFORK_CORE_343_TIME - mi);\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 320% collateral, call price is 16/1.75 CORE/USD = 64/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(16000));\n   call_order_id_type call3_id = call3.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // no margin call so far\n\n   // This order would match call when it's margin called, it has an amount same as call's debt which will be full filled later\n   limit_order_id_type sell_med = create_sell_order(seller_id(db), asset(1000, usd_id), asset(10000))->id; // 1/10\n   // Another big order above sell_med, amount bigger than call2's debt\n   limit_order_id_type sell_med2 = create_sell_order(seller_id(db), asset(1200, usd_id), asset(12120))->id; // 1/10.1\n   // Another small order above sell_med2\n   limit_order_id_type sell_med3 = create_sell_order(seller_id(db), asset(120, usd_id), asset(1224))->id; // 1/10.2\n\n   // adjust price feed to get the call orders  into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // Fixed #453 multiple order matching issue occurs\n   BOOST_CHECK( !db.find<limit_order_object>( sell_med ) ); // sell_med get filled\n   BOOST_CHECK( !db.find<limit_order_object>( sell_med2 ) ); // sell_med2 get filled\n   BOOST_CHECK( !db.find<limit_order_object>( sell_med3 ) ); // sell_med3 get filled\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) ); // the first call order get filled\n   BOOST_CHECK( !db.find<call_order_object>( call2_id ) ); // the second call order get filled\n   BOOST_CHECK( db.find<call_order_object>( call3_id ) ); // the third call order is still there\n\n   // generate a block\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Tests (big) limit order matching logic after #625 got fixed\n */\nBOOST_AUTO_TEST_CASE(hardfork_core_625_big_limit_order_test)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n   if(hf1270)\n      generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   else\n      generate_blocks(HARDFORK_CORE_625_TIME - mi);\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(buyer2)(buyer3)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, buyer2_id, asset(init_balance));\n   transfer(committee_account, buyer3_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 500% collateral, call price is 25/1.75 CORE/USD = 100/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(25000));\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 15000, get_balance(borrower, core) );\n   BOOST_CHECK_EQUAL( init_balance - 15500, get_balance(borrower2, core) );\n   BOOST_CHECK_EQUAL( init_balance - 25000, get_balance(borrower3, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower3, bitusd) );\n\n   // adjust price feed to get call and call2 (but not call3) into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This sell order above MSSP will not be matched with a call\n   limit_order_id_type sell_high = create_sell_order(seller, bitusd.amount(7), core.amount(78))->id;\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_high )->for_sale.value, 7 );\n\n   BOOST_CHECK_EQUAL( 2993, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // This buy order is too low will not be matched with a sell order\n   limit_order_id_type buy_low = create_sell_order(buyer, asset(80), bitusd.amount(10))->id;\n   // This buy order at MSSP will be matched only if no margin call (margin call takes precedence)\n   limit_order_id_type buy_med = create_sell_order(buyer2, asset(11000), bitusd.amount(1000))->id;\n   // This buy order above MSSP will be matched with a sell order (limit order with better price takes precedence)\n   limit_order_id_type buy_high = create_sell_order(buyer3, asset(111), bitusd.amount(10))->id;\n\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer3, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 80, get_balance(buyer, core) );\n   BOOST_CHECK_EQUAL( init_balance - 11000, get_balance(buyer2, core) );\n   BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) );\n\n   // Create a big sell order slightly below the call price, will be matched with several orders\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700*4), core.amount(5900*4) ) );\n\n   // firstly it will match with buy_high, at buy_high's price\n   BOOST_CHECK( !db.find<limit_order_object>( buy_high ) );\n   // buy_high pays 111 CORE, receives 10 USD goes to buyer3's balance\n   BOOST_CHECK_EQUAL( 10, get_balance(buyer3, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) );\n\n   // then it will match with call, at mssp: 1/11 = 1000/11000\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) );\n   // call pays 11000 CORE, receives 1000 USD to cover borrower's position, remaining CORE goes to borrower's balance\n   BOOST_CHECK_EQUAL( init_balance - 11000, get_balance(borrower, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n\n   // then it will match with call2, at mssp: 1/11 = 1000/11000\n   BOOST_CHECK( !db.find<call_order_object>( call2_id ) );\n   // call2 pays 11000 CORE, receives 1000 USD to cover borrower2's position, remaining CORE goes to borrower2's balance\n   BOOST_CHECK_EQUAL( init_balance - 11000, get_balance(borrower2, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2, bitusd) );\n\n   // then it will match with buy_med, at buy_med's price. Since buy_med is too big, it's partially filled.\n   // buy_med receives the remaining USD of sell order, minus market fees, goes to buyer2's balance\n   BOOST_CHECK_EQUAL( 783, get_balance(buyer2, bitusd) ); // 700*4-10-1000-1000=790, minus 1% market fee 790*100/10000=7\n   BOOST_CHECK_EQUAL( init_balance - 11000, get_balance(buyer2, core) );\n   // buy_med pays at 1/11 = 790/8690\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_med )->for_sale.value, 11000-8690 );\n\n   // call3 is not in margin call territory so won't be matched\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n\n   // buy_low's price is too low that won't be matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 80 );\n\n   // check seller balance\n   BOOST_CHECK_EQUAL( 193, get_balance(seller, bitusd) ); // 3000 - 7 - 700*4\n   BOOST_CHECK_EQUAL( 30801, get_balance(seller, core) ); // 111 + 11000 + 11000 + 8690\n\n   // Cancel buy_med\n   cancel_limit_order( buy_med(db) );\n   BOOST_CHECK( !db.find<limit_order_object>( buy_med ) );\n   BOOST_CHECK_EQUAL( 783, get_balance(buyer2, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 8690, get_balance(buyer2, core) );\n\n   // Create another sell order slightly below the call price, won't fill\n   limit_order_id_type sell_med = create_sell_order( seller, bitusd.amount(7), core.amount(59) )->id;\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_med )->for_sale.value, 7 );\n   // check seller balance\n   BOOST_CHECK_EQUAL( 193-7, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 30801, get_balance(seller, core) );\n\n   // call3 is not in margin call territory so won't be matched\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n\n   // buy_low's price is too low that won't be matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 80 );\n\n   // generate a block\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #453 #606: multiple order matching without black swan, multiple bitassets\n */\nBOOST_AUTO_TEST_CASE(hard_fork_453_cross_test)\n{ try { // create orders before hard fork, which will be matched on hard fork\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_453_TIME - mi); // assume all hard forks occur at same time\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& biteur = create_bitasset(\"EURBIT\", feedproducer_id);\n   const auto& bitcny = create_bitasset(\"CNYBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n   asset_id_type eur_id = biteur.id;\n   asset_id_type cny_id = bitcny.id;\n   asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n   update_feed_producers( biteur, {feedproducer.id} );\n   update_feed_producers( bitcny, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   current_feed.settlement_price = biteur.amount( 1 ) / core.amount(5);\n   publish_feed( biteur, feedproducer, current_feed );\n   current_feed.settlement_price = bitcny.amount( 1 ) / core.amount(5);\n   publish_feed( bitcny, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call_usd = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_usd_id = call_usd.id;\n   const call_order_object& call_eur = *borrow( borrower, biteur.amount(1000), asset(15000));\n   call_order_id_type call_eur_id = call_eur.id;\n   const call_order_object& call_cny = *borrow( borrower, bitcny.amount(1000), asset(15000));\n   call_order_id_type call_cny_id = call_cny.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call_usd2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call_usd2_id = call_usd2.id;\n   const call_order_object& call_eur2 = *borrow( borrower2, biteur.amount(1000), asset(15500));\n   call_order_id_type call_eur2_id = call_eur2.id;\n   const call_order_object& call_cny2 = *borrow( borrower2, bitcny.amount(1000), asset(15500));\n   call_order_id_type call_cny2_id = call_cny2.id;\n   // create yet another position with 320% collateral, call price is 16/1.75 CORE/USD = 64/7\n   const call_order_object& call_usd3 = *borrow( borrower3, bitusd.amount(1000), asset(16000));\n   call_order_id_type call_usd3_id = call_usd3.id;\n   const call_order_object& call_eur3 = *borrow( borrower3, biteur.amount(1000), asset(16000));\n   call_order_id_type call_eur3_id = call_eur3.id;\n   const call_order_object& call_cny3 = *borrow( borrower3, bitcny.amount(1000), asset(16000));\n   call_order_id_type call_cny3_id = call_cny3.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n   transfer(borrower, seller, biteur.amount(1000));\n   transfer(borrower2, seller, biteur.amount(1000));\n   transfer(borrower3, seller, biteur.amount(1000));\n   transfer(borrower, seller, bitcny.amount(1000));\n   transfer(borrower2, seller, bitcny.amount(1000));\n   transfer(borrower3, seller, bitcny.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call_usd.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call_usd.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call_usd2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call_usd2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call_usd3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call_usd3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 1000, call_eur.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call_eur.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call_eur2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call_eur2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call_eur3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call_eur3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, biteur) );\n   BOOST_CHECK_EQUAL( 1000, call_cny.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call_cny.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call_cny2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call_cny2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call_cny3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call_cny3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitcny) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   current_feed.settlement_price = biteur.amount( 1 ) / core.amount(10);\n   publish_feed( biteur, feedproducer, current_feed );\n   current_feed.settlement_price = bitcny.amount( 1 ) / core.amount(10);\n   publish_feed( bitcny, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This order below the call price will not be matched before hard fork: 1/8 #606\n   limit_order_id_type sell_usd_low = create_sell_order(seller, bitusd.amount(1000), core.amount(7000))->id;\n   // This is a big order, price below the call price will not be matched before hard fork: 1007/9056 = 1/8 #606\n   limit_order_id_type sell_usd_low2 = create_sell_order(seller, bitusd.amount(1007), core.amount(8056))->id;\n   // This order above the MSSP will not be matched before hard fork\n   limit_order_id_type sell_usd_high = create_sell_order(seller, bitusd.amount(7), core.amount(78))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_usd_med = create_sell_order(seller, bitusd.amount(700), core.amount(6400))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_usd_med2 = create_sell_order(seller, bitusd.amount(7), core.amount(65))->id;\n\n   // This order below the call price will not be matched before hard fork: 1/8 #606\n   limit_order_id_type sell_eur_low = create_sell_order(seller, biteur.amount(1000), core.amount(7000))->id;\n   // This is a big order, price below the call price will not be matched before hard fork: 1007/9056 = 1/8 #606\n   limit_order_id_type sell_eur_low2 = create_sell_order(seller, biteur.amount(1007), core.amount(8056))->id;\n   // This order above the MSSP will not be matched before hard fork\n   limit_order_id_type sell_eur_high = create_sell_order(seller, biteur.amount(7), core.amount(78))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_eur_med = create_sell_order(seller, biteur.amount(700), core.amount(6400))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_eur_med2 = create_sell_order(seller, biteur.amount(7), core.amount(65))->id;\n\n   // This order below the call price will not be matched before hard fork: 1/8 #606\n   limit_order_id_type sell_cny_low = create_sell_order(seller, bitcny.amount(1000), core.amount(7000))->id;\n   // This is a big order, price below the call price will not be matched before hard fork: 1007/9056 = 1/8 #606\n   limit_order_id_type sell_cny_low2 = create_sell_order(seller, bitcny.amount(1007), core.amount(8056))->id;\n   // This order above the MSSP will not be matched before hard fork\n   limit_order_id_type sell_cny_high = create_sell_order(seller, bitcny.amount(7), core.amount(78))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_cny_med = create_sell_order(seller, bitcny.amount(700), core.amount(6400))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_cny_med2 = create_sell_order(seller, bitcny.amount(7), core.amount(65))->id;\n\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7-700-7, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7-700-7, get_balance(seller_id, eur_id) );\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7-700-7, get_balance(seller_id, cny_id) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // generate a block to include operations above\n   generate_block();\n   // go over the hard fork, make sure feed doesn't expire\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   // sell_low and call should get matched first\n   BOOST_CHECK( !db.find<limit_order_object>( sell_usd_low ) );\n   BOOST_CHECK( !db.find<call_order_object>( call_usd_id ) );\n   // sell_low2 and call2 should get matched\n   BOOST_CHECK( !db.find<call_order_object>( call_usd2_id ) );\n   // sell_low2 and call3 should get matched: fixed #453\n   BOOST_CHECK( !db.find<limit_order_object>( sell_usd_low2 ) );\n   // sell_med and call3 should get matched\n   BOOST_CHECK( !db.find<limit_order_object>( sell_usd_med ) );\n   // call3 now is not at margin call state, so sell_med2 won't get matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_usd_med2 )->for_sale.value, 7 );\n   // sell_high should still be there, didn't match anything\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_usd_high )->for_sale.value, 7 );\n\n   // sell_low and call should get matched first\n   BOOST_CHECK( !db.find<limit_order_object>( sell_eur_low ) );\n   BOOST_CHECK( !db.find<call_order_object>( call_eur_id ) );\n   // sell_low2 and call2 should get matched\n   BOOST_CHECK( !db.find<call_order_object>( call_eur2_id ) );\n   // sell_low2 and call3 should get matched: fixed #453\n   BOOST_CHECK( !db.find<limit_order_object>( sell_eur_low2 ) );\n   // sell_med and call3 should get matched\n   BOOST_CHECK( !db.find<limit_order_object>( sell_eur_med ) );\n   // call3 now is not at margin call state, so sell_med2 won't get matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_eur_med2 )->for_sale.value, 7 );\n   // sell_high should still be there, didn't match anything\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_eur_high )->for_sale.value, 7 );\n\n   // sell_low and call should get matched first\n   BOOST_CHECK( !db.find<limit_order_object>( sell_cny_low ) );\n   BOOST_CHECK( !db.find<call_order_object>( call_cny_id ) );\n   // sell_low2 and call2 should get matched\n   BOOST_CHECK( !db.find<call_order_object>( call_cny2_id ) );\n   // sell_low2 and call3 should get matched: fixed #453\n   BOOST_CHECK( !db.find<limit_order_object>( sell_cny_low2 ) );\n   // sell_med and call3 should get matched\n   BOOST_CHECK( !db.find<limit_order_object>( sell_cny_med ) );\n   // call3 now is not at margin call state, so sell_med2 won't get matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_cny_med2 )->for_sale.value, 7 );\n   // sell_high should still be there, didn't match anything\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_cny_high )->for_sale.value, 7 );\n\n   // all match price would be limit order price\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7-700-7, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7-700-7, get_balance(seller_id, eur_id) );\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7-700-7, get_balance(seller_id, cny_id) );\n   BOOST_CHECK_EQUAL( (7000+8056+6400)*3, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 1000-7-700, call_usd3_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 16000-56-6400, call_usd3_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 1000-7-700, call_eur3_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 16000-56-6400, call_eur3_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 1000-7-700, call_cny3_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 16000-56-6400, call_cny3_id(db).collateral.value );\n   // call3's call_price should be updated: 9544/293/1.75 = 9544*4 / 293*7 = 38176/2051 CORE/USD\n   BOOST_CHECK( price(asset(38176),asset(2051,usd_id)) == call_usd3_id(db).call_price );\n   BOOST_CHECK( price(asset(38176),asset(2051,eur_id)) == call_eur3_id(db).call_price );\n   BOOST_CHECK( price(asset(38176),asset(2051,cny_id)) == call_cny3_id(db).call_price );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #338 #453 #606: multiple order matching with black swan\n */\nBOOST_AUTO_TEST_CASE(hard_fork_338_cross_test)\n{ try { // create orders before hard fork, which will be matched on hard fork\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_338_TIME - mi); // assume all hard forks occur at same time\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n   asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 320% collateral, call price is 16/1.75 CORE/USD = 64/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(16000));\n   call_order_id_type call3_id = call3.id;\n   // create yet another position with 400% collateral, call price is 20/1.75 CORE/USD = 80/7\n   const call_order_object& call4 = *borrow( borrower4, bitusd.amount(1000), asset(20000));\n   call_order_id_type call4_id = call4.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This order below the call price will not be matched before hard fork: 1/8 #606\n   limit_order_id_type sell_low = create_sell_order(seller, bitusd.amount(1000), core.amount(7000))->id;\n   // This is a big order, price below the call price will not be matched before hard fork: 1007/9056 = 1/8 #606\n   limit_order_id_type sell_low2 = create_sell_order(seller, bitusd.amount(1007), core.amount(8056))->id;\n   // This would match but is blocked by sell_low?! #606\n   limit_order_id_type sell_med = create_sell_order(seller, bitusd.amount(7), core.amount(64))->id;\n\n   // adjust price feed to get call_order into black swan territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(16);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/16, mssp = 10/176\n\n   // due to sell_low, black swan won't occur\n   BOOST_CHECK( !usd_id(db).bitasset_data(db).has_settlement() );\n\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // generate a block to include operations above\n   generate_block();\n   // go over the hard fork, make sure feed doesn't expire\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   // sell_low and call should get matched first\n   BOOST_CHECK( !db.find<limit_order_object>( sell_low ) );\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) );\n   // sell_low2 and call2 should get matched\n   BOOST_CHECK( !db.find<call_order_object>( call2_id ) );\n   // sell_low2 and call3 should get matched: fixed #453\n   BOOST_CHECK( !db.find<limit_order_object>( sell_low2 ) );\n   // sell_med and call3 should get matched\n   BOOST_CHECK( !db.find<limit_order_object>( sell_med ) );\n\n   // at this moment,\n   // collateralization of call3 is (16000-56-64) / (1000-7-7) = 15880/986 = 16.1, it's > 16 but < 17.6\n   // although there is no sell order, it should trigger a black swan event right away,\n   // because after hard fork new limit order won't trigger black swan event\n   BOOST_CHECK( usd_id(db).bitasset_data(db).has_settlement() );\n   BOOST_CHECK( !db.find<call_order_object>( call3_id ) );\n   BOOST_CHECK( !db.find<call_order_object>( call4_id ) );\n\n   // since 16.1 > 16, global settlement should at feed price 16/1\n   // so settlement fund should be 986*16 + 1000*16\n   BOOST_CHECK_EQUAL( 1986*16, usd_id(db).bitasset_data(db).settlement_fund.value );\n   // global settlement price should be 16/1, since no rounding here\n   BOOST_CHECK( price(asset(1,usd_id),asset(16) ) == usd_id(db).bitasset_data(db).settlement_price );\n\n   BOOST_CHECK_EQUAL( 3000-1000-1007-7, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 7000+8056+64, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower3_id, usd_id) );\n   BOOST_CHECK_EQUAL( init_balance-16000+15880-986*16, get_balance(borrower3_id, core_id) );\n   BOOST_CHECK_EQUAL( 1000, get_balance(borrower4_id, usd_id) );\n   BOOST_CHECK_EQUAL( init_balance-1000*16, get_balance(borrower4_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #649: Black swan detection fetch call order by call_price but not collateral ratio\n */\nBOOST_AUTO_TEST_CASE(hard_fork_649_cross_test)\n{ try { // create orders before hard fork, which will be matched on hard fork\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_343_TIME - mi); // assume all hard forks occur at same time\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n   asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 320% collateral, call price is 16/1.75 CORE/USD = 64/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(16000));\n   call_order_id_type call3_id = call3.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 16000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This would match with call at price 707/6464\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(707), core.amount(6464)) );\n   BOOST_CHECK_EQUAL( 3000-707, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 6464, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 293, call.debt.value );\n   BOOST_CHECK_EQUAL( 8536, call.collateral.value );\n\n   // at this moment,\n   // collateralization of call is 8536 / 293 = 29.1\n   // collateralization of call2 is 15500 / 1000 = 15.5\n   // collateralization of call3 is 16000 / 1000 = 16\n\n   generate_block();\n   set_expiration( db, trx );\n   update_feed_producers( usd_id(db), {feedproducer_id} );\n\n   // adjust price feed to get call_order into black swan territory\n   current_feed.settlement_price = price(asset(1,usd_id) / asset(20));\n   publish_feed( usd_id(db), feedproducer_id(db), current_feed );\n   // settlement price = 1/20, mssp = 1/22\n\n   // due to #649, black swan won't occur\n   BOOST_CHECK( !usd_id(db).bitasset_data(db).has_settlement() );\n\n   // generate a block to include operations above\n   generate_block();\n   BOOST_CHECK( !usd_id(db).bitasset_data(db).has_settlement() );\n   // go over the hard fork, make sure feed doesn't expire\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   // a black swan event should occur\n   BOOST_CHECK( usd_id(db).bitasset_data(db).has_settlement() );\n   BOOST_CHECK( !db.find<call_order_object>( call_id ) );\n   BOOST_CHECK( !db.find<call_order_object>( call2_id ) );\n   BOOST_CHECK( !db.find<call_order_object>( call3_id ) );\n\n   // since least collateral ratio 15.5 < 20, global settlement should execute at price = least collateral ratio 15.5/1\n   // so settlement fund should be 15500 + 15500 + round_up(15.5 * 293)\n   BOOST_CHECK_EQUAL( 15500*2 + (293 * 155 + 9) / 10, usd_id(db).bitasset_data(db).settlement_fund.value );\n   // global settlement price should be settlement_fund/(2000+293), but not 15.5/1 due to rounding\n   BOOST_CHECK( price(asset(2293,usd_id),asset(15500*2+(293*155+9)/10) ) == usd_id(db).bitasset_data(db).settlement_price );\n\n   BOOST_CHECK_EQUAL( 3000-707, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 6464, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower_id, usd_id) );\n   BOOST_CHECK_EQUAL( init_balance-6464-(293*155+9)/10, get_balance(borrower_id, core_id) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2_id, usd_id) );\n   BOOST_CHECK_EQUAL( init_balance-15500, get_balance(borrower2_id, core_id) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower3_id, usd_id) );\n   BOOST_CHECK_EQUAL( init_balance-15500, get_balance(borrower3_id, core_id) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * Fixed bitshares-core issue #343: change sorting of call orders when matching against limit order\n */\nBOOST_AUTO_TEST_CASE(hard_fork_343_cross_test)\n{ try { // create orders before hard fork, which will be matched on hard fork\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_343_TIME - mi); // assume all hard forks occur at same time\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(borrower4)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n   asset_id_type usd_id = bitusd.id;\n   asset_id_type core_id = core.id;\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   transfer(committee_account, borrower4_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000));\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500));\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 350% collateral, call price is 17.5/1.75 CORE/USD = 77/7\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(17500));\n   call_order_id_type call3_id = call3.id;\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // adjust price feed to get call_order into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This would match with call at price 700/6400\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700), core.amount(6400)) );\n   BOOST_CHECK_EQUAL( 3000-700, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 6400, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 300, call.debt.value );\n   BOOST_CHECK_EQUAL( 8600, call.collateral.value );\n\n   // at this moment,\n   // collateralization of call is 8600 / 300 = 28.67\n   // collateralization of call2 is 15500 / 1000 = 15.5\n   // collateralization of call3 is 17500 / 1000 = 17.5\n\n   // generate a block to include operations above\n   generate_block();\n   // go over the hard fork, make sure feed doesn't expire\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   // This will match with call2 at price 7/77 (#343 fixed)\n   BOOST_CHECK( !create_sell_order(seller_id(db), asset(7*50,usd_id), asset(65*50)) );\n   BOOST_CHECK_EQUAL( 3000-700-7*50, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 6400+77*50, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 300, call_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 8600, call_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 1000-7*50, call2_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 15500-77*50, call2_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 17500, call3_id(db).collateral.value );\n\n   // at this moment,\n   // collateralization of call is 8600 / 300 = 28.67\n   // collateralization of call2 is 11650 / 650 = 17.9\n   // collateralization of call3 is 17500 / 1000 = 17.5\n\n   // This will match with call3 at price 7/77 (#343 fixed)\n   BOOST_CHECK( !create_sell_order(seller_id(db), asset(7,usd_id), asset(65)) );\n   BOOST_CHECK_EQUAL( 3000-700-7*50-7, get_balance(seller_id, usd_id) );\n   BOOST_CHECK_EQUAL( 6400+77*50+77, get_balance(seller_id, core_id) );\n   BOOST_CHECK_EQUAL( 300, call_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 8600, call_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 1000-7*50, call2_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 15500-77*50, call2_id(db).collateral.value );\n   BOOST_CHECK_EQUAL( 1000-7, call3_id(db).debt.value );\n   BOOST_CHECK_EQUAL( 17500-77, call3_id(db).collateral.value );\n\n   // at this moment,\n   // collateralization of call is 8600 / 300 = 28.67\n   // collateralization of call2 is 11650 / 650 = 17.9\n   // collateralization of call3 is 17423 / 993 = 17.55\n\n   // no more margin call now\n   BOOST_CHECK( create_sell_order(seller_id(db), asset(7,usd_id), asset(65)) );\n\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * BSIP38 \"target_collateral_ratio\" test: matching a taker limit order with multiple maker call orders\n */\nBOOST_AUTO_TEST_CASE(target_cr_test_limit_call)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n   if(hf1270)\n      generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   else\n      generate_blocks(HARDFORK_CORE_834_TIME - mi);\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(buyer2)(buyer3)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, buyer2_id, asset(init_balance));\n   transfer(committee_account, buyer3_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7, tcr 170% is lower than 175%\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000), 1700);\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7, tcr 200% is higher than 175%\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500), 2000);\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 500% collateral, call price is 25/1.75 CORE/USD = 100/7, no tcr\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(25000));\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 15000, get_balance(borrower, core) );\n   BOOST_CHECK_EQUAL( init_balance - 15500, get_balance(borrower2, core) );\n   BOOST_CHECK_EQUAL( init_balance - 25000, get_balance(borrower3, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower3, bitusd) );\n\n   // adjust price feed to get call and call2 (but not call3) into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // This sell order above MSSP will not be matched with a call\n   limit_order_id_type sell_high = create_sell_order(seller, bitusd.amount(7), core.amount(78))->id;\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_high )->for_sale.value, 7 );\n\n   BOOST_CHECK_EQUAL( 2993, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // This buy order is too low will not be matched with a sell order\n   limit_order_id_type buy_low = create_sell_order(buyer, asset(80), bitusd.amount(10))->id;\n   // This buy order at MSSP will be matched only if no margin call (margin call takes precedence)\n   limit_order_id_type buy_med = create_sell_order(buyer2, asset(33000), bitusd.amount(3000))->id;\n   // This buy order above MSSP will be matched with a sell order (limit order with better price takes precedence)\n   limit_order_id_type buy_high = create_sell_order(buyer3, asset(111), bitusd.amount(10))->id;\n\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer3, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 80, get_balance(buyer, core) );\n   BOOST_CHECK_EQUAL( init_balance - 33000, get_balance(buyer2, core) );\n   BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) );\n\n   // call and call2's CR is quite high, and debt amount is quite a lot, assume neither of them will be completely filled\n   price match_price( bitusd.amount(1) / core.amount(11) );\n   share_type call_to_cover = call_id(db).get_max_debt_to_cover(match_price,current_feed.settlement_price,1750);\n   share_type call2_to_cover = call2_id(db).get_max_debt_to_cover(match_price,current_feed.settlement_price,1750);\n   BOOST_CHECK_LT( call_to_cover.value, call_id(db).debt.value );\n   BOOST_CHECK_LT( call2_to_cover.value, call2_id(db).debt.value );\n   // even though call2 has a higher CR, since call's TCR is less than call2's TCR, so we expect call will cover less when called\n   BOOST_CHECK_LT( call_to_cover.value, call2_to_cover.value );\n\n   // Create a big sell order slightly below the call price, will be matched with several orders\n   BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700*4), core.amount(5900*4) ) );\n\n   // firstly it will match with buy_high, at buy_high's price\n   BOOST_CHECK( !db.find<limit_order_object>( buy_high ) );\n   // buy_high pays 111 CORE, receives 10 USD goes to buyer3's balance\n   BOOST_CHECK_EQUAL( 10, get_balance(buyer3, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) );\n\n   // then it will match with call, at mssp: 1/11 = 1000/11000\n   const call_order_object* tmp_call = db.find<call_order_object>( call_id );\n   BOOST_CHECK( tmp_call != nullptr );\n\n   // call will receive call_to_cover, pay 11*call_to_cover\n   share_type call_to_pay = call_to_cover * 11;\n   BOOST_CHECK_EQUAL( 1000 - call_to_cover.value, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000 - call_to_pay.value, call.collateral.value );\n   // new collateral ratio should be higher than mcr as well as tcr\n   BOOST_CHECK( call.debt.value * 10 * 1750 < call.collateral.value * 1000 );\n   idump( (call) );\n   // borrower's balance doesn't change\n   BOOST_CHECK_EQUAL( init_balance - 15000, get_balance(borrower, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n\n   // the limit order then will match with call2, at mssp: 1/11 = 1000/11000\n   const call_order_object* tmp_call2 = db.find<call_order_object>( call2_id );\n   BOOST_CHECK( tmp_call2 != nullptr );\n\n   // call2 will receive call2_to_cover, pay 11*call2_to_cover\n   share_type call2_to_pay = call2_to_cover * 11;\n   BOOST_CHECK_EQUAL( 1000 - call2_to_cover.value, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500 - call2_to_pay.value, call2.collateral.value );\n   // new collateral ratio should be higher than mcr as well as tcr\n   BOOST_CHECK( call2.debt.value * 10 * 2000 < call2.collateral.value * 1000 );\n   idump( (call2) );\n   // borrower2's balance doesn't change\n   BOOST_CHECK_EQUAL( init_balance - 15500, get_balance(borrower2, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2, bitusd) );\n\n   // then it will match with buy_med, at buy_med's price. Since buy_med is too big, it's partially filled.\n   // buy_med receives the remaining USD of sell order, minus market fees, goes to buyer2's balance\n   share_type buy_med_get = 700*4 - 10 - call_to_cover - call2_to_cover;\n   share_type buy_med_pay = buy_med_get * 11; // buy_med pays at 1/11\n   buy_med_get -= (buy_med_get/100); // minus 1% market fee\n   BOOST_CHECK_EQUAL( buy_med_get.value, get_balance(buyer2, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 33000, get_balance(buyer2, core) );\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_med )->for_sale.value, 33000-buy_med_pay.value );\n\n   // call3 is not in margin call territory so won't be matched\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n\n   // buy_low's price is too low that won't be matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 80 );\n\n   // check seller balance\n   BOOST_CHECK_EQUAL( 193, get_balance(seller, bitusd) ); // 3000 - 7 - 700*4\n   BOOST_CHECK_EQUAL( 30801, get_balance(seller, core) ); // 111 + (700*4-10)*11\n\n   // Cancel buy_med\n   cancel_limit_order( buy_med(db) );\n   BOOST_CHECK( !db.find<limit_order_object>( buy_med ) );\n   BOOST_CHECK_EQUAL( buy_med_get.value, get_balance(buyer2, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - buy_med_pay.value, get_balance(buyer2, core) );\n\n   // Create another sell order slightly below the call price, won't fill\n   limit_order_id_type sell_med = create_sell_order( seller, bitusd.amount(7), core.amount(59) )->id;\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_med )->for_sale.value, 7 );\n   // check seller balance\n   BOOST_CHECK_EQUAL( 193-7, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 30801, get_balance(seller, core) );\n\n   // call3 is not in margin call territory so won't be matched\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n\n   // buy_low's price is too low that won't be matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 80 );\n\n   // generate a block\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\n/***\n * BSIP38 \"target_collateral_ratio\" test: matching a maker limit order with multiple taker call orders\n */\nBOOST_AUTO_TEST_CASE(target_cr_test_call_limit)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n\n   if(hf1270)\n      generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   else\n      generate_blocks(HARDFORK_CORE_834_TIME - mi);\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   set_expiration( db, trx );\n\n   ACTORS((buyer)(seller)(borrower)(borrower2)(borrower3)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, buyer_id, asset(init_balance));\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   transfer(committee_account, borrower3_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio = 1100;\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(5);\n   publish_feed( bitusd, feedproducer, current_feed );\n   // start out with 300% collateral, call price is 15/1.75 CORE/USD = 60/7, tcr 170% is lower than 175%\n   const call_order_object& call = *borrow( borrower, bitusd.amount(1000), asset(15000), 1700);\n   call_order_id_type call_id = call.id;\n   // create another position with 310% collateral, call price is 15.5/1.75 CORE/USD = 62/7, tcr 200% is higher than 175%\n   const call_order_object& call2 = *borrow( borrower2, bitusd.amount(1000), asset(15500), 2000);\n   call_order_id_type call2_id = call2.id;\n   // create yet another position with 500% collateral, call price is 25/1.75 CORE/USD = 100/7, no tcr\n   const call_order_object& call3 = *borrow( borrower3, bitusd.amount(1000), asset(25000));\n   transfer(borrower, seller, bitusd.amount(1000));\n   transfer(borrower2, seller, bitusd.amount(1000));\n   transfer(borrower3, seller, bitusd.amount(1000));\n\n   BOOST_CHECK_EQUAL( 1000, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000, call.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500, call2.collateral.value );\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n   BOOST_CHECK_EQUAL( 3000, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 15000, get_balance(borrower, core) );\n   BOOST_CHECK_EQUAL( init_balance - 15500, get_balance(borrower2, core) );\n   BOOST_CHECK_EQUAL( init_balance - 25000, get_balance(borrower3, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower3, bitusd) );\n\n   // This sell order above MSSP will not be matched with a call\n   limit_order_id_type sell_high = create_sell_order(seller, bitusd.amount(7), core.amount(78))->id;\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_high )->for_sale.value, 7 );\n\n   BOOST_CHECK_EQUAL( 2993, get_balance(seller, bitusd) );\n   BOOST_CHECK_EQUAL( 0, get_balance(seller, core) );\n\n   // This buy order is too low will not be matched with a sell order\n   limit_order_id_type buy_low = create_sell_order(buyer, asset(80), bitusd.amount(10))->id;\n\n   BOOST_CHECK_EQUAL( 0, get_balance(buyer, bitusd) );\n   BOOST_CHECK_EQUAL( init_balance - 80, get_balance(buyer, core) );\n\n   // Create a sell order which will be matched with several call orders later, price 1/9\n   limit_order_id_type sell_id = create_sell_order(seller, bitusd.amount(500), core.amount(4500) )->id;\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( sell_id )->for_sale.value, 500 );\n\n   // prepare price feed to get call and call2 (but not call3) into margin call territory\n   current_feed.settlement_price = bitusd.amount( 1 ) / core.amount(10);\n\n   // call and call2's CR is quite high, and debt amount is quite a lot, assume neither of them will be completely filled\n   price match_price = sell_id(db).sell_price;\n   share_type call_to_cover = call_id(db).get_max_debt_to_cover(match_price,current_feed.settlement_price,1750);\n   share_type call2_to_cover = call2_id(db).get_max_debt_to_cover(match_price,current_feed.settlement_price,1750);\n   BOOST_CHECK_LT( call_to_cover.value, call_id(db).debt.value );\n   BOOST_CHECK_LT( call2_to_cover.value, call2_id(db).debt.value );\n   // even though call2 has a higher CR, since call's TCR is less than call2's TCR, so we expect call will cover less when called\n   BOOST_CHECK_LT( call_to_cover.value, call2_to_cover.value );\n\n   // adjust price feed to get call and call2 (but not call3) into margin call territory\n   publish_feed( bitusd, feedproducer, current_feed );\n   // settlement price = 1/10, mssp = 1/11\n\n   // firstly the limit order will match with call, at limit order's price: 1/9\n   const call_order_object* tmp_call = db.find<call_order_object>( call_id );\n   BOOST_CHECK( tmp_call != nullptr );\n\n   // call will receive call_to_cover, pay 9*call_to_cover\n   share_type call_to_pay = call_to_cover * 9;\n   BOOST_CHECK_EQUAL( 1000 - call_to_cover.value, call.debt.value );\n   BOOST_CHECK_EQUAL( 15000 - call_to_pay.value, call.collateral.value );\n   // new collateral ratio should be higher than mcr as well as tcr\n   BOOST_CHECK( call.debt.value * 10 * 1750 < call.collateral.value * 1000 );\n   idump( (call) );\n   // borrower's balance doesn't change\n   BOOST_CHECK_EQUAL( init_balance - 15000, get_balance(borrower, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower, bitusd) );\n\n   // the limit order then will match with call2, at limit order's price: 1/9\n   const call_order_object* tmp_call2 = db.find<call_order_object>( call2_id );\n   BOOST_CHECK( tmp_call2 != nullptr );\n\n   // if the limit is big enough, call2 will receive call2_to_cover, pay 11*call2_to_cover\n   // however it's not the case, so call2 will receive less\n   call2_to_cover = 500 - call_to_cover;\n   share_type call2_to_pay = call2_to_cover * 9;\n   BOOST_CHECK_EQUAL( 1000 - call2_to_cover.value, call2.debt.value );\n   BOOST_CHECK_EQUAL( 15500 - call2_to_pay.value, call2.collateral.value );\n   idump( (call2) );\n   // borrower2's balance doesn't change\n   BOOST_CHECK_EQUAL( init_balance - 15500, get_balance(borrower2, core) );\n   BOOST_CHECK_EQUAL( 0, get_balance(borrower2, bitusd) );\n\n   // call3 is not in margin call territory so won't be matched\n   BOOST_CHECK_EQUAL( 1000, call3.debt.value );\n   BOOST_CHECK_EQUAL( 25000, call3.collateral.value );\n\n   // sell_id is completely filled\n   BOOST_CHECK( !db.find<limit_order_object>( sell_id ) );\n\n   // check seller balance\n   BOOST_CHECK_EQUAL( 2493, get_balance(seller, bitusd) ); // 3000 - 7 - 500\n   BOOST_CHECK_EQUAL( 4500, get_balance(seller, core) ); // 500*9\n\n   // buy_low's price is too low that won't be matched\n   BOOST_CHECK_EQUAL( db.find<limit_order_object>( buy_low )->for_sale.value, 80 );\n\n   // generate a block\n   generate_block();\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(mcr_bug_increase_before1270)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_453_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((seller)(borrower)(borrower2)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio  = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800));\n   auto b1_id = b1.id;\n   const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) );\n   auto b2_id = b2.id;\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 );\n\n   // move order to margin call territory with mcr only\n   current_feed.maintenance_collateral_ratio = 2000;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 );\n\n   BOOST_CHECK( db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n   // attempt to trade the margin call\n   create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000  );\n\n   print_market(bitusd.symbol, core.symbol);\n\n   // both calls are still there, no margin call, mcr bug\n   BOOST_CHECK( db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(mcr_bug_increase_after1270)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((seller)(borrower)(borrower2)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio  = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800));\n   auto b1_id = b1.id;\n   const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) );\n   auto b2_id = b2.id;\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 );\n\n   // move order to margin call territory with mcr only\n   current_feed.maintenance_collateral_ratio = 2000;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 );\n\n   BOOST_CHECK( db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n   // attempt to trade the margin call\n   create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998900 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 999100  );\n\n   print_market(bitusd.symbol, core.symbol);\n\n   // b1 is margin called\n   BOOST_CHECK( ! db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(mcr_bug_decrease_before1270)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_453_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((seller)(borrower)(borrower2)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio  = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800));\n   auto b1_id = b1.id;\n   const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) );\n   auto b2_id = b2.id;\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 );\n\n   // move order to margin call territory with the feed\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(150);\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   // getting out of margin call territory with mcr change\n   current_feed.maintenance_collateral_ratio = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 );\n\n   BOOST_CHECK( db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n   // attempt to trade the margin call\n   create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998350 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 999650  );\n\n   print_market(bitusd.symbol, core.symbol);\n\n   // margin call at b1, mcr bug\n   BOOST_CHECK( !db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(mcr_bug_decrease_after1270)\n{ try {\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();\n\n   set_expiration( db, trx );\n\n   ACTORS((seller)(borrower)(borrower2)(feedproducer));\n\n   const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n   const auto& core   = asset_id_type()(db);\n\n   int64_t init_balance(1000000);\n\n   transfer(committee_account, borrower_id, asset(init_balance));\n   transfer(committee_account, borrower2_id, asset(init_balance));\n   update_feed_producers( bitusd, {feedproducer.id} );\n\n   price_feed current_feed;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n   current_feed.maintenance_collateral_ratio = 1750;\n   current_feed.maximum_short_squeeze_ratio  = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800));\n   auto b1_id = b1.id;\n   const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) );\n   auto b2_id = b2.id;\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 );\n\n   // move order to margin call territory with the feed\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(150);\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   // getting out of margin call territory with mcr decrease\n   current_feed.maintenance_collateral_ratio = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 );\n\n   BOOST_CHECK( db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n   // attempt to trade the margin call\n   create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) );\n\n   BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 );\n   BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 );\n   BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000  );\n\n   print_market(bitusd.symbol, core.symbol);\n\n   // both calls are there, no margin call, good\n   BOOST_CHECK( db.find<call_order_object>( b1_id ) );\n   BOOST_CHECK( db.find<call_order_object>( b2_id ) );\n\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(mcr_bug_cross1270)\n{ try {\n\n   INVOKE(mcr_bug_increase_before1270);\n\n   auto mi = db.get_global_properties().parameters.maintenance_interval;\n   generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n\n   const asset_object& core = get_asset(GRAPHENE_SYMBOL);\n   const asset_object& bitusd = get_asset(\"USDBIT\");\n   const asset_id_type bitusd_id = bitusd.id;\n   const account_object& feedproducer = get_account(\"feedproducer\");\n\n   // feed is expired\n   auto mcr = (*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio;\n   BOOST_CHECK_EQUAL(mcr, GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n   // make new feed\n   price_feed current_feed;\n   current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n   current_feed.maintenance_collateral_ratio = 2000;\n   current_feed.maximum_short_squeeze_ratio  = 1100;\n   publish_feed( bitusd, feedproducer, current_feed );\n\n   mcr = (*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio;\n   BOOST_CHECK_EQUAL(mcr, 2000);\n\n   // pass hardfork\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();\n\n   // feed is still valid\n   mcr = (*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio;\n   BOOST_CHECK_EQUAL(mcr, 2000);\n\n   // margin call is traded\n   print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol);\n\n   // call b1 not there anymore\n   BOOST_CHECK( !db.find<call_order_object>( call_order_id_type() ) );\n   BOOST_CHECK( db.find<call_order_object>( call_order_id_type(1) ) );\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(hardfork_core_338_test_after_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(hardfork_core_338_test);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(hardfork_core_453_test_after_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(hardfork_core_453_test);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(hardfork_core_625_big_limit_order_test_after_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(hardfork_core_625_big_limit_order_test);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(target_cr_test_limit_call_after_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(target_cr_test_limit_call);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(target_cr_test_call_limit_after_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(target_cr_test_call_limit);\n\n} FC_LOG_AND_RETHROW() }\n\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/network_broadcast_api_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/api.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE(network_broadcast_api_tests, database_fixture)\n\nBOOST_AUTO_TEST_CASE( broadcast_transaction_with_callback_test ) {\n   try {\n\n      uint32_t called = 0;\n      auto callback = [&]( const variant& v )\n      {\n         ++called;\n      };\n\n      fc::ecc::private_key cid_key = fc::ecc::private_key::regenerate( fc::digest(\"key\") );\n      const account_id_type cid_id = create_account( \"cid\", cid_key.get_public_key() ).id;\n      fund( cid_id(db) );\n\n      auto nb_api = std::make_shared< graphene::app::network_broadcast_api >( app );\n\n      set_expiration( db, trx );\n      transfer_operation trans;\n      trans.from = cid_id;\n      trans.to   = account_id_type();\n      trans.amount = asset(1);\n      trx.operations.push_back( trans );\n      sign( trx, cid_key );\n\n      nb_api->broadcast_transaction_with_callback( callback, trx );\n\n      trx.clear();\n\n      generate_block();\n\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      BOOST_CHECK_EQUAL( called, 1u );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( broadcast_transaction_too_large ) {\n   try {\n\n      fc::ecc::private_key cid_key = fc::ecc::private_key::regenerate( fc::digest(\"key\") );\n      const account_id_type cid_id = create_account( \"cid\", cid_key.get_public_key() ).id;\n      fund( cid_id(db) );\n\n      auto nb_api = std::make_shared< graphene::app::network_broadcast_api >( app );\n\n      generate_blocks( HARDFORK_CORE_1573_TIME + 10 );\n\n      set_expiration( db, trx );\n      transfer_operation trans;\n      trans.from = cid_id;\n      trans.to   = account_id_type();\n      trans.amount = asset(1);\n      for(int i = 0; i < 250; ++i )\n         trx.operations.push_back( trans );\n      sign( trx, cid_key );\n\n      BOOST_CHECK_THROW( nb_api->broadcast_transaction( trx ), fc::exception );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/operation_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n#include <boost/assign/list_of.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/vesting_balance_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n\n#include <graphene/market_history/market_history_plugin.hpp>\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\n#define UIA_TEST_SYMBOL \"UIATEST\"\n\nBOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( feed_limit_logic_test )\n{\n   try {\n      asset usd(1000,asset_id_type(1));\n      asset core(1000,asset_id_type(0));\n      price_feed feed;\n      feed.settlement_price = usd / core;\n\n      // require 3x min collateral\n      auto swanp = usd / core;\n      auto callp = ~price::call_price( usd, core, 1750 );\n      // 1:1 collateral\n//      wdump((callp.to_real())(callp));\n//      wdump((swanp.to_real())(swanp));\n      FC_ASSERT( callp.to_real() > swanp.to_real() );\n\n      /*\n      wdump((feed.settlement_price.to_real()));\n      wdump((feed.maintenance_price().to_real()));\n      wdump((feed.max_short_squeeze_price().to_real()));\n\n      BOOST_CHECK( usd * feed.settlement_price < usd * feed.maintenance_price() );\n      BOOST_CHECK( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() );\n      */\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( call_order_update_test )\n{\n   try {\n\n      ACTORS((dan)(sam));\n      const auto& bitusd = create_bitasset(\"USDBIT\", sam.id);\n      const auto& core   = asset_id_type()(db);\n\n      transfer(committee_account, dan_id, asset(10000000));\n      transfer(committee_account, sam_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      publish_feed( bitusd, sam, current_feed );\n\n      FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );\n\n      BOOST_TEST_MESSAGE( \"covering 2500 usd and freeing 5000 core...\" );\n      cover( dan, bitusd.amount(2500), asset(5000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 5000  );\n\n      BOOST_TEST_MESSAGE( \"verifying that attempting to cover the full amount without claiming the collateral fails\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0)  ), fc::exception );\n\n      cover( dan, bitusd.amount(2500), core.amount(5000));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 0 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000  );\n\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000  );\n\n\n      // test just increasing collateral\n      BOOST_TEST_MESSAGE( \"increasing collateral\" );\n      borrow( dan, bitusd.amount(0), asset(10000));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000  );\n\n      // test just decreasing debt\n      BOOST_TEST_MESSAGE( \"decreasing debt\" );\n      cover( dan, bitusd.amount(1000), asset(0));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 4000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000  );\n\n      BOOST_TEST_MESSAGE( \"increasing debt without increasing collateral\" );\n      borrow( dan, bitusd.amount(1000), asset(0));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000  );\n\n      BOOST_TEST_MESSAGE( \"increasing debt a lot without increasing collateral, fails due to black swan\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0)), fc::exception );\n      BOOST_TEST_MESSAGE( \"attempting to claim most of collateral without paying off debt, fails due to black swan\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000-1)), fc::exception );\n      BOOST_TEST_MESSAGE( \"attempting to claim all collateral without paying off debt\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000)), fc::exception );\n\n      borrow( sam, bitusd.amount(1000), asset(10000));\n      transfer( sam, dan, bitusd.amount(1000) );\n\n      BOOST_TEST_MESSAGE( \"attempting to claim more collateral than available\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(4000), asset(20001)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(4000), asset(20100)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(4000), asset(30000)), fc::exception );\n\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(5000), asset(20001)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(5000), asset(20100)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(5000), asset(30000)), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"attempting to pay more debt than required\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(15000)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(20000)), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"attempting to pay more debt than required, and claim more collateral than available\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(20001)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(40000)), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"attempting reduce collateral without paying off any debt\" );\n      cover( dan, bitusd.amount(0), asset(1000));\n\n      BOOST_TEST_MESSAGE( \"attempting change call price to be below minimum for debt/collateral ratio\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0)), fc::exception );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( old_call_order_update_test_after_hardfork_583 )\n{\n   try {\n\n      auto hf_time = HARDFORK_CORE_583_TIME;\n      if( bsip77 )\n         hf_time = HARDFORK_BSIP_77_TIME;\n      generate_blocks( hf_time );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((dan)(sam));\n      const auto& bitusd = create_bitasset(\"USDBIT\", sam.id);\n      const auto& core   = asset_id_type()(db);\n\n      transfer(committee_account, dan_id, asset(10000000));\n      transfer(committee_account, sam_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      publish_feed( bitusd, sam, current_feed );\n\n      FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );\n\n      BOOST_TEST_MESSAGE( \"covering 2500 usd and freeing 5000 core...\" );\n      cover( dan, bitusd.amount(2500), asset(5000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 5000  );\n\n      BOOST_TEST_MESSAGE( \"verifying that attempting to cover the full amount without claiming the collateral fails\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0)  ), fc::exception );\n\n      cover( dan, bitusd.amount(2500), core.amount(5000));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 0 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000  );\n\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000  );\n\n\n      // test just increasing collateral\n      BOOST_TEST_MESSAGE( \"increasing collateral\" );\n      borrow( dan, bitusd.amount(0), asset(10000));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000  );\n\n      // test just decreasing debt\n      BOOST_TEST_MESSAGE( \"decreasing debt\" );\n      cover( dan, bitusd.amount(1000), asset(0));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 4000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000  );\n\n      BOOST_TEST_MESSAGE( \"increasing debt without increasing collateral\" );\n      borrow( dan, bitusd.amount(1000), asset(0));\n\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000  );\n\n      BOOST_TEST_MESSAGE( \"increasing debt a lot without increasing collateral, fails due to black swan\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0)), fc::exception );\n      BOOST_TEST_MESSAGE( \"attempting to claim most of collateral without paying off debt, fails due to black swan\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000-1)), fc::exception );\n      BOOST_TEST_MESSAGE( \"attempting to claim all collateral without paying off debt\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000)), fc::exception );\n\n      borrow( sam, bitusd.amount(1000), asset(10000));\n      transfer( sam, dan, bitusd.amount(1000) );\n\n      BOOST_TEST_MESSAGE( \"attempting to claim more collateral than available\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(4000), asset(20001)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(4000), asset(20100)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(4000), asset(30000)), fc::exception );\n\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(5000), asset(20001)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(5000), asset(20100)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(5000), asset(30000)), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"attempting to pay more debt than required\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(15000)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(20000)), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"attempting to pay more debt than required, and claim more collateral than available\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(20001)), fc::exception );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(6000), asset(40000)), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"attempting reduce collateral without paying off any debt\" );\n      cover( dan, bitusd.amount(0), asset(1000));\n\n      BOOST_TEST_MESSAGE( \"attempting change call price to be below minimum for debt/collateral ratio\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0)), fc::exception );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( asset_settle_cancel_operation_test_after_hf588 )\n{\n   set_expiration( db, trx );\n\n   BOOST_TEST_MESSAGE( \"Creating a proposal containing a asset_settle_cancel_operation\" );\n   {\n      proposal_create_operation pcop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time());\n      pcop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      pcop.expiration_time = db.head_block_time() + *pcop.review_period_seconds + 10;\n      asset_settle_cancel_operation ascop;\n      ascop.amount.amount = 1;\n      pcop.proposed_ops.emplace_back(ascop);\n      trx.operations.push_back(pcop);\n\n      BOOST_CHECK_EXCEPTION(PUSH_TX(db, trx), fc::assert_exception,\n            [](fc::assert_exception const &e) -> bool {\n               std::cout << e.to_string() << std::endl;\n               if (e.to_string().find(\"Virtual operation\") != std::string::npos)\n                  return true;\n\n               return false;\n            });\n   }\n\n   BOOST_TEST_MESSAGE( \"Creating a recursive proposal containing asset_settle_cancel_operation\" );\n   {\n      proposal_create_operation pcop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time());\n\n      pcop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      pcop.expiration_time = db.head_block_time() + *pcop.review_period_seconds + 10;\n      proposal_create_operation inner_pcop = proposal_create_operation::committee_proposal(\n            db.get_global_properties().parameters, db.head_block_time());\n\n      inner_pcop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n      inner_pcop.expiration_time = db.head_block_time() + *inner_pcop.review_period_seconds + 10;\n\n      asset_settle_cancel_operation ascop;\n      ascop.amount.amount = 1;\n      inner_pcop.proposed_ops.emplace_back(ascop);\n      pcop.proposed_ops.emplace_back(inner_pcop);\n\n      trx.operations.push_back(pcop);\n\n      BOOST_CHECK_EXCEPTION(PUSH_TX(db, trx), fc::assert_exception,\n            [](fc::assert_exception const &e) -> bool {\n               std::cout << e.to_string() << std::endl;\n               if (e.to_string().find(\"Virtual operation\") != std::string::npos)\n                  return true;\n\n               return false;\n            });\n   }\n}\n\n/// Test case for bsip77:\n/// * the \"initial_collateral_ratio\" parameter can only be set after the BSIP77 hard fork\n/// * the parameter should be within a range\n// TODO removed the hard fork part after the hard fork, keep the valid range part\nBOOST_AUTO_TEST_CASE( bsip77_hardfork_time_and_param_valid_range_test )\n{\n   try {\n\n      // Proceeds to a recent hard fork\n      generate_blocks( HARDFORK_CORE_583_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      // Before bsip77 hard fork, unable to create a bitasset with ICR\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 0 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 1 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 1000 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 1001 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 1750 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 32000 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBIT\", sam_id, 100, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 32001 ), fc::exception );\n\n      // Can create a bitasset without ICR\n      const auto& bitusd = create_bitasset( \"USDBIT\", sam.id, 100, charge_market_fee, 2, {},\n                                            GRAPHENE_MAX_SHARE_SUPPLY );\n      asset_id_type usd_id = bitusd.id;\n\n      // helper function for setting ICR for an asset\n      auto set_icr_for_asset = [&](asset_id_type aid, optional<uint16_t> icr) {\n         const asset_object& ao = aid(db);\n         const asset_bitasset_data_object& abo = ao.bitasset_data(db);\n         asset_update_bitasset_operation uop;\n         uop.issuer = ao.issuer;\n         uop.asset_to_update = aid;\n         uop.new_options = abo.options;\n         uop.new_options.extensions.value.initial_collateral_ratio = icr;\n         trx.operations.clear();\n         trx.operations.push_back( uop );\n         trx.validate();\n         set_expiration( db, trx );\n         PUSH_TX(db, trx, ~0);\n      };\n\n      // Before bsip77 hard fork, unable to update a bitasset with ICR\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 0 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 1 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 1000 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 1001 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 1750 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 32000 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 32001 ), fc::exception );\n\n      // helper function for creating a proposal which contains an asset_create_operation with ICR\n      auto propose_create_bitasset = [&]( string name, optional<uint16_t> icr ) {\n         asset_create_operation acop = make_bitasset( name, sam_id, 100, charge_market_fee, 2, {},\n                                                      GRAPHENE_MAX_SHARE_SUPPLY, icr );\n         proposal_create_operation cop;\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.expiration_time = db.head_block_time() + 100;\n         cop.proposed_ops.emplace_back( acop );\n         trx.operations.clear();\n         trx.operations.push_back( cop );\n         trx.validate();\n         set_expiration( db, trx );\n         processed_transaction ptx = PUSH_TX(db, trx, ~0);\n         trx.operations.clear();\n      };\n\n      // Before bsip77 hard fork, unable to create a proposal with an asset_create_operation with ICR\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 0 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 1 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 1000 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 1001 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 1750 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 32000 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITA\", 32001 ), fc::exception );\n\n      // helper function for creating a proposal which contains an asset_update_bitasset_operation with ICR\n      auto propose_set_icr_for_asset = [&](asset_id_type aid, optional<uint16_t> icr) {\n         const asset_object& ao = aid(db);\n         const asset_bitasset_data_object& abo = ao.bitasset_data(db);\n         asset_update_bitasset_operation uop;\n         uop.issuer = ao.issuer;\n         uop.asset_to_update = aid;\n         uop.new_options = abo.options;\n         uop.new_options.extensions.value.initial_collateral_ratio = icr;\n\n         proposal_create_operation cop;\n         cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n         cop.expiration_time = db.head_block_time() + 100;\n         cop.proposed_ops.emplace_back( uop );\n         trx.operations.clear();\n         trx.operations.push_back( cop );\n         trx.validate();\n         set_expiration( db, trx );\n         PUSH_TX(db, trx, ~0);\n         trx.operations.clear();\n      };\n\n      // Before bsip77 hard fork, unable to create a proposal with an asset_update_bitasset_op with ICR\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 0 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 1 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 1000 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 1001 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 1750 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 32000 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 32001 ), fc::exception );\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_BSIP_77_TIME );\n      set_expiration( db, trx );\n\n      // Unable to create a bitasset with an invalid ICR\n      BOOST_CHECK_THROW( create_bitasset( \"USDBITB\", sam_id, 0, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 0 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBITB\", sam_id, 1, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 0 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBITB\", sam_id, 1000, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 0 ), fc::exception );\n      BOOST_CHECK_THROW( create_bitasset( \"USDBITB\", sam_id, 32001, charge_market_fee, 2, {},\n                                          GRAPHENE_MAX_SHARE_SUPPLY, 0 ), fc::exception );\n      // Able to create a bitasset with a valid ICR\n      asset_id_type usdc_id = create_bitasset( \"USDBITC\", sam.id, 100, charge_market_fee, 2, {},\n                                               GRAPHENE_MAX_SHARE_SUPPLY, 1001 ).id;\n      asset_id_type usdd_id = create_bitasset( \"USDBITD\", sam.id, 100, charge_market_fee, 2, {},\n                                               GRAPHENE_MAX_SHARE_SUPPLY, 1750 ).id;\n      asset_id_type usde_id = create_bitasset( \"USDBITE\", sam.id, 100, charge_market_fee, 2, {},\n                                               GRAPHENE_MAX_SHARE_SUPPLY, 32000 ).id;\n      // Able to create a bitasset without ICR\n      asset_id_type usdf_id = create_bitasset( \"USDBITF\", sam.id, 100, charge_market_fee, 2, {},\n                                               GRAPHENE_MAX_SHARE_SUPPLY, {} ).id;\n\n      BOOST_CHECK( usdc_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio == 1001 );\n      BOOST_CHECK( usdd_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio == 1750 );\n      BOOST_CHECK( usde_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio == 32000 );\n      BOOST_CHECK( !usdf_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n\n      // Unable to update a bitasset with an invalid ICR\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 0 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 1 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 1000 ), fc::exception );\n      BOOST_CHECK_THROW( set_icr_for_asset( usd_id, 32001 ), fc::exception );\n      // Able to update a bitasset with a valid ICR\n      set_icr_for_asset( usd_id, 1001 );\n      BOOST_CHECK( usd_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio == 1001 );\n      set_icr_for_asset( usd_id, 1750 );\n      BOOST_CHECK( usd_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio == 1750 );\n      set_icr_for_asset( usd_id, 32000 );\n      BOOST_CHECK( usd_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio == 32000 );\n      // Able to update a bitasset, unset its ICR\n      set_icr_for_asset( usd_id, {} );\n      BOOST_CHECK( !usd_id(db).bitasset_data(db).options.extensions.value.initial_collateral_ratio.valid() );\n\n      // Unable to create a proposal with an asset_create_operation with an invalid ICR\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITG\", 0 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITG\", 1 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITG\", 1000 ), fc::exception );\n      BOOST_CHECK_THROW( propose_create_bitasset( \"USDBITG\", 32001 ), fc::exception );\n      // able to create a proposal with a valid ICR or no ICR\n      propose_create_bitasset( \"USDBITG\", 1001 );\n      propose_create_bitasset( \"USDBITG\", 1750 );\n      propose_create_bitasset( \"USDBITG\", 32000 );\n      propose_create_bitasset( \"USDBITG\", {} );\n\n      // Unable to create a proposal with an asset_update_bitasset_op with an invalid ICR\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 0 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 1 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 1000 ), fc::exception );\n      BOOST_CHECK_THROW( propose_set_icr_for_asset( usd_id, 32001 ), fc::exception );\n      // Able to create a proposal with a valid ICR or no ICR\n      propose_set_icr_for_asset( usd_id, 1001 );\n      propose_set_icr_for_asset( usd_id, 1750 );\n      propose_set_icr_for_asset( usd_id, 32000 );\n      propose_set_icr_for_asset( usd_id, {} );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( old_call_order_update_test_after_hardfork_bsip77_when_icr_not_set )\n{\n   bsip77 = true;\n   INVOKE( old_call_order_update_test_after_hardfork_583 );\n}\n\nBOOST_AUTO_TEST_CASE( more_call_order_update_test )\n{\n   try {\n\n      ACTORS((dan)(sam)(alice)(bob));\n      const auto& bitusd = create_bitasset(\"USDBIT\", sam.id);\n      const auto& core   = asset_id_type()(db);\n\n      transfer(committee_account, dan_id, asset(10000000));\n      transfer(committee_account, sam_id, asset(10000000));\n      transfer(committee_account, alice_id, asset(10000000));\n      transfer(committee_account, bob_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      current_feed.maximum_short_squeeze_ratio = 1100; // need to set this explicitly, testnet has a different default\n      publish_feed( bitusd, sam, current_feed );\n\n      FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"attempting to borrow using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 4x collateral at 1:1 price\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(100000), core.amount(400000) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n\n      BOOST_TEST_MESSAGE( \"alice place an order to sell usd at 1.05\" );\n      const limit_order_id_type alice_sell_id = create_sell_order( alice, bitusd.amount(1000), core.amount(1050) )->id;\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow less using 1.75x collateral at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(100), core.amount(175) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      const call_order_id_type bob_call_id = borrow( bob, bitusd.amount(100), asset(200))->id;\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 200 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much more using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000-100), core.amount(17500-200) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to reduce collateral to 1.75x at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(0), core.amount(175-200) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 + 105 );\n      BOOST_CHECK( !db.find<call_order_object>( bob_call_id ) );\n\n      BOOST_TEST_MESSAGE( \"alice cancel sell order\" );\n      cancel_limit_order( alice_sell_id(db) );\n\n      BOOST_TEST_MESSAGE( \"dan attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );\n\n      BOOST_TEST_MESSAGE( \"sam update price feed so dan's position will enter margin call territory.\" );\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(180);\n      publish_feed( bitusd, sam, current_feed );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5000 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5001 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5001)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 4999 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(4999)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5000 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 4999 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(4999)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5001 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5001)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 0 usd and freeing 1 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan adding 1 core as collateral should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( more_call_order_update_test_after_hardfork_583 )\n{\n   try {\n\n      auto hf_time = HARDFORK_CORE_583_TIME;\n      if( bsip77 )\n         hf_time = HARDFORK_BSIP_77_TIME;\n      generate_blocks( hf_time );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((dan)(sam)(alice)(bob));\n      const auto& bitusd = create_bitasset(\"USDBIT\", sam.id);\n      const auto& core   = asset_id_type()(db);\n\n      transfer(committee_account, dan_id, asset(10000000));\n      transfer(committee_account, sam_id, asset(10000000));\n      transfer(committee_account, alice_id, asset(10000000));\n      transfer(committee_account, bob_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      current_feed.maximum_short_squeeze_ratio = 1100; // need to set this explicitly, testnet has a different default\n      publish_feed( bitusd, sam, current_feed );\n\n      FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"attempting to borrow using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 4x collateral at 1:1 price\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(100000), core.amount(400000) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n\n      BOOST_TEST_MESSAGE( \"alice place an order to sell usd at 1.05\" );\n      const limit_order_id_type alice_sell_id = create_sell_order( alice, bitusd.amount(1000), core.amount(1050) )->id;\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow less using 1.75x collateral at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(100), core.amount(175) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      const call_order_id_type bob_call_id = borrow( bob, bitusd.amount(100), asset(200))->id;\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 200 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much more using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000-100), core.amount(17500-200) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to reduce collateral to 1.75x at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(0), core.amount(175-200) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 + 105 );\n      BOOST_CHECK( !db.find<call_order_object>( bob_call_id ) );\n\n      BOOST_TEST_MESSAGE( \"alice cancel sell order\" );\n      cancel_limit_order( alice_sell_id(db) );\n\n      BOOST_TEST_MESSAGE( \"dan attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );\n\n      BOOST_TEST_MESSAGE( \"sam update price feed so dan's position will enter margin call territory.\" );\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(180);\n      publish_feed( bitusd, sam, current_feed );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5000 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5001 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5001)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5000 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 4999 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(4999)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 4999 core should be allowed...\" );\n      cover( dan, bitusd.amount(2500), asset(4999));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 4999  );\n\n      BOOST_TEST_MESSAGE( \"dan covering 0 usd and freeing 1 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan adding 1 core as collateral should be allowed...\" );\n      borrow( dan, bitusd.amount(0), asset(1));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 4999 - 1  );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5002 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5002)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5003 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), asset(5003) ), fc::exception );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( more_call_order_update_test_after_hardfork_bsip77_when_icr_not_set )\n{\n   bsip77 = true;\n   INVOKE( more_call_order_update_test_after_hardfork_583 );\n}\n\nBOOST_AUTO_TEST_CASE( more_call_order_update_test_after_hardfork_bsip77_when_icr_is_set )\n{\n   try {\n\n      auto hf_time = HARDFORK_BSIP_77_TIME;\n      generate_blocks( hf_time );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((dan)(sam)(alice)(bob));\n      const auto& bitusd = create_bitasset( \"USDBIT\", sam.id, 100, charge_market_fee, 2, {},\n                                            GRAPHENE_MAX_SHARE_SUPPLY, 1050 ); // ICR = 1.05\n      const auto& core   = asset_id_type()(db);\n\n      asset_id_type usd_id = bitusd.id;\n\n      // helper function for setting ICR for an asset\n      auto set_icr_for_asset = [&](asset_id_type aid, optional<uint16_t> icr) {\n         const asset_object& ao = aid(db);\n         const asset_bitasset_data_object& abo = ao.bitasset_data(db);\n         asset_update_bitasset_operation uop;\n         uop.issuer = ao.issuer;\n         uop.asset_to_update = aid;\n         uop.new_options = abo.options;\n         uop.new_options.extensions.value.initial_collateral_ratio = icr;\n         trx.operations.clear();\n         trx.operations.push_back( uop );\n         trx.validate();\n         set_expiration( db, trx );\n         PUSH_TX(db, trx, ~0);\n      };\n\n      transfer(committee_account, dan_id, asset(10000000));\n      transfer(committee_account, sam_id, asset(10000000));\n      transfer(committee_account, alice_id, asset(10000000));\n      transfer(committee_account, bob_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      current_feed.maximum_short_squeeze_ratio = 1100; // need to set this explicitly, testnet has a different default\n      publish_feed( bitusd, sam, current_feed );\n\n      FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"ICR 1.05, MCR 1.75\" );\n      BOOST_TEST_MESSAGE( \"attempting to borrow using <=1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17499) ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 1.7501x collateral at 1:1 price should be allowed\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(10000), core.amount(17501) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 17501 );\n      BOOST_TEST_MESSAGE( \"ICR 1.05, MCR 1.75, Alice CR 1.7501\" );\n\n      // Update ICR\n      BOOST_TEST_MESSAGE( \"Updating ICR to 1.85\" );\n      set_icr_for_asset( usd_id, 1850 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.7501\" );\n\n      BOOST_TEST_MESSAGE( \"alice adding more collateral should be allowed\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(0), core.amount(18000-17501) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 18000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.8000\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral should not be allowed if CR<=1.85 and not margin called\" );\n      GRAPHENE_REQUIRE_THROW( cover( alice, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 1.8502x collateral at 1:1 price should be allowed\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(0), core.amount(18502-18000) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 18502 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.8502\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to >1.85x should be allowed\" );\n      cover( alice, bitusd.amount(0), core.amount(1) );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 18501 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.8501\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to <=1.85x should not be allowed if not margin called\" );\n      GRAPHENE_REQUIRE_THROW( cover( alice, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 4x collateral at 1:1 price\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(100000-10000), core.amount(400000-18501) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 4.0000\" );\n\n      BOOST_TEST_MESSAGE( \"alice place an order to sell usd at 1.05\" );\n      const limit_order_id_type alice_sell_id = create_sell_order( alice, bitusd.amount(1000), core.amount(1050) )->id;\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow less using 1.75x collateral at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(100), core.amount(175) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      const call_order_id_type bob_call_id = borrow( bob, bitusd.amount(100), asset(200))->id;\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 200 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much more using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000-100), core.amount(17500-200) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to reduce collateral to 1.75x at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(0), core.amount(175-200) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 + 105 );\n      BOOST_CHECK( !db.find<call_order_object>( bob_call_id ) );\n\n      BOOST_TEST_MESSAGE( \"alice cancel sell order\" );\n      cancel_limit_order( alice_sell_id(db) );\n\n      BOOST_TEST_MESSAGE( \"dan attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );\n\n      BOOST_TEST_MESSAGE( \"sam update price feed so dan's position will enter margin call territory.\" );\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(180);\n      publish_feed( bitusd, sam, current_feed );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5000 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5001 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5001)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5000 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 4999 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(4999)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 4999 core should be allowed...\" );\n      cover( dan, bitusd.amount(2500), asset(4999));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 4999  );\n\n      BOOST_TEST_MESSAGE( \"dan covering 0 usd and freeing 1 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan adding 1 core as collateral should be allowed...\" );\n      borrow( dan, bitusd.amount(0), asset(1));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 4999 - 1  );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5002 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5002)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5003 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), asset(5003) ), fc::exception );\n\n      // CR of Alice's postion is now 4.0 / 1.8 ~= 2.2222\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 2.222222\" );\n\n      BOOST_TEST_MESSAGE( \"alice adding more collateral should be allowed\" );\n      const call_order_id_type alice_call_id = borrow( alice, bitusd.amount(0), asset(1))->id;\n      BOOST_CHECK_EQUAL( alice_call_id(db).collateral.value, 400000 + 1 );\n      BOOST_CHECK_EQUAL( alice_call_id(db).debt.value, 100000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 2.222228\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to >1.85x should be allowed\" );\n      cover( alice, bitusd.amount(0), core.amount(67000) );\n      BOOST_CHECK_EQUAL( alice_call_id(db).collateral.value, 333001 );\n      BOOST_CHECK_EQUAL( alice_call_id(db).debt.value, 100000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.850006\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to <=1.85x should not be allowed if not margin called\" );\n      GRAPHENE_REQUIRE_THROW( cover( alice, bitusd.amount(0), core.amount(1) ), fc::exception );\n\n      // Update ICR\n      BOOST_TEST_MESSAGE( \"Updating ICR to 1.84\" );\n      set_icr_for_asset( usd_id, 1840 );\n      BOOST_TEST_MESSAGE( \"ICR 1.84, MCR 1.75, Alice CR 1.850006\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to >1.84x should be allowed\" );\n      cover( alice, bitusd.amount(0), core.amount(1) );\n      BOOST_CHECK_EQUAL( alice_call_id(db).collateral.value, 333000 );\n      BOOST_CHECK_EQUAL( alice_call_id(db).debt.value, 100000 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( more_call_order_update_test_after_hardfork_bsip77_when_icr_is_fed )\n{\n   try {\n\n      auto hf_time = HARDFORK_BSIP_77_TIME;\n      generate_blocks( hf_time );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((dan)(sam)(alice)(bob));\n      const auto& bitusd = create_bitasset( \"USDBIT\", sam.id, 100, charge_market_fee, 2, {},\n                                            GRAPHENE_MAX_SHARE_SUPPLY, {} ); // ICR is not set\n      const auto& core   = asset_id_type()(db);\n\n      transfer(committee_account, dan_id, asset(10000000));\n      transfer(committee_account, sam_id, asset(10000000));\n      transfer(committee_account, alice_id, asset(10000000));\n      transfer(committee_account, bob_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      current_feed.maximum_short_squeeze_ratio = 1100; // need to set this explicitly, testnet has a different default\n      publish_feed( bitusd, sam, current_feed, 1050 ); // ICR = 1.05\n\n      FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"ICR 1.05, MCR 1.75\" );\n      BOOST_TEST_MESSAGE( \"attempting to borrow using <=1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17499) ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 1.7501x collateral at 1:1 price should be allowed\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(10000), core.amount(17501) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 17501 );\n      BOOST_TEST_MESSAGE( \"ICR 1.05, MCR 1.75, Alice CR 1.7501\" );\n\n      // Update ICR\n      BOOST_TEST_MESSAGE( \"Updating ICR to 1.85\" );\n      publish_feed( bitusd, sam, current_feed, 1850 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.7501\" );\n\n      BOOST_TEST_MESSAGE( \"alice adding more collateral should be allowed\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(0), core.amount(18000-17501) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 18000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.8000\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral should not be allowed if CR<=1.85 and not margin called\" );\n      GRAPHENE_REQUIRE_THROW( cover( alice, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 1.8502x collateral at 1:1 price should be allowed\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(0), core.amount(18502-18000) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 18502 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.8502\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to >1.85x should be allowed\" );\n      cover( alice, bitusd.amount(0), core.amount(1) );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 10000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 18501 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.8501\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to <=1.85x should not be allowed if not margin called\" );\n      GRAPHENE_REQUIRE_THROW( cover( alice, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"alice borrow using 4x collateral at 1:1 price\" );\n      BOOST_CHECK( borrow( alice, bitusd.amount(100000-10000), core.amount(400000-18501) ) != nullptr );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 4.0000\" );\n\n      BOOST_TEST_MESSAGE( \"alice place an order to sell usd at 1.05\" );\n      const limit_order_id_type alice_sell_id = create_sell_order( alice, bitusd.amount(1000), core.amount(1050) )->id;\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000), core.amount(17500) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow less using 1.75x collateral at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(100), core.amount(175) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      const call_order_id_type bob_call_id = borrow( bob, bitusd.amount(100), asset(200))->id;\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 200 );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to borrow too much more using 1.75x collateral at 1:1 price should not be allowed\" );\n      GRAPHENE_REQUIRE_THROW( borrow( bob, bitusd.amount(10000-100), core.amount(17500-200) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"bob attempting to reduce collateral to 1.75x at 1:1 price should be allowed and margin called\" );\n      BOOST_CHECK( !borrow( bob, bitusd.amount(0), core.amount(175-200) ) );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, bitusd ), 100 + 100 );\n      BOOST_REQUIRE_EQUAL( get_balance( bob, core ), 10000000 - 105 - 105 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, bitusd ), 100000 - 1000 );\n      BOOST_REQUIRE_EQUAL( get_balance( alice, core ), 10000000 - 400000 + 105 + 105 );\n      BOOST_CHECK( !db.find<call_order_object>( bob_call_id ) );\n\n      BOOST_TEST_MESSAGE( \"alice cancel sell order\" );\n      cancel_limit_order( alice_sell_id(db) );\n\n      BOOST_TEST_MESSAGE( \"dan attempting to borrow using 2x collateral at 1:1 price now that there is a valid order\" );\n      borrow( dan, bitusd.amount(5000), asset(10000));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );\n\n      BOOST_TEST_MESSAGE( \"sam update price feed so dan's position will enter margin call territory.\" );\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(180);\n      publish_feed( bitusd, sam, current_feed, 1850 );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5000 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 5001 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(5001)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5000 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5000)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 4999 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(4999)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan covering 2500 usd and freeing 4999 core should be allowed...\" );\n      cover( dan, bitusd.amount(2500), asset(4999));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 4999  );\n\n      BOOST_TEST_MESSAGE( \"dan covering 0 usd and freeing 1 core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, bitusd.amount(0), core.amount(1)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan adding 1 core as collateral should be allowed...\" );\n      borrow( dan, bitusd.amount(0), asset(1));\n      BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );\n      BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 4999 - 1  );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5002 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), core.amount(5002)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"dan borrow 2500 more usd wth 5003 more core should not be allowed...\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, bitusd.amount(2500), asset(5003) ), fc::exception );\n\n      // CR of Alice's postion is now 4.0 / 1.8 ~= 2.2222\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 2.222222\" );\n\n      BOOST_TEST_MESSAGE( \"alice adding more collateral should be allowed\" );\n      const call_order_id_type alice_call_id = borrow( alice, bitusd.amount(0), asset(1))->id;\n      BOOST_CHECK_EQUAL( alice_call_id(db).collateral.value, 400000 + 1 );\n      BOOST_CHECK_EQUAL( alice_call_id(db).debt.value, 100000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 2.222228\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to >1.85x should be allowed\" );\n      cover( alice, bitusd.amount(0), core.amount(67000) );\n      BOOST_CHECK_EQUAL( alice_call_id(db).collateral.value, 333001 );\n      BOOST_CHECK_EQUAL( alice_call_id(db).debt.value, 100000 );\n      BOOST_TEST_MESSAGE( \"ICR 1.85, MCR 1.75, Alice CR 1.850006\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to <=1.85x should not be allowed if not margin called\" );\n      GRAPHENE_REQUIRE_THROW( cover( alice, bitusd.amount(0), core.amount(1) ), fc::exception );\n\n      // Update ICR\n      BOOST_TEST_MESSAGE( \"Updating ICR to 1.84\" );\n      publish_feed( bitusd, sam, current_feed, 1840 );\n      BOOST_TEST_MESSAGE( \"ICR 1.84, MCR 1.75, Alice CR 1.850006\" );\n\n      BOOST_TEST_MESSAGE( \"alice reducing collateral to >1.84x should be allowed\" );\n      cover( alice, bitusd.amount(0), core.amount(1) );\n      BOOST_CHECK_EQUAL( alice_call_id(db).collateral.value, 333000 );\n      BOOST_CHECK_EQUAL( alice_call_id(db).debt.value, 100000 );\n\n      generate_block();\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( call_order_update_validation_test )\n{\n   call_order_update_operation op;\n\n   // throw on default values\n   BOOST_CHECK_THROW( op.validate(), fc::assert_exception );\n\n   // minimum changes to make it valid\n   op.delta_debt = asset( 1, asset_id_type(1) );\n   op.validate(); // won't throw if has a non-zero debt with different asset_id_type than collateral\n\n   // throw on negative fee\n   op.fee = asset( -1 );\n   BOOST_CHECK_THROW( op.validate(), fc::assert_exception );\n   op.fee = asset( 0 );\n\n   // throw on identical debt and collateral asset id\n   op.delta_collateral = asset( 0, asset_id_type(1) );\n   BOOST_CHECK_THROW( op.validate(), fc::assert_exception );\n\n   // throw on zero debt and collateral amount\n   op.delta_debt = asset( 0, asset_id_type(0) );\n   BOOST_CHECK_THROW( op.validate(), fc::assert_exception );\n   op.delta_debt = asset( -1, asset_id_type(0) );\n\n   op.validate(); // valid now\n\n   op.extensions.value.target_collateral_ratio = 0;\n   op.validate(); // still valid\n\n   op.extensions.value.target_collateral_ratio = 65535;\n   op.validate(); // still valid\n}\n\n/**\n *  This test sets up a situation where a margin call will be executed and ensures that\n *  it is properly filled.\n *\n *  A margin call can happen in the following situation:\n *  0. there exists a bid above the mas short squeeze price\n *  1. highest bid is lower than the call price of an order\n *  2. the asset is not a prediction market\n *  3. there is a valid price feed\n *\n *  This test creates two scenarios:\n *  a) when the bids are above the short squeese limit (should execute)\n *  b) when the bids are below the short squeeze limit (should not execute)\n */\nBOOST_AUTO_TEST_CASE( margin_call_limit_test )\n{ try {\n      ACTORS((buyer)(seller)(borrower)(borrower2)(feedproducer));\n\n      const auto& bitusd = create_bitasset(\"USDBIT\", feedproducer_id);\n      const auto& core   = asset_id_type()(db);\n\n      int64_t init_balance(1000000);\n\n      transfer(committee_account, buyer_id, asset(init_balance));\n      transfer(committee_account, borrower_id, asset(init_balance));\n      transfer(committee_account, borrower2_id, asset(init_balance));\n      update_feed_producers( bitusd, {feedproducer.id} );\n\n      price_feed current_feed;\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      current_feed.maximum_short_squeeze_ratio  = 1500; // need to set this explicitly, testnet has a different default\n\n      // starting out with price 1:1\n      publish_feed( bitusd, feedproducer, current_feed );\n\n      // start out with 2:1 collateral\n      borrow( borrower, bitusd.amount(1000), asset(2000));\n      borrow( borrower2, bitusd.amount(1000), asset(4000) );\n\n      BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n      BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 );\n      BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 2000 );\n      BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 4000 );\n\n      // this should trigger margin call that is below the call limit, but above the\n      // protection threshold.\n      BOOST_TEST_MESSAGE( \"Creating a margin call that is NOT protected by the max short squeeze price\" );\n      auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1400) );\n      if( db.head_block_time() <= HARDFORK_436_TIME )\n      {\n         BOOST_CHECK( order == nullptr );\n\n         BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 4000 + 1400 );\n         BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 );\n\n         BOOST_CHECK_EQUAL( get_balance( borrower, core ), init_balance - 2000 + 600 );\n         BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n      }\n      else\n      {\n         BOOST_CHECK( order != nullptr );\n\n         BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 );\n         BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 );\n         BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 2000 );\n         BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 4000 );\n      }\n\n      BOOST_TEST_MESSAGE( \"Creating a margin call that is protected by the max short squeeze price\" );\n      borrow( borrower, bitusd.amount(1000), asset(2000) );\n      borrow( borrower2, bitusd.amount(1000), asset(4000) );\n\n      // this should trigger margin call without protection from the price feed.\n      order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) );\n      BOOST_CHECK( order != nullptr );\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( prediction_market )\n{ try {\n      ACTORS((judge)(dan)(nathan));\n\n      const auto& pmark = create_prediction_market(\"PMARK\", judge_id);\n      const auto pmark_dd_id = pmark.dynamic_asset_data_id;\n      const auto& core  = asset_id_type()(db);\n\n      int64_t init_balance(1000000);\n      transfer(committee_account, judge_id, asset(init_balance));\n      transfer(committee_account, dan_id, asset(init_balance));\n      transfer(committee_account, nathan_id, asset(init_balance));\n\n      update_feed_producers( pmark, { judge_id });\n      price_feed feed;\n      feed.settlement_price = asset( 1, pmark.id ) / asset( 1 );\n      publish_feed( pmark, judge, feed );\n\n      BOOST_TEST_MESSAGE( \"Require throw for mismatch collateral amounts\" );\n      GRAPHENE_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Open position with equal collateral\" );\n      borrow( dan, pmark.amount(1000), asset(1000) );\n\n      BOOST_TEST_MESSAGE( \"Cover position with unequal asset should fail.\" );\n      GRAPHENE_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Cover half of position with equal ammounts\" );\n      cover( dan, pmark.amount(500), asset(500) );\n\n      BOOST_TEST_MESSAGE( \"Verify that forced settlment fails before global settlement\" );\n      GRAPHENE_REQUIRE_THROW( force_settle( dan, pmark.amount(100) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Shouldn't be allowed to force settle at more than 1 collateral per debt\" );\n      GRAPHENE_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(105) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Globally settling\" );\n      force_global_settle( pmark, pmark.amount(100) / core.amount(95) );\n\n      BOOST_TEST_MESSAGE( \"Can not globally settle again\" );\n      GRAPHENE_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(95) ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Verify that forced settlment succeedes after global settlement\" );\n      force_settle( dan, pmark.amount(100) );\n\n      // force settle the rest\n      force_settle( dan, pmark.amount(400) );\n      BOOST_CHECK_EQUAL( 0, pmark_dd_id(db).current_supply.value );\n\n      generate_block(~database::skip_transaction_dupe_check);\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      generate_block();\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( prediction_market_resolves_to_0 )\n{ try {\n      ACTORS((judge)(dan)(nathan));\n\n      const auto& pmark = create_prediction_market(\"PMARK\", judge_id);\n      const auto pmark_dd_id = pmark.dynamic_asset_data_id;\n      const auto& core  = asset_id_type()(db);\n\n      int64_t init_balance(1000000);\n      transfer(committee_account, judge_id, asset(init_balance));\n      transfer(committee_account, dan_id, asset(init_balance));\n      transfer(committee_account, nathan_id, asset(init_balance));\n\n      update_feed_producers( pmark, { judge_id });\n      price_feed feed;\n      feed.settlement_price = asset( 1, pmark.id ) / asset( 1 );\n      publish_feed( pmark, judge, feed );\n\n      borrow( dan, pmark.amount(1000), asset(1000) );\n      // force settle with 0 outcome\n      force_global_settle( pmark, pmark.amount(100) / core.amount(0) );\n\n      BOOST_TEST_MESSAGE( \"Verify that forced settlment succeedes after global settlement\" );\n      force_settle( dan, pmark.amount(100) );\n\n      // force settle the rest\n      force_settle( dan, pmark.amount(900) );\n      BOOST_CHECK_EQUAL( 0, pmark_dd_id(db).current_supply.value );\n\n      generate_block(~database::skip_transaction_dupe_check);\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      generate_block();\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * Prediction markets should not suffer a black swan (Issue #460)\n */\nBOOST_AUTO_TEST_CASE( prediction_market_black_swan )\n{ \n   try {\n      ACTORS((judge)(dan)(nathan));\n\n      // progress to recent hardfork\n      generate_blocks( HARDFORK_CORE_1270_TIME );\n      set_expiration( db, trx );\n\n      const auto& pmark = create_prediction_market(\"PMARK\", judge_id);\n\n      int64_t init_balance(1000000);\n      transfer(committee_account, judge_id, asset(init_balance));\n      transfer(committee_account, dan_id, asset(init_balance));\n\n      update_feed_producers( pmark, { judge_id });\n      price_feed feed;\n      feed.settlement_price = asset( 1, pmark.id ) / asset( 1 );\n      publish_feed( pmark, judge, feed );\n\n      borrow( dan, pmark.amount(1000), asset(1000) );\n\n      // feed a price that will cause a black swan\n      feed.settlement_price = asset( 1, pmark.id ) / asset( 1000 );\n      publish_feed( pmark, judge, feed );\n\n      // verify a black swan happened\n      GRAPHENE_REQUIRE_THROW(borrow( dan, pmark.amount(1000), asset(1000) ), fc::exception);\n      trx.clear();\n\n      // progress past hardfork\n      generate_blocks( HARDFORK_CORE_460_TIME + db.get_global_properties().parameters.maintenance_interval );\n      set_expiration( db, trx );\n\n      // create another prediction market to test the hardfork\n      const auto& pmark2 = create_prediction_market(\"PMARKII\", judge_id);\n      update_feed_producers( pmark2, { judge_id });\n      price_feed feed2;\n      feed2.settlement_price = asset( 1, pmark2.id ) / asset( 1 );\n      publish_feed( pmark2, judge, feed2 );\n\n      borrow( dan, pmark2.amount(1000), asset(1000) );\n\n      // feed a price that would have caused a black swan\n      feed2.settlement_price = asset( 1, pmark2.id ) / asset( 1000 );\n      publish_feed( pmark2, judge, feed2 );\n\n      // verify a black swan did not happen\n      borrow( dan, pmark2.amount(1000), asset(1000) );\n\n      generate_block(~database::skip_transaction_dupe_check);\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      generate_block();\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( create_account_test )\n{\n   try {\n      generate_blocks( HARDFORK_CORE_143_TIME );\n      set_expiration( db, trx );\n      trx.operations.push_back(make_account());\n      account_create_operation op = trx.operations.back().get<account_create_operation>();\n\n      REQUIRE_THROW_WITH_VALUE(op, registrar, account_id_type(9999999));\n      REQUIRE_THROW_WITH_VALUE(op, fee, asset(-1));\n      REQUIRE_THROW_WITH_VALUE(op, name, \"!\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"Sam\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"saM\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"sAm\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"6j\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"j-\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"-j\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \"aaaa.\");\n      REQUIRE_THROW_WITH_VALUE(op, name, \".aaaa\");\n      REQUIRE_THROW_WITH_VALUE(op, options.voting_account, account_id_type(999999999));\n\n      // Not allow voting for non-exist entities.\n      auto save_num_committee = op.options.num_committee;\n      auto save_num_witness = op.options.num_witness;\n      op.options.num_committee = 1;\n      op.options.num_witness = 0;\n      REQUIRE_THROW_WITH_VALUE(op, options.votes, boost::assign::list_of<vote_id_type>(vote_id_type(\"0:1\")).convert_to_container<flat_set<vote_id_type>>());\n      op.options.num_witness = 1;\n      op.options.num_committee = 0;\n      REQUIRE_THROW_WITH_VALUE(op, options.votes, boost::assign::list_of<vote_id_type>(vote_id_type(\"1:19\")).convert_to_container<flat_set<vote_id_type>>());\n      op.options.num_witness = 0;\n      REQUIRE_THROW_WITH_VALUE(op, options.votes, boost::assign::list_of<vote_id_type>(vote_id_type(\"2:19\")).convert_to_container<flat_set<vote_id_type>>());\n      REQUIRE_THROW_WITH_VALUE(op, options.votes, boost::assign::list_of<vote_id_type>(vote_id_type(\"3:99\")).convert_to_container<flat_set<vote_id_type>>());\n      GRAPHENE_REQUIRE_THROW( vote_id_type(\"2:a\"), fc::exception );\n      GRAPHENE_REQUIRE_THROW( vote_id_type(\"\"), fc::exception );\n      op.options.num_committee = save_num_committee;\n      op.options.num_witness = save_num_witness;\n\n      auto auth_bak = op.owner;\n      op.owner.add_authority(account_id_type(9999999999), 10);\n      trx.operations.back() = op;\n      op.owner = auth_bak;\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n      op.owner = auth_bak;\n\n      trx.operations.back() = op;\n      sign( trx,  init_account_priv_key );\n      trx.validate();\n      PUSH_TX( db, trx, ~0 );\n\n      const account_object& nathan_account = *db.get_index_type<account_index>().indices().get<by_name>().find(\"nathan\");\n      BOOST_CHECK(nathan_account.id.space() == protocol_ids);\n      BOOST_CHECK(nathan_account.id.type() == account_object_type);\n      BOOST_CHECK(nathan_account.name == \"nathan\");\n\n      BOOST_REQUIRE(nathan_account.owner.num_auths() == 1);\n      BOOST_CHECK(nathan_account.owner.key_auths.at(committee_key) == 123);\n      BOOST_REQUIRE(nathan_account.active.num_auths() == 1);\n      BOOST_CHECK(nathan_account.active.key_auths.at(committee_key) == 321);\n      BOOST_CHECK(nathan_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT);\n      BOOST_CHECK(nathan_account.options.memo_key == committee_key);\n\n      const account_statistics_object& statistics = nathan_account.statistics(db);\n      BOOST_CHECK(statistics.id.space() == implementation_ids);\n      BOOST_CHECK(statistics.id.type() == impl_account_statistics_object_type);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( update_account )\n{\n   try {\n      const account_object& nathan = create_account(\"nathan\", init_account_pub_key);\n      const fc::ecc::private_key nathan_new_key = fc::ecc::private_key::generate();\n      const public_key_type key_id = nathan_new_key.get_public_key();\n      const auto& active_committee_members = db.get_global_properties().active_committee_members;\n\n      transfer(account_id_type()(db), nathan, asset(1000000000));\n\n      trx.operations.clear();\n      account_update_operation op;\n      op.account = nathan.id;\n      op.owner = authority(2, key_id, 1, init_account_pub_key, 1);\n      op.active = authority(2, key_id, 1, init_account_pub_key, 1);\n      op.new_options = nathan.options;\n      op.new_options->votes = flat_set<vote_id_type>({active_committee_members[0](db).vote_id, active_committee_members[5](db).vote_id});\n      op.new_options->num_committee = 2;\n      trx.operations.push_back(op);\n      BOOST_TEST_MESSAGE( \"Updating account\" );\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK(nathan.options.memo_key == init_account_pub_key);\n      BOOST_CHECK(nathan.active.weight_threshold == 2);\n      BOOST_CHECK(nathan.active.num_auths() == 2);\n      BOOST_CHECK(nathan.active.key_auths.at(key_id) == 1);\n      BOOST_CHECK(nathan.active.key_auths.at(init_account_pub_key) == 1);\n      BOOST_CHECK(nathan.owner.weight_threshold == 2);\n      BOOST_CHECK(nathan.owner.num_auths() == 2);\n      BOOST_CHECK(nathan.owner.key_auths.at(key_id) == 1);\n      BOOST_CHECK(nathan.owner.key_auths.at(init_account_pub_key) == 1);\n      BOOST_CHECK(nathan.options.votes.size() == 2);\n\n      enable_fees();\n      {\n         account_upgrade_operation op;\n         op.account_to_upgrade = nathan.id;\n         op.upgrade_to_lifetime_member = true;\n         op.fee = db.get_global_properties().parameters.get_current_fees().calculate_fee(op);\n         trx.operations = {op};\n         PUSH_TX( db, trx, ~0 );\n      }\n\n      BOOST_CHECK( nathan.is_lifetime_member() );\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( transfer_core_asset )\n{\n   try {\n      INVOKE(create_account_test);\n\n      account_id_type committee_account;\n      asset committee_balance = db.get_balance(account_id_type(), asset_id_type());\n\n      const account_object& nathan_account = *db.get_index_type<account_index>().indices().get<by_name>().find(\"nathan\");\n      transfer_operation top;\n      top.from = committee_account;\n      top.to = nathan_account.id;\n      top.amount = asset( 10000);\n      trx.operations.push_back(top);\n      for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n\n      asset fee = trx.operations.front().get<transfer_operation>().fee;\n      trx.validate();\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(get_balance(account_id_type()(db), asset_id_type()(db)),\n                        (committee_balance.amount - 10000 - fee.amount).value);\n      committee_balance = db.get_balance(account_id_type(), asset_id_type());\n\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 10000);\n\n      trx = signed_transaction();\n      top.from = nathan_account.id;\n      top.to = committee_account;\n      top.amount = asset(2000);\n      trx.operations.push_back(top);\n\n      for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n\n      fee = trx.operations.front().get<transfer_operation>().fee;\n      set_expiration( db, trx );\n      trx.validate();\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 8000 - fee.amount.value);\n      BOOST_CHECK_EQUAL(get_balance(account_id_type()(db), asset_id_type()(db)), committee_balance.amount.value + 2000);\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( create_committee_member )\n{\n   try {\n      committee_member_create_operation op;\n      op.committee_member_account = account_id_type();\n      op.fee = asset();\n      trx.operations.push_back(op);\n\n      REQUIRE_THROW_WITH_VALUE(op, committee_member_account, account_id_type(99999999));\n      REQUIRE_THROW_WITH_VALUE(op, fee, asset(-600));\n      trx.operations.back() = op;\n\n      committee_member_id_type committee_member_id = db.get_index_type<committee_member_index>().get_next_id();\n      PUSH_TX( db, trx, ~0 );\n      const committee_member_object& d = committee_member_id(db);\n\n      BOOST_CHECK(d.committee_member_account == account_id_type());\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( create_mia )\n{\n   try {\n      const asset_object& bitusd = create_bitasset( \"USDBIT\" );\n      BOOST_CHECK(bitusd.symbol == \"USDBIT\");\n      BOOST_CHECK(bitusd.bitasset_data(db).options.short_backing_asset == asset_id_type());\n      BOOST_CHECK(bitusd.dynamic_asset_data_id(db).current_supply == 0);\n      GRAPHENE_REQUIRE_THROW( create_bitasset(\"USDBIT\"), fc::exception);\n   } catch ( const fc::exception& e ) {\n      elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( update_mia )\n{\n   try {\n      INVOKE(create_mia);\n      generate_block();\n      const asset_object& bit_usd = get_asset(\"USDBIT\");\n\n      asset_update_operation op;\n      op.issuer = bit_usd.issuer;\n      op.asset_to_update = bit_usd.id;\n      op.new_options = bit_usd.options;\n      trx.operations.emplace_back(op);\n\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n      std::swap(op.new_options.flags, op.new_options.issuer_permissions);\n      op.new_issuer = account_id_type();\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      {\n         asset_publish_feed_operation pop;\n         pop.asset_id = bit_usd.get_id();\n         pop.publisher = get_account(\"init0\").get_id();\n         price_feed feed;\n         feed.settlement_price = feed.core_exchange_rate = price(bit_usd.amount(5), bit_usd.amount(5));\n         REQUIRE_THROW_WITH_VALUE(pop, feed, feed);\n         feed.settlement_price = feed.core_exchange_rate = ~price(bit_usd.amount(5), asset(5));\n         REQUIRE_THROW_WITH_VALUE(pop, feed, feed);\n         feed.settlement_price = feed.core_exchange_rate = price(bit_usd.amount(5), asset(5));\n         pop.feed = feed;\n         REQUIRE_THROW_WITH_VALUE(pop, feed.maintenance_collateral_ratio, 0);\n         trx.operations.back() = pop;\n         PUSH_TX( db, trx, ~0 );\n      }\n\n      trx.operations.clear();\n      auto nathan = create_account(\"nathan\");\n      op.issuer = account_id_type();\n      op.new_issuer = nathan.id;\n      trx.operations.emplace_back(op);\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK(bit_usd.issuer == nathan.id);\n\n      op.issuer = nathan.id;\n      op.new_issuer = account_id_type();\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK(bit_usd.issuer == account_id_type());\n   } catch ( const fc::exception& e ) {\n      elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n      throw;\n   }\n}\n\n\nBOOST_AUTO_TEST_CASE( create_uia )\n{\n   try {\n      asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();\n      asset_create_operation creator;\n      creator.issuer = account_id_type();\n      creator.fee = asset();\n      creator.symbol = UIA_TEST_SYMBOL;\n      creator.common_options.max_supply = 100000000;\n      creator.precision = 2;\n      creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/\n      creator.common_options.issuer_permissions = DEFAULT_UIA_ASSET_ISSUER_PERMISSION;\n      creator.common_options.flags = charge_market_fee;\n      creator.common_options.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1)));\n      trx.operations.push_back(std::move(creator));\n      PUSH_TX( db, trx, ~0 );\n\n      const asset_object& test_asset = test_asset_id(db);\n      BOOST_CHECK(test_asset.symbol == UIA_TEST_SYMBOL);\n      BOOST_CHECK(asset(1, test_asset_id) * test_asset.options.core_exchange_rate == asset(2));\n      BOOST_CHECK((test_asset.options.flags & white_list) == 0);\n      BOOST_CHECK(test_asset.options.max_supply == 100000000);\n      BOOST_CHECK(!test_asset.bitasset_data_id.valid());\n      BOOST_CHECK(test_asset.options.market_fee_percent == GRAPHENE_MAX_MARKET_FEE_PERCENT/100);\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n\n      const asset_dynamic_data_object& test_asset_dynamic_data = test_asset.dynamic_asset_data_id(db);\n      BOOST_CHECK(test_asset_dynamic_data.current_supply == 0);\n      BOOST_CHECK(test_asset_dynamic_data.accumulated_fees == 0);\n      BOOST_CHECK(test_asset_dynamic_data.fee_pool == 0);\n\n      auto op = trx.operations.back().get<asset_create_operation>();\n      op.symbol = \"TESTFAIL\";\n      REQUIRE_THROW_WITH_VALUE(op, issuer, account_id_type(99999999));\n      REQUIRE_THROW_WITH_VALUE(op, common_options.max_supply, -1);\n      REQUIRE_THROW_WITH_VALUE(op, common_options.max_supply, 0);\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \"A\");\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \"qqq\");\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \"11\");\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \".AAA\");\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \"AAA.\");\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \"AB CD\");\n      REQUIRE_THROW_WITH_VALUE(op, symbol, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\");\n      REQUIRE_THROW_WITH_VALUE(op, common_options.core_exchange_rate, price(asset(-100), asset(1)));\n      REQUIRE_THROW_WITH_VALUE(op, common_options.core_exchange_rate, price(asset(100),asset(-1)));\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( update_uia )\n{\n   using namespace graphene;\n   try {\n      INVOKE(create_uia);\n      const auto& test = get_asset(UIA_TEST_SYMBOL);\n      const auto& nathan = create_account(\"nathan\");\n\n      asset_update_operation op;\n      op.issuer = test.issuer;\n      op.asset_to_update = test.id;\n      op.new_options = test.options;\n\n      trx.operations.push_back(op);\n\n      //Cannot change issuer to same as before\n      BOOST_TEST_MESSAGE( \"Make sure changing issuer to same as before is forbidden\" );\n      REQUIRE_THROW_WITH_VALUE(op, new_issuer, test.issuer);\n\n      //Cannot convert to an MIA\n      BOOST_TEST_MESSAGE( \"Make sure we can't convert UIA to MIA\" );\n      REQUIRE_THROW_WITH_VALUE(op, new_options.issuer_permissions, ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK);\n      REQUIRE_THROW_WITH_VALUE(op, new_options.core_exchange_rate, price(asset(5), asset(5)));\n\n      BOOST_TEST_MESSAGE( \"Test updating core_exchange_rate\" );\n      op.new_options.core_exchange_rate = price(asset(3), test.amount(5));\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n      REQUIRE_THROW_WITH_VALUE(op, new_options.core_exchange_rate, price());\n      op.new_options.core_exchange_rate = test.options.core_exchange_rate;\n      op.new_issuer = nathan.id;\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_TEST_MESSAGE( \"Test setting flags\" );\n      op.issuer = nathan.id;\n      op.new_issuer.reset();\n      op.new_options.flags = transfer_restricted | white_list;\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_TEST_MESSAGE( \"Disable white_list permission\" );\n      op.new_options.issuer_permissions = test.options.issuer_permissions & ~white_list;\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_TEST_MESSAGE( \"Can't toggle white_list\" );\n      REQUIRE_THROW_WITH_VALUE(op, new_options.flags, test.options.flags & ~white_list);\n\n      BOOST_TEST_MESSAGE( \"Can toggle transfer_restricted\" );\n      for( int i=0; i<2; i++ )\n      {\n         op.new_options.flags = test.options.flags ^ transfer_restricted;\n         trx.operations.back() = op;\n         PUSH_TX( db, trx, ~0 );\n      }\n\n      asset_issue_operation issue_op;\n      issue_op.issuer = op.issuer;\n      issue_op.asset_to_issue =  asset(5000000,op.asset_to_update);\n      issue_op.issue_to_account = nathan.get_id();\n      trx.operations.push_back(issue_op);\n      PUSH_TX(db, trx, ~0);\n      \n      BOOST_TEST_MESSAGE( \"Make sure white_list can't be re-enabled (after tokens issued)\" );\n      op.new_options.issuer_permissions = test.options.issuer_permissions;\n      op.new_options.flags = test.options.flags;\n      BOOST_CHECK(!(test.options.issuer_permissions & white_list));\n      REQUIRE_THROW_WITH_VALUE(op, new_options.issuer_permissions, DEFAULT_UIA_ASSET_ISSUER_PERMISSION);\n\n      BOOST_TEST_MESSAGE( \"We can change issuer to account_id_type(), but can't do it again\" );\n      op.new_issuer = account_id_type();\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n      op.issuer = account_id_type();\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n      op.new_issuer.reset();\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( update_uia_issuer )\n{\n   using namespace graphene;\n   using namespace graphene::chain;\n   using namespace graphene::chain::test;\n   try {\n\n      // Lambda for creating accounts with 2 different keys\n      auto create_account_2_keys = [&]( const string name,\n           fc::ecc::private_key active,\n           fc::ecc::private_key owner ) {\n\n         trx.operations.push_back(make_account());\n         account_create_operation op = trx.operations.back().get<account_create_operation>();\n         op.name = name;\n         op.active = authority(1, public_key_type(active.get_public_key()), 1);\n         op.owner = authority(1, public_key_type(owner.get_public_key()), 1);\n         signed_transaction trx;\n         trx.operations.push_back(op);\n         db.current_fee_schedule().set_fee( trx.operations.back() );\n         set_expiration( db, trx );\n         PUSH_TX( db, trx, ~0 );\n\n         return get_account(name);\n      };\n\n      auto update_asset_issuer = [&](const asset_object& current,\n                                     const account_object& new_issuer) {\n         asset_update_operation op;\n         op.issuer =  current.issuer;\n         op.asset_to_update = current.id;\n         op.new_options = current.options;\n         op.new_issuer = new_issuer.id;\n         signed_transaction tx;\n         tx.operations.push_back( op );\n         db.current_fee_schedule().set_fee( tx.operations.back() );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, ~0 );\n      };\n\n      // Lambda for updating the issuer on chain using a particular key\n      auto update_issuer = [&](const asset_id_type asset_id,\n                               const account_object& issuer,\n                               const account_object& new_issuer,\n                               const fc::ecc::private_key& key)\n      {\n         asset_update_issuer_operation op;\n         op.issuer = issuer.id;\n         op.new_issuer = new_issuer.id;\n         op.asset_to_update = asset_id;\n         signed_transaction tx;\n         tx.operations.push_back( op );\n         db.current_fee_schedule().set_fee( tx.operations.back() );\n         set_expiration( db, tx );\n         sign(tx, key);\n         PUSH_TX( db, tx, database::skip_transaction_dupe_check );\n      };\n\n      auto update_issuer_proposal = [&](const asset_id_type asset_id,\n                                        const account_object& issuer,\n                                        const account_object& new_issuer,\n                                        const fc::ecc::private_key& key)\n      {\n          asset_update_issuer_operation op;\n          op.issuer = issuer.id;\n          op.new_issuer = new_issuer.id;\n          op.asset_to_update = asset_id;\n\n          const auto& curfees = db.get_global_properties().parameters.get_current_fees();\n          const auto& proposal_create_fees = curfees.get<proposal_create_operation>();\n          proposal_create_operation prop;\n          prop.fee_paying_account = issuer.id;\n          prop.proposed_ops.emplace_back( op );\n          prop.expiration_time =  db.head_block_time() + fc::days(1);\n          prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n\n          signed_transaction tx;\n          tx.operations.push_back( prop );\n          db.current_fee_schedule().set_fee( tx.operations.back() );\n          set_expiration( db, tx );\n          sign( tx, key );\n          PUSH_TX( db, tx );\n\n      };\n\n      // Create alice account\n      fc::ecc::private_key alice_owner  = fc::ecc::private_key::regenerate(fc::digest(\"key1\"));\n      fc::ecc::private_key alice_active = fc::ecc::private_key::regenerate(fc::digest(\"key2\"));\n      fc::ecc::private_key bob_owner    = fc::ecc::private_key::regenerate(fc::digest(\"key3\"));\n      fc::ecc::private_key bob_active   = fc::ecc::private_key::regenerate(fc::digest(\"key4\"));\n\n      // Create accounts\n      const auto& alice = create_account_2_keys(\"alice\", alice_active, alice_owner);\n      const auto& bob = create_account_2_keys(\"bob\", bob_active, bob_owner);\n      const account_id_type alice_id = alice.id;\n      const account_id_type bob_id = bob.id;\n\n      // Create asset\n      const auto& test = create_user_issued_asset(\"UPDATEISSUER\", alice_id(db), 0);\n      const asset_id_type test_id = test.id;\n\n      // Fast Forward to Hardfork time\n      generate_blocks( HARDFORK_CORE_199_TIME );\n\n      update_issuer_proposal( test_id, alice_id(db), bob_id(db), alice_owner);\n\n      BOOST_TEST_MESSAGE( \"Can't change issuer if not my asset\" );\n      GRAPHENE_REQUIRE_THROW( update_issuer( test_id, bob_id(db), alice_id(db), bob_active ), fc::exception );\n      GRAPHENE_REQUIRE_THROW( update_issuer( test_id, bob_id(db), alice_id(db), bob_owner ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Can't change issuer with alice's active key\" );\n      GRAPHENE_REQUIRE_THROW( update_issuer( test_id, alice_id(db), bob_id(db), alice_active ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Old method with asset_update needs to fail\" );\n      GRAPHENE_REQUIRE_THROW( update_asset_issuer( test_id(db), bob_id(db)  ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Updating issuer to bob\" );\n      update_issuer( test_id, alice_id(db), bob_id(db), alice_owner );\n\n      BOOST_CHECK(test_id(db).issuer == bob_id);\n\n   }\n   catch( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( issue_uia )\n{\n   try {\n      INVOKE(create_uia);\n      INVOKE(create_account_test);\n\n      const asset_object& test_asset = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(UIA_TEST_SYMBOL);\n      const account_object& nathan_account = *db.get_index_type<account_index>().indices().get<by_name>().find(\"nathan\");\n\n      asset_issue_operation op;\n      op.issuer = test_asset.issuer;\n      op.asset_to_issue =  test_asset.amount(5000000);\n      op.issue_to_account = nathan_account.id;\n      trx.operations.push_back(op);\n\n      REQUIRE_THROW_WITH_VALUE(op, asset_to_issue, asset(200));\n      REQUIRE_THROW_WITH_VALUE(op, fee, asset(-1));\n      REQUIRE_THROW_WITH_VALUE(op, issue_to_account, account_id_type(999999999));\n\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      const asset_dynamic_data_object& test_dynamic_data = test_asset.dynamic_asset_data_id(db);\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), 5000000);\n      BOOST_CHECK(test_dynamic_data.current_supply == 5000000);\n      BOOST_CHECK(test_dynamic_data.accumulated_fees == 0);\n      BOOST_CHECK(test_dynamic_data.fee_pool == 0);\n\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), 10000000);\n      BOOST_CHECK(test_dynamic_data.current_supply == 10000000);\n      BOOST_CHECK(test_dynamic_data.accumulated_fees == 0);\n      BOOST_CHECK(test_dynamic_data.fee_pool == 0);\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( transfer_uia )\n{\n   try {\n      INVOKE(issue_uia);\n\n      const asset_object& uia = *db.get_index_type<asset_index>().indices().get<by_symbol>().find(UIA_TEST_SYMBOL);\n      const account_object& nathan = *db.get_index_type<account_index>().indices().get<by_name>().find(\"nathan\");\n      const account_object& committee = account_id_type()(db);\n\n      BOOST_CHECK_EQUAL(get_balance(nathan, uia), 10000000);\n      transfer_operation top;\n      top.from = nathan.id;\n      top.to = committee.id;\n      top.amount = uia.amount(5000);\n      trx.operations.push_back(top);\n      BOOST_TEST_MESSAGE( \"Transfering 5000 TEST from nathan to committee\" );\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK_EQUAL(get_balance(nathan, uia), 10000000 - 5000);\n      BOOST_CHECK_EQUAL(get_balance(committee, uia), 5000);\n\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK_EQUAL(get_balance(nathan, uia), 10000000 - 10000);\n      BOOST_CHECK_EQUAL(get_balance(committee, uia), 10000);\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n\nBOOST_AUTO_TEST_CASE( create_buy_uia_multiple_match_new )\n{ try {\n   INVOKE( issue_uia );\n   const asset_object&   core_asset     = get_asset( UIA_TEST_SYMBOL );\n   const asset_object&   test_asset     = get_asset( GRAPHENE_SYMBOL );\n   const account_object& nathan_account = get_account( \"nathan\" );\n   const account_object& buyer_account  = create_account( \"buyer\" );\n   const account_object& seller_account = create_account( \"seller\" );\n\n   transfer( committee_account(db), buyer_account, test_asset.amount( 10000 ) );\n   transfer( nathan_account, seller_account, core_asset.amount(10000) );\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 10000 );\n\n   limit_order_id_type first_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(100) )->id;\n   limit_order_id_type second_id = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(200) )->id;\n   limit_order_id_type third_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(300) )->id;\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 9700 );\n\n   //print_market( \"\", \"\" );\n   auto unmatched = create_sell_order( seller_account, core_asset.amount(300), test_asset.amount(150) );\n   //print_market( \"\", \"\" );\n   BOOST_CHECK( !db.find( first_id ) );\n   BOOST_CHECK( !db.find( second_id ) );\n   BOOST_CHECK( db.find( third_id ) );\n   if( unmatched ) wdump((*unmatched));\n   BOOST_CHECK( !unmatched );\n\n   BOOST_CHECK_EQUAL( get_balance( seller_account, test_asset ), 200 );\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, core_asset ), 297 );\n   BOOST_CHECK_EQUAL( core_asset.dynamic_asset_data_id(db).accumulated_fees.value , 3 );\n }\n catch ( const fc::exception& e )\n {\n    elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n    throw;\n }\n}\n\nBOOST_AUTO_TEST_CASE( create_buy_exact_match_uia )\n{ try {\n   INVOKE( issue_uia );\n   const asset_object&   test_asset     = get_asset( UIA_TEST_SYMBOL );\n   const asset_object&   core_asset     = get_asset( GRAPHENE_SYMBOL );\n   const account_object& nathan_account = get_account( \"nathan\" );\n   const account_object& buyer_account  = create_account( \"buyer\" );\n   const account_object& seller_account = create_account( \"seller\" );\n\n   transfer( committee_account(db), seller_account, asset( 10000 ) );\n   transfer( nathan_account, buyer_account, test_asset.amount(10000) );\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 10000 );\n\n   limit_order_id_type first_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(100) )->id;\n   limit_order_id_type second_id = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(200) )->id;\n   limit_order_id_type third_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(300) )->id;\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 9700 );\n\n   //print_market( \"\", \"\" );\n   auto unmatched = create_sell_order( seller_account, core_asset.amount(100), test_asset.amount(100) );\n   //print_market( \"\", \"\" );\n   BOOST_CHECK( !db.find( first_id ) );\n   BOOST_CHECK( db.find( second_id ) );\n   BOOST_CHECK( db.find( third_id ) );\n   if( unmatched ) wdump((*unmatched));\n   BOOST_CHECK( !unmatched );\n\n   BOOST_CHECK_EQUAL( get_balance( seller_account, test_asset ), 99 );\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, core_asset ), 100 );\n   BOOST_CHECK_EQUAL( test_asset.dynamic_asset_data_id(db).accumulated_fees.value , 1 );\n }\n catch ( const fc::exception& e )\n {\n    elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n    throw;\n }\n}\n\n\nBOOST_AUTO_TEST_CASE( create_buy_uia_multiple_match_new_reverse )\n{ try {\n   INVOKE( issue_uia );\n   const asset_object&   test_asset     = get_asset( UIA_TEST_SYMBOL );\n   const asset_object&   core_asset     = get_asset( GRAPHENE_SYMBOL );\n   const account_object& nathan_account = get_account( \"nathan\" );\n   const account_object& buyer_account  = create_account( \"buyer\" );\n   const account_object& seller_account = create_account( \"seller\" );\n\n   transfer( committee_account(db), seller_account, asset( 10000 ) );\n   transfer( nathan_account, buyer_account, test_asset.amount(10000),test_asset.amount(0) );\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 10000 );\n\n   limit_order_id_type first_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(100) )->id;\n   limit_order_id_type second_id = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(200) )->id;\n   limit_order_id_type third_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(300) )->id;\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 9700 );\n\n   //print_market( \"\", \"\" );\n   auto unmatched = create_sell_order( seller_account, core_asset.amount(300), test_asset.amount(150) );\n   //print_market( \"\", \"\" );\n   BOOST_CHECK( !db.find( first_id ) );\n   BOOST_CHECK( !db.find( second_id ) );\n   BOOST_CHECK( db.find( third_id ) );\n   if( unmatched ) wdump((*unmatched));\n   BOOST_CHECK( !unmatched );\n\n   BOOST_CHECK_EQUAL( get_balance( seller_account, test_asset ), 198 );\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, core_asset ), 300 );\n   BOOST_CHECK_EQUAL( test_asset.dynamic_asset_data_id(db).accumulated_fees.value , 2 );\n }\n catch ( const fc::exception& e )\n {\n    elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n    throw;\n }\n}\n\nBOOST_AUTO_TEST_CASE( create_buy_uia_multiple_match_new_reverse_fract )\n{ try {\n   INVOKE( issue_uia );\n   const asset_object&   test_asset     = get_asset( UIA_TEST_SYMBOL );\n   const asset_object&   core_asset     = get_asset( GRAPHENE_SYMBOL );\n   const account_object& nathan_account = get_account( \"nathan\" );\n   const account_object& buyer_account  = create_account( \"buyer\" );\n   const account_object& seller_account = create_account( \"seller\" );\n\n   transfer( committee_account(db), seller_account, asset( 30 ) );\n   transfer( nathan_account, buyer_account, test_asset.amount(10000),test_asset.amount(0) );\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 10000 );\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, core_asset ), 0 );\n   BOOST_CHECK_EQUAL( get_balance( seller_account, core_asset ), 30 );\n\n   limit_order_id_type first_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(10) )->id;\n   limit_order_id_type second_id = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(20) )->id;\n   limit_order_id_type third_id  = create_sell_order( buyer_account, test_asset.amount(100), core_asset.amount(30) )->id;\n\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, test_asset ), 9700 );\n\n   //print_market( \"\", \"\" );\n   auto unmatched = create_sell_order( seller_account, core_asset.amount(30), test_asset.amount(150) );\n   //print_market( \"\", \"\" );\n   BOOST_CHECK( !db.find( first_id ) );\n   BOOST_CHECK( !db.find( second_id ) );\n   BOOST_CHECK( db.find( third_id ) );\n   if( unmatched ) wdump((*unmatched));\n   BOOST_CHECK( !unmatched );\n\n   BOOST_CHECK_EQUAL( get_balance( seller_account, test_asset ), 198 );\n   BOOST_CHECK_EQUAL( get_balance( buyer_account, core_asset ), 30 );\n   BOOST_CHECK_EQUAL( get_balance( seller_account, core_asset ), 0 );\n   BOOST_CHECK_EQUAL( test_asset.dynamic_asset_data_id(db).accumulated_fees.value , 2 );\n }\n catch ( const fc::exception& e )\n {\n    elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n    throw;\n }\n}\n\n\nBOOST_AUTO_TEST_CASE( uia_fees )\n{\n   try {\n      INVOKE( issue_uia );\n\n      enable_fees();\n\n      const asset_object& test_asset = get_asset(UIA_TEST_SYMBOL);\n      const asset_dynamic_data_object& asset_dynamic = test_asset.dynamic_asset_data_id(db);\n      const account_object& nathan_account = get_account(\"nathan\");\n      const account_object& committee_account = account_id_type()(db);\n      const share_type prec = asset::scaled_precision( asset_id_type()(db).precision );\n\n      fund_fee_pool(committee_account, test_asset, 1000*prec);\n      BOOST_CHECK(asset_dynamic.fee_pool == 1000*prec);\n\n      transfer_operation op;\n      op.fee = test_asset.amount(0);\n      op.from = nathan_account.id;\n      op.to   = committee_account.id;\n      op.amount = test_asset.amount(100);\n      op.fee = db.current_fee_schedule().calculate_fee( op, test_asset.options.core_exchange_rate );\n      BOOST_CHECK(op.fee.asset_id == test_asset.id);\n      asset old_balance = db.get_balance(nathan_account.get_id(), test_asset.get_id());\n      asset fee = op.fee;\n      BOOST_CHECK(fee.amount > 0);\n      asset core_fee = fee*test_asset.options.core_exchange_rate;\n      trx.operations.push_back(std::move(op));\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset),\n                        (old_balance - fee - test_asset.amount(100)).amount.value);\n      BOOST_CHECK_EQUAL(get_balance(committee_account, test_asset), 100);\n      BOOST_CHECK(asset_dynamic.accumulated_fees == fee.amount);\n      BOOST_CHECK(asset_dynamic.fee_pool == 1000*prec - core_fee.amount);\n\n      //Do it again, for good measure.\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset),\n                        (old_balance - fee - fee - test_asset.amount(200)).amount.value);\n      BOOST_CHECK_EQUAL(get_balance(committee_account, test_asset), 200);\n      BOOST_CHECK(asset_dynamic.accumulated_fees == fee.amount + fee.amount);\n      BOOST_CHECK(asset_dynamic.fee_pool == 1000*prec - core_fee.amount - core_fee.amount);\n\n      op = std::move(trx.operations.back().get<transfer_operation>());\n      trx.operations.clear();\n      op.amount = asset(20);\n\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 0);\n      transfer(committee_account, nathan_account, asset(20));\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 20);\n\n      trx.operations.emplace_back(std::move(op));\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset),\n                        (old_balance - fee - fee - fee - test_asset.amount(200)).amount.value);\n      BOOST_CHECK_EQUAL(get_balance(committee_account, test_asset), 200);\n      BOOST_CHECK(asset_dynamic.accumulated_fees == fee.amount.value * 3);\n      BOOST_CHECK(asset_dynamic.fee_pool == 1000*prec - core_fee.amount.value * 3);\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( cancel_limit_order_test )\n{ try {\n   INVOKE( issue_uia );\n   const asset_object&   test_asset     = get_asset( UIA_TEST_SYMBOL );\n   const account_object& buyer_account  = create_account( \"buyer\" );\n\n   transfer( committee_account(db), buyer_account, asset( 10000 ) );\n\n   BOOST_CHECK_EQUAL( get_balance(buyer_account, asset_id_type()(db)), 10000 );\n   auto sell_order = create_sell_order( buyer_account, asset(1000), test_asset.amount(100+450*1) );\n   FC_ASSERT( sell_order );\n   auto refunded = cancel_limit_order( *sell_order );\n   BOOST_CHECK( refunded == asset(1000) );\n   BOOST_CHECK_EQUAL( get_balance(buyer_account, asset_id_type()(db)), 10000 );\n }\n catch ( const fc::exception& e )\n {\n    elog( \"${e}\", (\"e\", e.to_detail_string() ) );\n    throw;\n }\n}\n\nBOOST_AUTO_TEST_CASE( witness_feeds )\n{\n   using namespace graphene::chain;\n   try {\n      INVOKE( create_mia );\n      {\n         auto& current = get_asset( \"USDBIT\" );\n         asset_update_operation uop;\n         uop.issuer =  current.issuer;\n         uop.asset_to_update = current.id;\n         uop.new_options = current.options;\n         uop.new_issuer = account_id_type();\n         trx.operations.push_back(uop);\n         PUSH_TX( db, trx, ~0 );\n         trx.clear();\n      }\n      generate_block();\n      const asset_object& bit_usd = get_asset(\"USDBIT\");\n      auto& global_props = db.get_global_properties();\n      vector<account_id_type> active_witnesses;\n      for( const witness_id_type& wit_id : global_props.active_witnesses )\n         active_witnesses.push_back( wit_id(db).witness_account );\n      BOOST_REQUIRE_EQUAL(active_witnesses.size(), INITIAL_WITNESS_COUNT);\n\n      asset_publish_feed_operation op;\n      op.publisher = active_witnesses[0];\n      op.asset_id = bit_usd.get_id();\n      op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30));\n      // Accept defaults for required collateral\n      trx.operations.emplace_back(op);\n      PUSH_TX( db, trx, ~0 );\n\n      const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db);\n      BOOST_CHECK(bitasset.current_feed.settlement_price.to_real() == 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      op.publisher = active_witnesses[1];\n      op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25));\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      op.publisher = active_witnesses[2];\n      op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40));\n      // But this witness is an idiot.\n      op.feed.maintenance_collateral_ratio = 1001;\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n   } catch (const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/**\n *  Create an order that cannot be filled immediately and have the\n *  transaction fail.\n */\nBOOST_AUTO_TEST_CASE( limit_order_fill_or_kill )\n{ try {\n   INVOKE(issue_uia);\n   const account_object& nathan = get_account(\"nathan\");\n   const asset_object& test = get_asset(UIA_TEST_SYMBOL);\n   const asset_object& core = asset_id_type()(db);\n\n   limit_order_create_operation op;\n   op.seller = nathan.id;\n   op.amount_to_sell = test.amount(500);\n   op.min_to_receive = core.amount(500);\n   op.fill_or_kill = true;\n\n   trx.operations.clear();\n   trx.operations.push_back(op);\n   GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n   op.fill_or_kill = false;\n   trx.operations.back() = op;\n   PUSH_TX( db, trx, ~0 );\n} FC_LOG_AND_RETHROW() }\n\n/// Shameless code coverage plugging. Otherwise, these calls never happen.\nBOOST_AUTO_TEST_CASE( fill_order )\n{ try {\n   fill_order_operation o;\n   GRAPHENE_CHECK_THROW(o.validate(), fc::exception);\n   //o.calculate_fee(db.current_fee_schedule());\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( witness_pay_test )\n{ try {\n\n   const share_type prec = asset::scaled_precision( asset_id_type()(db).precision );\n\n   // there is an immediate maintenance interval in the first block\n   //   which will initialize last_budget_time\n   generate_block();\n\n   // Make an account and upgrade it to prime, so that witnesses get some pay\n   create_account(\"nathan\", init_account_pub_key);\n   transfer(account_id_type()(db), get_account(\"nathan\"), asset(20000*prec));\n   transfer(account_id_type()(db), get_account(\"init3\"), asset(20*prec));\n   generate_block();\n\n   auto last_witness_vbo_balance = [&]() -> share_type\n   {\n      const witness_object& wit = db.fetch_block_by_number(db.head_block_num())->witness(db);\n      if( !wit.pay_vb.valid() )\n         return 0;\n      return (*wit.pay_vb)(db).balance.amount;\n   };\n\n   const auto block_interval = db.get_global_properties().parameters.block_interval;\n   const asset_object* core = &asset_id_type()(db);\n   const account_object* nathan = &get_account(\"nathan\");\n   enable_fees();\n   BOOST_CHECK_GT(db.current_fee_schedule().get<account_upgrade_operation>().membership_lifetime_fee, 0u);\n   // Based on the size of the reserve fund later in the test, the witness budget will be set to this value\n   const uint64_t ref_budget =\n      ((uint64_t( db.current_fee_schedule().get<account_upgrade_operation>().membership_lifetime_fee )\n         * GRAPHENE_CORE_ASSET_CYCLE_RATE * 30\n         * block_interval\n       ) + ((uint64_t(1) << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS)-1)\n      ) >> GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS\n      ;\n   // change this if ref_budget changes\n   BOOST_CHECK_EQUAL( ref_budget, 594u );\n   const uint64_t witness_ppb = ref_budget * 10 / 23 + 1;\n   // change this if ref_budget changes\n   BOOST_CHECK_EQUAL( witness_ppb, 259u );\n   // following two inequalities need to hold for maximal code coverage\n   BOOST_CHECK_LT( witness_ppb * 2, ref_budget );\n   BOOST_CHECK_GT( witness_ppb * 3, ref_budget );\n\n   db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )\n   {\n      _gpo.parameters.witness_pay_per_block = witness_ppb;\n   } );\n\n   BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0);\n   BOOST_TEST_MESSAGE( \"Upgrading account\" );\n   account_upgrade_operation uop;\n   uop.account_to_upgrade = nathan->get_id();\n   uop.upgrade_to_lifetime_member = true;\n   set_expiration( db, trx );\n   trx.operations.push_back(uop);\n   for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);\n   trx.validate();\n   sign( trx, init_account_priv_key );\n   PUSH_TX( db, trx );\n   auto pay_fee_time = db.head_block_time().sec_since_epoch();\n   trx.clear();\n   BOOST_CHECK( get_balance(*nathan, *core) == 20000*prec - account_upgrade_operation::fee_parameters_type().membership_lifetime_fee );;\n\n   generate_block();\n   nathan = &get_account(\"nathan\");\n   core = &asset_id_type()(db);\n   BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );\n\n   auto schedule_maint = [&]()\n   {\n      // now we do maintenance\n      db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo )\n      {\n         _dpo.next_maintenance_time = db.head_block_time() + 1;\n      } );\n   };\n   BOOST_TEST_MESSAGE( \"Generating some blocks\" );\n\n   // generate some blocks\n   while( db.head_block_time().sec_since_epoch() - pay_fee_time < 24 * block_interval )\n   {\n      generate_block();\n      BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );\n   }\n   BOOST_CHECK_EQUAL( db.head_block_time().sec_since_epoch() - pay_fee_time, 24u * block_interval );\n\n   schedule_maint();\n   // The 80% lifetime referral fee went to the committee account, which burned it. Check that it's here.\n   BOOST_CHECK( core->reserved(db).value == 8000*prec );\n   generate_block();\n   BOOST_CHECK_EQUAL( core->reserved(db).value, 999999406 );\n   BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, (int64_t)ref_budget );\n   // first witness paid from old budget (so no pay)\n   BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );\n   // second witness finally gets paid!\n   generate_block();\n   BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, (int64_t)witness_ppb );\n   BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, (int64_t)(ref_budget - witness_ppb) );\n\n   generate_block();\n   BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, (int64_t)witness_ppb );\n   BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, (int64_t)(ref_budget - 2 * witness_ppb) );\n\n   generate_block();\n   BOOST_CHECK_LT( last_witness_vbo_balance().value, (int64_t)witness_ppb );\n   BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, (int64_t)(ref_budget - 2 * witness_ppb) );\n   BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 );\n\n   generate_block();\n   BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );\n   BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 );\n   BOOST_CHECK_EQUAL(core->reserved(db).value, 999999406 );\n\n} FC_LOG_AND_RETHROW() }\n\n/**\n *  Reserve asset test should make sure that all assets except bitassets\n *  can be burned, and all supplies add up.\n */\nBOOST_AUTO_TEST_CASE( reserve_asset_test )\n{\n   try\n   {\n      ACTORS((alice)(bob)(sam)(judge));\n      const auto& basset = create_bitasset(\"USDBIT\", judge_id);\n      const auto& uasset = create_user_issued_asset(UIA_TEST_SYMBOL);\n      const auto& passet = create_prediction_market(\"PMARK\", judge_id);\n      const auto& casset = asset_id_type()(db);\n\n      auto reserve_asset = [&]( account_id_type payer, asset amount_to_reserve )\n      {\n         asset_reserve_operation op;\n         op.payer = payer;\n         op.amount_to_reserve = amount_to_reserve;\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      } ;\n\n      auto _issue_uia = [&]( const account_object& recipient, asset amount )\n      {\n         asset_issue_operation op;\n         op.issuer = amount.asset_id(db).issuer;\n         op.asset_to_issue = amount;\n         op.issue_to_account = recipient.id;\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      } ;\n\n      int64_t init_balance = 10000;\n      int64_t reserve_amount = 3000;\n      share_type initial_reserve;\n\n      BOOST_TEST_MESSAGE( \"Test reserve operation on core asset\" );\n      transfer( committee_account, alice_id, casset.amount( init_balance ) );\n\n      initial_reserve = casset.reserved( db );\n      reserve_asset( alice_id, casset.amount( reserve_amount  ) );\n      BOOST_CHECK_EQUAL( get_balance( alice, casset ), init_balance - reserve_amount );\n      BOOST_CHECK_EQUAL( (casset.reserved( db ) - initial_reserve).value, reserve_amount );\n      verify_asset_supplies(db);\n\n      BOOST_TEST_MESSAGE( \"Test reserve operation on market issued asset\" );\n      transfer( committee_account, alice_id, casset.amount( init_balance*100 ) );\n      update_feed_producers( basset, {sam.id} );\n      price_feed current_feed;\n      current_feed.settlement_price = basset.amount( 2 ) / casset.amount(100);\n      current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n      publish_feed( basset, sam, current_feed );\n      borrow( alice_id, basset.amount( init_balance ), casset.amount( 100*init_balance ) );\n      BOOST_CHECK_EQUAL( get_balance( alice, basset ), init_balance );\n\n      GRAPHENE_REQUIRE_THROW( reserve_asset( alice_id, basset.amount( reserve_amount ) ), asset_reserve_invalid_on_mia );\n\n      BOOST_TEST_MESSAGE( \"Test reserve operation on prediction market asset\" );\n      transfer( committee_account, alice_id, casset.amount( init_balance ) );\n      borrow( alice_id, passet.amount( init_balance ), casset.amount( init_balance ) );\n      GRAPHENE_REQUIRE_THROW( reserve_asset( alice_id, passet.amount( reserve_amount ) ), asset_reserve_invalid_on_mia );\n\n      BOOST_TEST_MESSAGE( \"Test reserve operation on user issued asset\" );\n      _issue_uia( alice, uasset.amount( init_balance ) );\n      BOOST_CHECK_EQUAL( get_balance( alice, uasset ), init_balance );\n      verify_asset_supplies(db);\n\n      BOOST_TEST_MESSAGE( \"Reserving asset\" );\n      initial_reserve = uasset.reserved( db );\n      reserve_asset( alice_id, uasset.amount( reserve_amount  ) );\n      BOOST_CHECK_EQUAL( get_balance( alice, uasset ), init_balance - reserve_amount );\n      BOOST_CHECK_EQUAL( (uasset.reserved( db ) - initial_reserve).value, reserve_amount );\n      verify_asset_supplies(db);\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( call_order_update_evaluator_test )\n{\n   try\n   {\n      ACTORS( (alice) (bob) );\n      transfer(committee_account, alice_id, asset(10000000 * GRAPHENE_BLOCKCHAIN_PRECISION));\n\n      const auto& core   = asset_id_type()(db);\n\n      // attempt to increase current supply beyond max_supply\n      const auto& bitjmj = create_bitasset( \"JMJBIT\", alice_id, 100, charge_market_fee, 2U, \n            asset_id_type{}, GRAPHENE_MAX_SHARE_SUPPLY / 2 );\n      auto bitjmj_id = bitjmj.get_id();\n      share_type original_max_supply = bitjmj.options.max_supply;\n\n      {\n         BOOST_TEST_MESSAGE( \"Setting price feed to $100000 / 1\" );\n         update_feed_producers( bitjmj, {alice_id} );\n         price_feed current_feed;\n         current_feed.settlement_price = bitjmj.amount( 100000 ) / core.amount(1);\n         publish_feed( bitjmj, alice, current_feed );\n      }\n\n      {\n         BOOST_TEST_MESSAGE( \"Attempting a call_order_update that exceeds max_supply\" );\n         call_order_update_operation op;\n         op.funding_account = alice_id;\n         op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         op.delta_debt = asset( bitjmj.options.max_supply + 1, bitjmj.id );\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n         generate_block();\n      }\n\n      // advance past hardfork\n      generate_blocks( HARDFORK_CORE_1465_TIME );\n      set_expiration( db, trx );\n\n      // bitjmj should have its problem corrected\n      auto newbitjmj = bitjmj_id(db);\n      BOOST_REQUIRE_GT(newbitjmj.options.max_supply.value, original_max_supply.value);\n\n      // now try with an asset after the hardfork\n      const auto& bitusd = create_bitasset( \"USDBIT\", alice_id, 100, charge_market_fee, 2U, \n            asset_id_type{}, GRAPHENE_MAX_SHARE_SUPPLY / 2 );\n\n      {\n         BOOST_TEST_MESSAGE( \"Setting price feed to $100000 / 1\" );\n         update_feed_producers( bitusd, {alice_id} );\n         price_feed current_feed;\n         current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1);\n         publish_feed( bitusd, alice_id(db), current_feed );\n      }\n\n      {\n         BOOST_TEST_MESSAGE( \"Attempting a call_order_update that exceeds max_supply\" );\n         call_order_update_operation op;\n         op.funding_account = alice_id;\n         op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         op.delta_debt = asset( bitusd.options.max_supply + 1, bitusd.id );\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception );\n      }\n\n      {\n         BOOST_TEST_MESSAGE( \"Creating 2 bitusd and transferring to bob (increases current supply)\" );\n         call_order_update_operation op;\n         op.funding_account = alice_id;\n         op.delta_collateral = asset( 100 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         op.delta_debt = asset( 2, bitusd.id );\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n         transfer( alice_id(db), bob_id(db), asset( 2, bitusd.id ) );\n      }\n\n      {\n         BOOST_TEST_MESSAGE( \"Again attempting a call_order_update_operation that is max_supply - 1 (should throw)\" );\n         call_order_update_operation op;\n         op.funding_account = alice_id;\n         op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         op.delta_debt = asset( bitusd.options.max_supply - 1, bitusd.id );\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception);\n      }\n\n      {\n         BOOST_TEST_MESSAGE( \"Again attempting a call_order_update_operation that equals max_supply (should work)\" );\n         call_order_update_operation op;\n         op.funding_account = alice_id;\n         op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION );\n         op.delta_debt = asset( bitusd.options.max_supply - 2, bitusd.id );\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      }\n   } FC_LOG_AND_RETHROW()\n}\n\n/**\n * This test demonstrates how using the call_order_update_operation to\n * trigger a margin call is legal if there is a matching order.\n */\nBOOST_AUTO_TEST_CASE( cover_with_collateral_test )\n{\n   try\n   {\n      ACTORS((alice)(bob)(sam));\n      const auto& bitusd = create_bitasset(\"USDBIT\", sam_id);\n      const auto& core   = asset_id_type()(db);\n\n      BOOST_TEST_MESSAGE( \"Setting price feed to $0.02 / 100\" );\n      transfer(committee_account, alice_id, asset(10000000));\n      update_feed_producers( bitusd, {sam.id} );\n\n      price_feed current_feed;\n      current_feed.settlement_price = bitusd.amount( 2 ) / core.amount(100);\n      publish_feed( bitusd, sam, current_feed );\n\n      BOOST_REQUIRE( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );\n\n      BOOST_TEST_MESSAGE( \"Alice borrows some BitUSD at 2x collateral and gives it to Bob\" );\n      const call_order_object* call_order = borrow( alice, bitusd.amount(100), asset(10000) );\n      BOOST_REQUIRE( call_order != nullptr );\n\n      // wdump( (*call_order) );\n\n      transfer( alice_id, bob_id, bitusd.amount(100) );\n\n      auto update_call_order = [&]( account_id_type acct, asset delta_collateral, asset delta_debt )\n      {\n         call_order_update_operation op;\n         op.funding_account = acct;\n         op.delta_collateral = delta_collateral;\n         op.delta_debt = delta_debt;\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      } ;\n\n      // margin call requirement:  1.75x\n      BOOST_TEST_MESSAGE( \"Alice decreases her collateral to maint level plus one satoshi\" );\n      asset delta_collateral = asset(int64_t( current_feed.maintenance_collateral_ratio ) * 5000 / GRAPHENE_COLLATERAL_RATIO_DENOM - 10000 + 1 );\n      update_call_order( alice_id, delta_collateral, bitusd.amount(0) );\n      // wdump( (*call_order) );\n\n      BOOST_TEST_MESSAGE( \"Alice cannot decrease her collateral by one satoshi, there is no buyer\" );\n      GRAPHENE_REQUIRE_THROW( update_call_order( alice_id, asset(-1), bitusd.amount(0) ), call_order_update_unfilled_margin_call );\n      // wdump( (*call_order) );\n\n      BOOST_TEST_MESSAGE( \"Bob offers to sell most of the BitUSD at the feed\" );\n      const limit_order_object* order = create_sell_order( bob_id, bitusd.amount(99), asset(4950) );\n      BOOST_REQUIRE( order != nullptr );\n      limit_order_id_type order1_id = order->id;\n      BOOST_CHECK_EQUAL( order->for_sale.value, 99 );\n      // wdump( (*call_order) );\n\n      BOOST_TEST_MESSAGE( \"Alice still cannot decrease her collateral to maint level\" );\n      GRAPHENE_REQUIRE_THROW( update_call_order( alice_id, asset(-1), bitusd.amount(0) ), call_order_update_unfilled_margin_call );\n      // wdump( (*call_order) );\n\n      BOOST_TEST_MESSAGE( \"Bob offers to sell the last of his BitUSD in another order\" );\n      order = create_sell_order( bob_id, bitusd.amount(1), asset(50) );\n      BOOST_REQUIRE( order != nullptr );\n      limit_order_id_type order2_id = order->id;\n      BOOST_CHECK_EQUAL( order->for_sale.value, 1 );\n      // wdump( (*call_order) );\n\n      BOOST_TEST_MESSAGE( \"Alice decreases her collateral to maint level and Bob's orders fill\" );\n      update_call_order( alice_id, asset(-1), bitusd.amount(0) );\n\n      BOOST_CHECK( db.find( order1_id ) == nullptr );\n      BOOST_CHECK( db.find( order2_id ) == nullptr );\n   }\n   catch (fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( vesting_balance_create_test )\n{ try {\n   INVOKE( create_uia );\n\n   const asset_object& core = asset_id_type()(db);\n   const asset_object& test_asset = get_asset(UIA_TEST_SYMBOL);\n\n   vesting_balance_create_operation op;\n   op.fee = core.amount( 0 );\n   op.creator = account_id_type();\n   op.owner = account_id_type();\n   op.amount = test_asset.amount( 100 );\n   //op.vesting_seconds = 60*60*24;\n   op.policy = cdd_vesting_policy_initializer{ 60*60*24 };\n\n   // Fee must be non-negative\n   REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) );\n   REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(0) );\n   REQUIRE_OP_VALIDATION_FAILURE( op, fee, core.amount(-1) );\n\n   // Amount must be positive\n   REQUIRE_OP_VALIDATION_SUCCESS( op, amount, core.amount(1) );\n   REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(0) );\n   REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(-1) );\n\n   // Setup world state we will need to test actual evaluation\n   const account_object& alice_account = create_account(\"alice\");\n   const account_object& bob_account = create_account(\"bob\");\n\n   transfer(committee_account(db), alice_account, core.amount(100000));\n\n   op.creator = alice_account.get_id();\n   op.owner = alice_account.get_id();\n\n   account_id_type nobody = account_id_type(1234);\n\n   trx.operations.push_back(op);\n   // Invalid account_id's\n   REQUIRE_THROW_WITH_VALUE( op, creator, nobody );\n   REQUIRE_THROW_WITH_VALUE( op,   owner, nobody );\n\n   // Insufficient funds\n   REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(999999999) );\n   // Alice can fund a bond to herself or to Bob\n   op.amount = core.amount( 1000 );\n   REQUIRE_OP_EVALUATION_SUCCESS( op, owner, alice_account.get_id() );\n   REQUIRE_OP_EVALUATION_SUCCESS( op, owner,   bob_account.get_id() );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test )\n{ try {\n   INVOKE( create_uia );\n   // required for head block time\n   generate_block();\n\n   const asset_object& core = asset_id_type()(db);\n   const asset_object& test_asset = get_asset( UIA_TEST_SYMBOL );\n\n   vesting_balance_withdraw_operation op;\n   op.fee = core.amount( 0 );\n   op.vesting_balance = vesting_balance_id_type();\n   op.owner = account_id_type();\n   op.amount = test_asset.amount( 100 );\n\n   // Fee must be non-negative\n   REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(  1 )  );\n   REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(  0 )  );\n   REQUIRE_OP_VALIDATION_FAILURE( op, fee, core.amount( -1 ) );\n\n   // Amount must be positive\n   REQUIRE_OP_VALIDATION_SUCCESS( op, amount, core.amount(  1 ) );\n   REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(  0 ) );\n   REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount( -1 ) );\n\n   // Setup world state we will need to test actual evaluation\n   const account_object& alice_account = create_account( \"alice\" );\n   const account_object& bob_account = create_account( \"bob\" );\n\n   transfer( committee_account(db), alice_account, core.amount( 1000000 ) );\n\n   auto spin_vbo_clock = [&]( const vesting_balance_object& vbo, uint32_t dt_secs )\n   {\n      // HACK:  This just modifies the DB creation record to be further\n      //    in the past\n      db.modify( vbo, [&]( vesting_balance_object& _vbo )\n      {\n         _vbo.policy.get<cdd_vesting_policy>().coin_seconds_earned_last_update -= dt_secs;\n      } );\n   };\n\n   auto create_vbo = [&](\n      account_id_type creator, account_id_type owner,\n      asset amount, uint32_t vesting_seconds, uint32_t elapsed_seconds\n      ) -> const vesting_balance_object&\n   {\n      transaction tx;\n\n      vesting_balance_create_operation create_op;\n      create_op.fee = core.amount( 0 );\n      create_op.creator = creator;\n      create_op.owner = owner;\n      create_op.amount = amount;\n      create_op.policy = cdd_vesting_policy_initializer(vesting_seconds);\n      tx.operations.push_back( create_op );\n      set_expiration( db, tx );\n\n      processed_transaction ptx = PUSH_TX( db,  tx, ~0  );\n      const vesting_balance_object& vbo = vesting_balance_id_type(\n         ptx.operation_results[0].get<object_id_type>())(db);\n\n      if( elapsed_seconds > 0 )\n         spin_vbo_clock( vbo, elapsed_seconds );\n      return vbo;\n   };\n\n   auto top_up = [&]()\n   {\n      trx.clear();\n      transfer( committee_account(db),\n         alice_account,\n         core.amount( 1000000 - db.get_balance( alice_account, core ).amount )\n         );\n      FC_ASSERT( db.get_balance( alice_account, core ).amount == 1000000 );\n      trx.clear();\n      trx.operations.push_back( op );\n   };\n\n   trx.clear();\n   trx.operations.push_back( op );\n\n   {\n      // Try withdrawing a single satoshi\n      const vesting_balance_object& vbo = create_vbo(\n         alice_account.id, alice_account.id, core.amount( 10000 ), 1000, 0);\n\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990000 );\n\n      op.vesting_balance = vbo.id;\n      op.owner = alice_account.id;\n\n      REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(1) );\n\n      // spin the clock and make sure we can withdraw 1/1000 in 1 second\n      spin_vbo_clock( vbo, 1 );\n      // Alice shouldn't be able to withdraw 11, it's too much\n      REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(11) );\n      op.amount = core.amount( 1 );\n      // Bob shouldn't be able to withdraw anything\n      REQUIRE_THROW_WITH_VALUE( op, owner, bob_account.id );\n      // Shouldn't be able to get out different asset than was put in\n      REQUIRE_THROW_WITH_VALUE( op, amount, test_asset.amount(1) );\n      // Withdraw the max, we are OK...\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(10) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990010 );\n      top_up();\n   }\n\n   // Make sure we can withdraw the correct amount after 999 seconds\n   {\n      const vesting_balance_object& vbo = create_vbo(\n         alice_account.id, alice_account.id, core.amount( 10000 ), 1000, 999);\n\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990000 );\n\n      op.vesting_balance = vbo.id;\n      op.owner = alice_account.id;\n      // Withdraw one satoshi too much, no dice\n      REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(9991) );\n      // Withdraw just the right amount, success!\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(9990) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  999990 );\n      top_up();\n   }\n\n   // Make sure we can withdraw the whole thing after 1000 seconds\n   {\n      const vesting_balance_object& vbo = create_vbo(\n         alice_account.id, alice_account.id, core.amount( 10000 ), 1000, 1000);\n\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990000 );\n\n      op.vesting_balance = vbo.id;\n      op.owner = alice_account.id;\n      // Withdraw one satoshi too much, no dice\n      REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(10001) );\n      // Withdraw just the right amount, success!\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(10000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount == 1000000 );\n   }\n\n   // Make sure that we can't withdraw a single extra satoshi no matter how old it is\n   {\n      const vesting_balance_object& vbo = create_vbo(\n         alice_account.id, alice_account.id, core.amount( 10000 ), 1000, 123456);\n\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990000 );\n\n      op.vesting_balance = vbo.id;\n      op.owner = alice_account.id;\n      // Withdraw one satoshi too much, no dice\n      REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(10001) );\n      // Withdraw just the right amount, success!\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(10000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount == 1000000 );\n   }\n\n   // Try withdrawing in three max installments:\n   //   5000 after  500      seconds\n   //   2000 after  400 more seconds\n   //   3000 after 1000 more seconds\n   {\n      const vesting_balance_object& vbo = create_vbo(\n         alice_account.id, alice_account.id, core.amount( 10000 ), 1000, 0);\n\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990000 );\n\n      op.vesting_balance = vbo.id;\n      op.owner = alice_account.id;\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(   1) );\n      spin_vbo_clock( vbo, 499 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(5000) );\n      spin_vbo_clock( vbo,   1 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(5001) );\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(5000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  995000 );\n\n      spin_vbo_clock( vbo, 399 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(2000) );\n      spin_vbo_clock( vbo,   1 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(2001) );\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(2000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  997000 );\n\n      spin_vbo_clock( vbo, 999 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(3000) );\n      spin_vbo_clock( vbo, 1   );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(3001) );\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(3000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount == 1000000 );\n   }\n\n   //\n   // Increase by 10,000 csd / sec initially.\n   // After 500 seconds, we have 5,000,000 csd.\n   // Withdraw 2,000, we are now at 8,000 csd / sec.\n   // At 8,000 csd / sec, it will take us 625 seconds to mature.\n   //\n   {\n      const vesting_balance_object& vbo = create_vbo(\n         alice_account.id, alice_account.id, core.amount( 10000 ), 1000, 0);\n\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  990000 );\n\n      op.vesting_balance = vbo.id;\n      op.owner = alice_account.id;\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(   1) );\n      spin_vbo_clock( vbo, 500 );\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(2000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount ==  992000 );\n\n      spin_vbo_clock( vbo, 624 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(8000) );\n      spin_vbo_clock( vbo,   1 );\n      REQUIRE_THROW_WITH_VALUE     ( op, amount, core.amount(8001) );\n      REQUIRE_OP_EVALUATION_SUCCESS( op, amount, core.amount(8000) );\n      FC_ASSERT( db.get_balance( alice_account,       core ).amount == 1000000 );\n   }\n   // TODO:  Test with non-core asset and Bob account\n} FC_LOG_AND_RETHROW() }\n\n// TODO:  Write linear VBO tests\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/operation_tests2.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/chain/balance_object.hpp>\n#include <graphene/chain/budget_record_object.hpp>\n#include <graphene/chain/committee_member_object.hpp>\n#include <graphene/chain/market_object.hpp>\n#include <graphene/chain/withdraw_permission_object.hpp>\n#include <graphene/chain/witness_object.hpp>\n#include <graphene/chain/worker_object.hpp>\n\n#include <graphene/witness/witness.hpp>\n\n#include <graphene/utilities/tempdir.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture )\n\n/***\n * A descriptor of a particular withdrawal period\n */\nstruct withdrawal_period_descriptor {\n   withdrawal_period_descriptor(const time_point_sec start, const time_point_sec end, const asset available, const asset claimed)\n      : period_start_time(start), period_end_time(end), available_this_period(available), claimed_this_period(claimed) {}\n\n   // Start of period\n   time_point_sec period_start_time;\n\n   // End of period\n   time_point_sec period_end_time;\n\n   // Quantify how much is still available to be withdrawn during this period\n   asset available_this_period;\n\n   // Quantify how much has already been claimed during this period\n   asset claimed_this_period;\n\n   string const to_string() const {\n       string asset_id = fc::to_string(available_this_period.asset_id.space_id)\n                         + \".\" + fc::to_string(available_this_period.asset_id.type_id)\n                         + \".\" + fc::to_string(available_this_period.asset_id.instance.value);\n       string text = fc::to_string(available_this_period.amount.value)\n                     + \" \" + asset_id\n                     + \" is available from \" + period_start_time.to_iso_string()\n                     + \" to \" + period_end_time.to_iso_string();\n       return text;\n   }\n};\n\n\n/***\n * Get a description of the current withdrawal period\n * @param current_time   Current time\n * @return A description of the current period\n */\nwithdrawal_period_descriptor current_period(const withdraw_permission_object& permit, fc::time_point_sec current_time) {\n   // @todo [6] Is there a potential race condition where a call to available_this_period might become out of sync with this function's later use of period start time?\n   asset available = permit.available_this_period(current_time);\n   asset claimed = asset(permit.withdrawal_limit.amount - available.amount, permit.withdrawal_limit.asset_id);\n   auto periods = (current_time - permit.period_start_time).to_seconds() / permit.withdrawal_period_sec;\n   time_point_sec current_period_start = permit.period_start_time + (periods * permit.withdrawal_period_sec);\n   time_point_sec current_period_end = current_period_start + permit.withdrawal_period_sec;\n   withdrawal_period_descriptor descriptor = withdrawal_period_descriptor(current_period_start, current_period_end, available, claimed);\n\n   return descriptor;\n}\n\n/**\n * This auxiliary test is used for two purposes:\n * (a) it checks the creation of withdrawal claims,\n * (b) it is used as a precursor for tests that evaluate withdrawal claims.\n *\n * NOTE: This test verifies proper withdrawal claim behavior.\n */\nBOOST_AUTO_TEST_CASE( withdraw_permission_create )\n{ try {\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   auto dan_private_key = generate_private_key(\"dan\");\n   account_id_type nathan_id = create_account(\"nathan\", nathan_private_key.get_public_key()).id;\n   account_id_type dan_id = create_account(\"dan\", dan_private_key.get_public_key()).id;\n\n   transfer(account_id_type(), nathan_id, asset(1000));\n   generate_block();\n   set_expiration( db, trx );\n\n   {\n      withdraw_permission_create_operation op;\n      op.authorized_account = dan_id;\n      op.withdraw_from_account = nathan_id;\n      op.withdrawal_limit = asset(5);\n      op.withdrawal_period_sec = fc::hours(1).to_seconds();\n      op.periods_until_expiration = 5;\n      op.period_start_time = db.head_block_time() + db.get_global_properties().parameters.block_interval*5; // 5 blocks after fork time\n      trx.operations.push_back(op);\n      REQUIRE_OP_VALIDATION_FAILURE(op, withdrawal_limit, asset());\n      REQUIRE_OP_VALIDATION_FAILURE(op, periods_until_expiration, 0);\n      REQUIRE_OP_VALIDATION_FAILURE(op, withdraw_from_account, dan_id);\n      REQUIRE_OP_VALIDATION_FAILURE(op, withdrawal_period_sec, 0);\n      REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(10, asset_id_type(10)));\n      REQUIRE_THROW_WITH_VALUE(op, authorized_account, account_id_type(1000));\n      REQUIRE_THROW_WITH_VALUE(op, period_start_time, fc::time_point_sec(10000));\n      REQUIRE_THROW_WITH_VALUE(op, withdrawal_period_sec, 1);\n      trx.operations.back() = op;\n   }\n   sign( trx, nathan_private_key );\n   PUSH_TX( db, trx );\n   trx.clear();\n} FC_LOG_AND_RETHROW() }\n\n/**\n * Test the claims of withdrawals both before and during\n * authorized withdrawal periods.\n * NOTE: The simulated elapse of blockchain time through the use of\n * generate_blocks() must be carefully used in order to simulate\n * this test.\n * NOTE: This test verifies proper withdrawal claim behavior.\n */\nBOOST_AUTO_TEST_CASE( withdraw_permission_test )\n{ try {\n   INVOKE(withdraw_permission_create);\n\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   auto dan_private_key = generate_private_key(\"dan\");\n   account_id_type nathan_id = get_account(\"nathan\").id;\n   account_id_type dan_id = get_account(\"dan\").id;\n   withdraw_permission_id_type permit;\n   set_expiration( db, trx );\n\n   fc::time_point_sec first_start_time;\n   {\n      const withdraw_permission_object& permit_object = permit(db);\n      BOOST_CHECK(permit_object.authorized_account == dan_id);\n      BOOST_CHECK(permit_object.withdraw_from_account == nathan_id);\n      BOOST_CHECK(permit_object.period_start_time > db.head_block_time());\n      first_start_time = permit_object.period_start_time;\n      BOOST_CHECK(permit_object.withdrawal_limit == asset(5));\n      BOOST_CHECK(permit_object.withdrawal_period_sec == fc::hours(1).to_seconds());\n      BOOST_CHECK(permit_object.expiration == first_start_time + permit_object.withdrawal_period_sec*5 );\n   }\n\n   {\n      withdraw_permission_claim_operation op;\n      op.withdraw_permission = permit;\n      op.withdraw_from_account = nathan_id;\n      op.withdraw_to_account = dan_id;\n      op.amount_to_withdraw = asset(1);\n      set_expiration( db, trx );\n\n      trx.operations.push_back(op);\n      sign( trx, dan_private_key ); // Transaction should be signed to be valid\n      //Throws because we haven't entered the first withdrawal period yet.\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);\n      //Get to the actual withdrawal period\n      bool miss_intermediate_blocks = false; // Required to have generate_blocks() elapse flush to the time of interest\n      generate_blocks(first_start_time, miss_intermediate_blocks);\n          set_expiration( db, trx );\n\n      REQUIRE_THROW_WITH_VALUE(op, withdraw_permission, withdraw_permission_id_type(5));\n      REQUIRE_THROW_WITH_VALUE(op, withdraw_from_account, dan_id);\n      REQUIRE_THROW_WITH_VALUE(op, withdraw_from_account, account_id_type());\n      REQUIRE_THROW_WITH_VALUE(op, withdraw_to_account, nathan_id);\n      REQUIRE_THROW_WITH_VALUE(op, withdraw_to_account, account_id_type());\n      REQUIRE_THROW_WITH_VALUE(op, amount_to_withdraw, asset(10));\n      REQUIRE_THROW_WITH_VALUE(op, amount_to_withdraw, asset(6));\n      set_expiration( db, trx );\n      trx.clear();\n      trx.operations.push_back(op);\n      sign( trx, dan_private_key );\n      PUSH_TX( db, trx ); // <-- Claim #1\n\n      // would be legal on its own, but doesn't work because trx already withdrew\n      REQUIRE_THROW_WITH_VALUE(op, amount_to_withdraw, asset(5));\n\n      // Make sure we can withdraw again this period, as long as we're not exceeding the periodic limit\n      trx.clear();\n      // withdraw 1\n      trx.operations = {op};\n      // make it different from previous trx so it's non-duplicate\n      trx.expiration += fc::seconds(1);\n      sign( trx, dan_private_key );\n      PUSH_TX( db, trx ); // <-- Claim #2\n      trx.clear();\n   }\n\n   // Account for two (2) claims of one (1) unit\n   BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 998);\n   BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 2);\n\n   {\n      const withdraw_permission_object& permit_object = permit(db);\n      BOOST_CHECK(permit_object.authorized_account == dan_id);\n      BOOST_CHECK(permit_object.withdraw_from_account == nathan_id);\n      BOOST_CHECK(permit_object.period_start_time == first_start_time);\n      BOOST_CHECK(permit_object.withdrawal_limit == asset(5));\n      BOOST_CHECK(permit_object.withdrawal_period_sec == fc::hours(1).to_seconds());\n      BOOST_CHECK_EQUAL(permit_object.claimed_this_period.value, 2 ); // <-- Account for two (2) claims of one (1) unit\n      BOOST_CHECK(permit_object.expiration == first_start_time + 5*permit_object.withdrawal_period_sec);\n      generate_blocks(first_start_time + permit_object.withdrawal_period_sec);\n      // lazy update:  verify period_start_time isn't updated until new trx occurs\n      BOOST_CHECK(permit_object.period_start_time == first_start_time);\n   }\n\n   {\n      // Leave Nathan with one unit\n      transfer(nathan_id, dan_id, asset(997));\n\n      // Attempt a withdrawal claim for units than available\n      withdraw_permission_claim_operation op;\n      op.withdraw_permission = permit;\n      op.withdraw_from_account = nathan_id;\n      op.withdraw_to_account = dan_id;\n      op.amount_to_withdraw = asset(5);\n      trx.operations.push_back(op);\n      set_expiration( db, trx );\n      sign( trx, dan_private_key );\n      //Throws because nathan doesn't have the money\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);\n\n      // Attempt a withdrawal claim for which nathan does have sufficient units\n      op.amount_to_withdraw = asset(1);\n      trx.clear();\n      trx.operations = {op};\n      set_expiration( db, trx );\n      sign( trx, dan_private_key );\n      PUSH_TX( db, trx );\n   }\n\n   BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 0);\n   BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 1000);\n   trx.clear();\n   transfer(dan_id, nathan_id, asset(1000));\n\n   {\n      const withdraw_permission_object& permit_object = permit(db);\n      BOOST_CHECK(permit_object.authorized_account == dan_id);\n      BOOST_CHECK(permit_object.withdraw_from_account == nathan_id);\n      BOOST_CHECK(permit_object.period_start_time == first_start_time + permit_object.withdrawal_period_sec);\n      BOOST_CHECK(permit_object.expiration == first_start_time + 5*permit_object.withdrawal_period_sec);\n      BOOST_CHECK(permit_object.withdrawal_limit == asset(5));\n      BOOST_CHECK(permit_object.withdrawal_period_sec == fc::hours(1).to_seconds());\n      generate_blocks(permit_object.expiration);\n   }\n   // Ensure the permit object has been garbage collected\n   BOOST_CHECK(db.find_object(permit) == nullptr);\n\n   {\n      withdraw_permission_claim_operation op;\n      op.withdraw_permission = permit;\n      op.withdraw_from_account = nathan_id;\n      op.withdraw_to_account = dan_id;\n      op.amount_to_withdraw = asset(5);\n      trx.operations.push_back(op);\n      set_expiration( db, trx );\n      sign( trx, dan_private_key );\n      //Throws because the permission has expired\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( withdraw_permission_nominal_case )\n{ try {\n   INVOKE(withdraw_permission_create);\n\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   auto dan_private_key = generate_private_key(\"dan\");\n   account_id_type nathan_id = get_account(\"nathan\").id;\n   account_id_type dan_id = get_account(\"dan\").id;\n   withdraw_permission_id_type permit;\n\n   // Wait until the permission period's start time\n   const withdraw_permission_object& first_permit_object = permit(db);\n   generate_blocks(\n           first_permit_object.period_start_time);\n\n   // Loop through the withdrawal periods and claim a withdrawal\n   while(true)\n   {\n      const withdraw_permission_object& permit_object = permit(db);\n      //wdump( (permit_object) );\n      withdraw_permission_claim_operation op;\n      op.withdraw_permission = permit;\n      op.withdraw_from_account = nathan_id;\n      op.withdraw_to_account = dan_id;\n      op.amount_to_withdraw = asset(5);\n      trx.operations.push_back(op);\n      set_expiration( db, trx );\n      sign( trx, dan_private_key );\n      PUSH_TX( db, trx );\n      // tx's involving withdraw_permissions can't delete it even\n      // if no further withdrawals are possible\n      BOOST_CHECK(db.find_object(permit) != nullptr);\n      BOOST_CHECK( permit_object.claimed_this_period == 5 );\n      BOOST_CHECK_EQUAL( permit_object.available_this_period(db.head_block_time()).amount.value, 0 );\n      BOOST_CHECK_EQUAL( current_period(permit_object, db.head_block_time()).available_this_period.amount.value, 0 );\n      trx.clear();\n      generate_blocks(\n           permit_object.period_start_time\n         + permit_object.withdrawal_period_sec );\n      if( db.find_object(permit) == nullptr )\n         break;\n   }\n\n   BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 975);\n   BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 25);\n} FC_LOG_AND_RETHROW() }\n\n/**\n * Test asset whitelisting feature for withdrawals.\n * Reproduces https://github.com/bitshares/bitshares-core/issues/942 and tests the fix for it.\n */\nBOOST_AUTO_TEST_CASE( withdraw_permission_whitelist_asset_test )\n{ try {\n\n   uint32_t skip = database::skip_witness_signature\n                 | database::skip_transaction_signatures\n                 | database::skip_transaction_dupe_check\n                 | database::skip_block_size_check\n                 | database::skip_tapos_check\n                 | database::skip_merkle_check\n                 ;\n\n   generate_block( skip );\n\n   for( int i=0; i<2; i++ )\n   {\n      int blocks = 0;\n      set_expiration( db, trx );\n\n      ACTORS( (nathan)(dan)(izzy) );\n\n      const asset_id_type uia_id = create_user_issued_asset( \"ADVANCED\", izzy_id(db), white_list ).id;\n\n      issue_uia( nathan_id, asset(1000, uia_id) );\n\n      // Make a whitelist authority\n      {\n         BOOST_TEST_MESSAGE( \"Changing the whitelist authority\" );\n         asset_update_operation uop;\n         uop.issuer = izzy_id;\n         uop.asset_to_update = uia_id;\n         uop.new_options = uia_id(db).options;\n         uop.new_options.whitelist_authorities.insert(izzy_id);\n         trx.operations.push_back(uop);\n         PUSH_TX( db, trx, ~0 );\n         trx.operations.clear();\n      }\n\n      // Add dan to whitelist\n      {\n         upgrade_to_lifetime_member( izzy_id );\n\n         account_whitelist_operation wop;\n         wop.authorizing_account = izzy_id;\n         wop.account_to_list = dan_id;\n         wop.new_listing = account_whitelist_operation::white_listed;\n         trx.operations.push_back( wop );\n         PUSH_TX( db, trx, ~0 );\n         trx.operations.clear();\n      }\n\n      // create withdraw permission\n      {\n         withdraw_permission_create_operation op;\n         op.authorized_account = dan_id;\n         op.withdraw_from_account = nathan_id;\n         op.withdrawal_limit = asset(5, uia_id);\n         op.withdrawal_period_sec = fc::hours(1).to_seconds();\n         op.periods_until_expiration = 5;\n         op.period_start_time = db.head_block_time() + 1;\n         trx.operations.push_back(op);\n         PUSH_TX( db, trx, ~0 );\n         trx.operations.clear();\n      }\n\n      withdraw_permission_id_type first_permit_id; // first object must have id 0\n\n      generate_block( skip ); // get to the time point that able to withdraw\n      ++blocks;\n      set_expiration( db, trx );\n\n      // try claim a withdrawal\n      {\n         withdraw_permission_claim_operation op;\n         op.withdraw_permission = first_permit_id;\n         op.withdraw_from_account = nathan_id;\n         op.withdraw_to_account = dan_id;\n         op.amount_to_withdraw = asset(5, uia_id);\n         trx.operations.push_back(op);\n         GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception );\n         trx.operations.clear();\n      }\n\n      // TODO add test cases for other white-listing features\n\n      // undo above tx's and reset\n      generate_block( skip );\n      ++blocks;\n      while( blocks > 0 )\n      {\n         db.pop_block();\n         --blocks;\n      }\n   }\n\n} FC_LOG_AND_RETHROW() }\n\n\n/**\n * This case checks to see whether the amount claimed within any particular withdrawal period\n * is properly reflected within the permission object.\n * The maximum withdrawal per period will be limited to 5 units.\n * There are a total of 5 withdrawal periods that are permitted.\n * The test will evaluate the following withdrawal pattern:\n * (1) during Period 1, a withdrawal of 4 units,\n * (2) during Period 2, a withdrawal of 1 units,\n * (3) during Period 3, a withdrawal of 0 units,\n * (4) during Period 4, a withdrawal of 5 units,\n * (5) during Period 5, a withdrawal of 3 units.\n *\n * Total withdrawal will be 13 units.\n */\nBOOST_AUTO_TEST_CASE( withdraw_permission_incremental_case )\n{ try {\n    INVOKE(withdraw_permission_create);\n    time_point_sec expected_first_period_start_time = db.head_block_time() + db.get_global_properties().parameters.block_interval*5; // Hard-coded to synchronize with withdraw_permission_create()\n    uint64_t expected_period_duration_seconds = fc::hours(1).to_seconds(); // Hard-coded to synchronize with withdraw_permission_create()\n\n    auto nathan_private_key = generate_private_key(\"nathan\");\n    auto dan_private_key = generate_private_key(\"dan\");\n    account_id_type nathan_id = get_account(\"nathan\").id;\n    account_id_type dan_id = get_account(\"dan\").id;\n    withdraw_permission_id_type permit;\n\n    // Wait until the permission period's start time\n    {\n        const withdraw_permission_object &before_first_permit_object = permit(db);\n        BOOST_CHECK_EQUAL(before_first_permit_object.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch());\n        generate_blocks(\n                before_first_permit_object.period_start_time);\n    }\n    // Before withdrawing, check the period description\n    const withdraw_permission_object &first_permit_object = permit(db);\n    const withdrawal_period_descriptor first_period = current_period(first_permit_object, db.head_block_time());\n    BOOST_CHECK_EQUAL(first_period.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch());\n    BOOST_CHECK_EQUAL(first_period.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + expected_period_duration_seconds);\n    BOOST_CHECK_EQUAL(first_period.available_this_period.amount.value, 5);\n\n    // Period 1: Withdraw 4 units\n    {\n        // Before claiming, check the period description\n        const withdraw_permission_object& permit_object = permit(db);\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        withdrawal_period_descriptor period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 5);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 0));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 1));\n\n        // Claim\n        withdraw_permission_claim_operation op;\n        op.withdraw_permission = permit;\n        op.withdraw_from_account = nathan_id;\n        op.withdraw_to_account = dan_id;\n        op.amount_to_withdraw = asset(4);\n        trx.operations.push_back(op);\n        set_expiration( db, trx );\n        sign( trx, dan_private_key );\n        PUSH_TX( db, trx );\n\n        // After claiming, check the period description\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        BOOST_CHECK( permit_object.claimed_this_period == 4 );\n        BOOST_CHECK_EQUAL( permit_object.claimed_this_period.value, 4 );\n        period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 1);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 0));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 1));\n\n        // Advance to next period\n        trx.clear();\n        generate_blocks(\n                permit_object.period_start_time\n                + permit_object.withdrawal_period_sec );\n    }\n\n    // Period 2: Withdraw 1 units\n    {\n        // Before claiming, check the period description\n        const withdraw_permission_object& permit_object = permit(db);\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        withdrawal_period_descriptor period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 5);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 1));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 2));\n\n        // Claim\n        withdraw_permission_claim_operation op;\n        op.withdraw_permission = permit;\n        op.withdraw_from_account = nathan_id;\n        op.withdraw_to_account = dan_id;\n        op.amount_to_withdraw = asset(1);\n        trx.operations.push_back(op);\n        set_expiration( db, trx );\n        sign( trx, dan_private_key );\n        PUSH_TX( db, trx );\n\n        // After claiming, check the period description\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        BOOST_CHECK( permit_object.claimed_this_period == 1 );\n        BOOST_CHECK_EQUAL( permit_object.claimed_this_period.value, 1 );\n        period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 4);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 1));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 2));\n\n        // Advance to next period\n        trx.clear();\n        generate_blocks(\n                permit_object.period_start_time\n                + permit_object.withdrawal_period_sec );\n    }\n\n    // Period 3: Withdraw 0 units\n    {\n        // Before claiming, check the period description\n        const withdraw_permission_object& permit_object = permit(db);\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        withdrawal_period_descriptor period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 5);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 2));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 3));\n\n        // No claim\n\n        // After doing nothing, check the period description\n        period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 5);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 2));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 3));\n\n        // Advance to end of Period 3\n        time_point_sec period_end_time = period_descriptor.period_end_time;\n        generate_blocks(period_end_time);\n    }\n\n    // Period 4: Withdraw 5 units\n    {\n        // Before claiming, check the period description\n        const withdraw_permission_object& permit_object = permit(db);\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        withdrawal_period_descriptor period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 5);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 3));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 4));\n\n        // Claim\n        withdraw_permission_claim_operation op;\n        op.withdraw_permission = permit;\n        op.withdraw_from_account = nathan_id;\n        op.withdraw_to_account = dan_id;\n        op.amount_to_withdraw = asset(5);\n        trx.operations.push_back(op);\n        set_expiration( db, trx );\n        sign( trx, dan_private_key );\n        PUSH_TX( db, trx );\n\n        // After claiming, check the period description\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        BOOST_CHECK( permit_object.claimed_this_period == 5 );\n        BOOST_CHECK_EQUAL( permit_object.claimed_this_period.value, 5 );\n        period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 0);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 3));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 4));\n\n        // Advance to next period\n        trx.clear();\n        generate_blocks(\n                permit_object.period_start_time\n                + permit_object.withdrawal_period_sec );\n    }\n\n    // Period 5: Withdraw 3 units\n    {\n        // Before claiming, check the period description\n        const withdraw_permission_object& permit_object = permit(db);\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        withdrawal_period_descriptor period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 5);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 4));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 5));\n\n        // Claim\n        withdraw_permission_claim_operation op;\n        op.withdraw_permission = permit;\n        op.withdraw_from_account = nathan_id;\n        op.withdraw_to_account = dan_id;\n        op.amount_to_withdraw = asset(3);\n        trx.operations.push_back(op);\n        set_expiration( db, trx );\n        sign( trx, dan_private_key );\n        PUSH_TX( db, trx );\n\n        // After claiming, check the period description\n        BOOST_CHECK(db.find_object(permit) != nullptr);\n        BOOST_CHECK( permit_object.claimed_this_period == 3 );\n        BOOST_CHECK_EQUAL( permit_object.claimed_this_period.value, 3 );\n        period_descriptor = current_period(permit_object, db.head_block_time());\n        BOOST_CHECK_EQUAL(period_descriptor.available_this_period.amount.value, 2);\n        BOOST_CHECK_EQUAL(period_descriptor.period_start_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 4));\n        BOOST_CHECK_EQUAL(period_descriptor.period_end_time.sec_since_epoch(), expected_first_period_start_time.sec_since_epoch() + (expected_period_duration_seconds * 5));\n\n        // Advance to next period\n        trx.clear();\n        generate_blocks(\n                permit_object.period_start_time\n                + permit_object.withdrawal_period_sec );\n    }\n\n    // Withdrawal periods completed\n    BOOST_CHECK(db.find_object(permit) == nullptr);\n\n    BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 987);\n    BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 13);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( withdraw_permission_update )\n{ try {\n   INVOKE(withdraw_permission_create);\n\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   account_id_type nathan_id = get_account(\"nathan\").id;\n   account_id_type dan_id = get_account(\"dan\").id;\n   withdraw_permission_id_type permit;\n   set_expiration( db, trx );\n\n   {\n      withdraw_permission_update_operation op;\n      op.permission_to_update = permit;\n      op.authorized_account = dan_id;\n      op.withdraw_from_account = nathan_id;\n      op.periods_until_expiration = 2;\n      op.period_start_time = db.head_block_time() + 10;\n      op.withdrawal_period_sec = 10;\n      op.withdrawal_limit = asset(12);\n      trx.operations.push_back(op);\n      REQUIRE_THROW_WITH_VALUE(op, periods_until_expiration, 0);\n      REQUIRE_THROW_WITH_VALUE(op, withdrawal_period_sec, 0);\n      REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(1, asset_id_type(12)));\n      REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(0));\n      REQUIRE_THROW_WITH_VALUE(op, withdraw_from_account, account_id_type(0));\n      REQUIRE_THROW_WITH_VALUE(op, authorized_account, account_id_type(0));\n      REQUIRE_THROW_WITH_VALUE(op, period_start_time, db.head_block_time() - 50);\n      trx.operations.back() = op;\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx );\n   }\n\n   {\n      const withdraw_permission_object& permit_object = db.get(permit);\n      BOOST_CHECK(permit_object.authorized_account == dan_id);\n      BOOST_CHECK(permit_object.withdraw_from_account == nathan_id);\n      BOOST_CHECK(permit_object.period_start_time == db.head_block_time() + 10);\n      BOOST_CHECK(permit_object.withdrawal_limit == asset(12));\n      BOOST_CHECK(permit_object.withdrawal_period_sec == 10);\n      // BOOST_CHECK(permit_object.remaining_periods == 2);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( withdraw_permission_delete )\n{ try {\n   INVOKE(withdraw_permission_update);\n\n   withdraw_permission_delete_operation op;\n   op.authorized_account = get_account(\"dan\").id;\n   op.withdraw_from_account = get_account(\"nathan\").id;\n   set_expiration( db, trx );\n   trx.operations.push_back(op);\n   sign( trx, generate_private_key(\"nathan\" ));\n   PUSH_TX( db, trx );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( mia_feeds )\n{ try {\n   ACTORS((nathan)(dan)(ben)(vikram));\n   asset_id_type bit_usd_id = create_bitasset(\"USDBIT\").id;\n\n   {\n      asset_update_operation op;\n      const asset_object& obj = bit_usd_id(db);\n      op.asset_to_update = bit_usd_id;\n      op.issuer = obj.issuer;\n      op.new_issuer = nathan_id;\n      op.new_options = obj.options;\n      op.new_options.flags &= ~witness_fed_asset;\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      generate_block();\n      trx.clear();\n   }\n   {\n      asset_update_feed_producers_operation op;\n      op.asset_to_update = bit_usd_id;\n      op.issuer = nathan_id;\n      op.new_feed_producers = {dan_id, ben_id, vikram_id};\n      trx.operations.push_back(op);\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx );\n      generate_block(database::skip_nothing);\n   }\n   {\n      const asset_bitasset_data_object& obj = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(obj.feeds.size(), 3u);\n      BOOST_CHECK( obj.current_feed.margin_call_params_equal( price_feed() ) );\n   }\n   {\n      const asset_object& bit_usd = bit_usd_id(db);\n      asset_publish_feed_operation op;\n      op.publisher = vikram_id;\n      op.asset_id = bit_usd_id;\n      op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30));\n\n      // We'll expire margins after a month\n      // Accept defaults for required collateral\n      trx.operations.emplace_back(op);\n      PUSH_TX( db, trx, ~0 );\n\n      const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db);\n      BOOST_CHECK(bitasset.current_feed.settlement_price.to_real() == 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      op.publisher = ben_id;\n      op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25));\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      op.publisher = dan_id;\n      op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40));\n      op.feed.maximum_short_squeeze_ratio = 1001;\n      op.feed.maintenance_collateral_ratio = 1001;\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION);\n      BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);\n\n      op.publisher = nathan_id;\n      trx.operations.back() = op;\n      GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n   }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( feed_limit_test )\n{ try {\n   INVOKE( mia_feeds );\n   const asset_object& bit_usd = get_asset(\"USDBIT\");\n   const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db);\n   GET_ACTOR(nathan);\n\n   BOOST_CHECK(!bitasset.current_feed.settlement_price.is_null());\n\n   BOOST_TEST_MESSAGE(\"Setting minimum feeds to 4\");\n   asset_update_bitasset_operation op;\n   op.new_options.minimum_feeds = 4;\n   op.asset_to_update = bit_usd.get_id();\n   op.issuer = bit_usd.issuer;\n   trx.operations = {op};\n   sign( trx, nathan_private_key );\n   PUSH_TX(db, trx);\n\n   BOOST_TEST_MESSAGE(\"Checking current_feed is null\");\n   BOOST_CHECK(bitasset.current_feed.settlement_price.is_null());\n\n   BOOST_TEST_MESSAGE(\"Setting minimum feeds to 3\");\n   op.new_options.minimum_feeds = 3;\n   trx.clear();\n   trx.operations = {op};\n   sign( trx, nathan_private_key );\n   PUSH_TX(db, trx);\n\n   BOOST_TEST_MESSAGE(\"Checking current_feed is not null\");\n   BOOST_CHECK(!bitasset.current_feed.settlement_price.is_null());\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( witness_create )\n{ try {\n\n   uint32_t skip = database::skip_witness_signature\n                 | database::skip_transaction_signatures\n                 | database::skip_transaction_dupe_check\n                 | database::skip_block_size_check\n                 | database::skip_tapos_check\n                 | database::skip_merkle_check\n                 ;\n   generate_block(skip);\n\n   auto wtplugin = app.register_plugin<graphene::witness_plugin::witness_plugin>();\n   wtplugin->plugin_set_app(&app);\n   boost::program_options::variables_map options;\n\n   // init witness key cahce\n   std::set< witness_id_type > caching_witnesses;\n   std::vector< std::string > witness_ids;\n   for( uint64_t i = 1; ; ++i )\n   {\n      witness_id_type wid(i);\n      caching_witnesses.insert( wid );\n      string wid_str = \"\\\"\" + std::string(object_id_type(wid)) + \"\\\"\";\n      witness_ids.push_back( wid_str );\n      if( !db.find(wid) )\n         break;\n   }\n   options.insert( std::make_pair( \"witness-id\", boost::program_options::variable_value( witness_ids, false ) ) );\n   wtplugin->plugin_initialize(options);\n   wtplugin->plugin_startup();\n\n   const auto& wit_key_cache = wtplugin->get_witness_key_cache();\n\n   // setup test account\n   ACTOR(nathan);\n   upgrade_to_lifetime_member(nathan_id);\n   trx.clear();\n\n   // create witness\n   witness_id_type nathan_witness_id = create_witness(nathan_id, nathan_private_key, skip).id;\n\n   // nathan should be in the cache\n   BOOST_CHECK_EQUAL( caching_witnesses.count(nathan_witness_id), 1u );\n\n   // nathan's key in the cache should still be null before a new block is generated\n   auto nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && !nathan_itr->second.valid() );\n\n   // Give nathan some voting stake\n   transfer(committee_account, nathan_id, asset(10000000));\n   generate_block(skip);\n\n   // nathan should be a witness now\n   BOOST_REQUIRE( db.find( nathan_witness_id ) );\n   // nathan's key in the cache should have been stored now\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == nathan_private_key.get_public_key() );\n\n   // undo the block\n   db.pop_block();\n\n   // nathan should not be a witness now\n   BOOST_REQUIRE( !db.find( nathan_witness_id ) );\n   // nathan's key in the cache should still be valid, since witness plugin doesn't get notified on popped block\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == nathan_private_key.get_public_key() );\n\n   // copy popped transactions\n   auto popped_tx = db._popped_tx;\n\n   // generate another block\n   generate_block(skip);\n\n   // nathan should not be a witness now\n   BOOST_REQUIRE( !db.find( nathan_witness_id ) );\n   // nathan's key in the cache should be null now\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && !nathan_itr->second.valid() );\n\n   // push the popped tx\n   for( const auto& tx : popped_tx )\n   {\n      PUSH_TX( db, tx, skip );\n   }\n   // generate another block\n   generate_block(skip);\n   set_expiration( db, trx );\n\n   // nathan should be a witness now\n   BOOST_REQUIRE( db.find( nathan_witness_id ) );\n   // nathan's key in the cache should have been stored now\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == nathan_private_key.get_public_key() );\n\n   // generate a new key\n   fc::ecc::private_key new_signing_key = fc::ecc::private_key::regenerate(fc::digest(\"nathan_new\"));\n\n   // update nathan's block signing key\n   {\n      witness_update_operation wuop;\n      wuop.witness_account = nathan_id;\n      wuop.witness = nathan_witness_id;\n      wuop.new_signing_key = new_signing_key.get_public_key();\n      signed_transaction wu_trx;\n      wu_trx.operations.push_back( wuop );\n      set_expiration( db, wu_trx );\n      PUSH_TX( db, wu_trx, skip );\n   }\n\n   // nathan's key in the cache should still be old key\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == nathan_private_key.get_public_key() );\n\n   // generate another block\n   generate_block(skip);\n\n   // nathan's key in the cache should have changed to new key\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == new_signing_key.get_public_key() );\n\n   // undo the block\n   db.pop_block();\n\n   // nathan's key in the cache should still be new key, since witness plugin doesn't get notified on popped block\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == new_signing_key.get_public_key() );\n\n   // generate another block\n   generate_block(skip);\n\n   // nathan's key in the cache should be old key now\n   nathan_itr = wit_key_cache.find( nathan_witness_id );\n   BOOST_CHECK( nathan_itr != wit_key_cache.end() && nathan_itr->second.valid()\n                && *nathan_itr->second == nathan_private_key.get_public_key() );\n\n   // voting\n   {\n      account_update_operation op;\n      op.account = nathan_id;\n      op.new_options = nathan_id(db).options;\n      op.new_options->votes.insert(nathan_witness_id(db).vote_id);\n      op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),\n                                                  [](vote_id_type id) { return id.type() == vote_id_type::witness; });\n      op.new_options->num_committee = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),\n                                                    [](vote_id_type id) { return id.type() == vote_id_type::committee; });\n      trx.operations.push_back(op);\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   const auto& witnesses = db.get_global_properties().active_witnesses;\n\n   // make sure we're in active_witnesses\n   auto itr = std::find(witnesses.begin(), witnesses.end(), nathan_witness_id);\n   BOOST_CHECK(itr != witnesses.end());\n\n   // generate blocks until we are at the beginning of a round\n   while( ((db.get_dynamic_global_properties().current_aslot + 1) % witnesses.size()) != 0 )\n      generate_block();\n\n   int produced = 0;\n   // Make sure we get scheduled at least once in witnesses.size()*2 blocks\n   // may take this many unless we measure where in the scheduling round we are\n   // TODO:  intense_test that repeats this loop many times\n   for( size_t i=0, n=witnesses.size()*2; i<n; i++ )\n   {\n      signed_block block = generate_block();\n      if( block.witness == nathan_witness_id )\n         produced++;\n   }\n   BOOST_CHECK_GE( produced, 1 );\n} FC_LOG_AND_RETHROW() }\n\n/**\n *  This test should verify that the asset_global_settle operation works as expected,\n *  make sure that global settling cannot be performed by anyone other than the\n *  issuer and only if the global settle bit is set.\n */\nBOOST_AUTO_TEST_CASE( global_settle_test )\n{ try {\n   uint32_t skip = database::skip_witness_signature\n                 | database::skip_transaction_signatures\n                 | database::skip_transaction_dupe_check\n                 | database::skip_block_size_check\n                 | database::skip_tapos_check\n                 | database::skip_merkle_check\n                 ;\n\n   generate_block( skip );\n\n  for( int i=0; i<2; i++ )\n  {\n   if( i == 1 )\n   {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_342_TIME - mi, true, skip);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n   }\n   set_expiration( db, trx );\n\n   ACTORS((nathan)(ben)(valentine)(dan));\n   asset_id_type bit_usd_id = create_bitasset(\"USDBIT\", nathan_id, 100, global_settle | charge_market_fee).get_id();\n\n   update_feed_producers( bit_usd_id(db), { nathan_id } );\n\n   price_feed feed;\n   feed.settlement_price = price( asset( 1000, bit_usd_id ), asset( 500 ) );\n   feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n   feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n   publish_feed( bit_usd_id(db), nathan, feed );\n\n   transfer(committee_account, ben_id, asset(10000));\n   transfer(committee_account, valentine_id, asset(10000));\n   transfer(committee_account, dan_id, asset(10000));\n   borrow(ben, asset(1000, bit_usd_id), asset(1000));\n   BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 1000);\n   BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 9000);\n\n   create_sell_order(ben_id, asset(1000, bit_usd_id), asset(1000));\n   BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0);\n   BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 9000);\n\n   create_sell_order(valentine_id, asset(1000), asset(1000, bit_usd_id));\n   BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0);\n   BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10000);\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990);\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 9000);\n\n   borrow(valentine, asset(500, bit_usd_id), asset(600));\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 1490);\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 8400);\n\n   create_sell_order(valentine_id, asset(500, bit_usd_id), asset(600));\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990);\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 8400);\n\n   create_sell_order(dan_id, asset(600), asset(500, bit_usd_id));\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990);\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 9000);\n   BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0);\n   BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10000);\n   BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 495);\n   BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9400);\n\n   // add some collateral\n   borrow(ben, asset(0, bit_usd_id), asset(1000));\n   BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 9000);\n\n   {\n      asset_global_settle_operation op;\n      op.asset_to_settle = bit_usd_id;\n      op.issuer = nathan_id;\n      op.settle_price = ~price(asset(10), asset(11, bit_usd_id));\n      trx.clear();\n      trx.operations.push_back(op);\n      REQUIRE_THROW_WITH_VALUE(op, settle_price, ~price(asset(2001), asset(1000, bit_usd_id)));\n      REQUIRE_THROW_WITH_VALUE(op, asset_to_settle, asset_id_type());\n      REQUIRE_THROW_WITH_VALUE(op, asset_to_settle, asset_id_type(100));\n      REQUIRE_THROW_WITH_VALUE(op, issuer, account_id_type(2));\n      trx.operations.back() = op;\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx );\n   }\n\n   force_settle(valentine_id(db), asset(990, bit_usd_id));\n   force_settle(dan_id(db), asset(495, bit_usd_id));\n\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 0);\n   BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 10045);\n   BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0);\n   if( i == 1 ) // BSIP35: better rounding\n   {\n      BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10090);\n      BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9850);\n   }\n   else\n   {\n      BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10091);\n      BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9849);\n   }\n   BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 0);\n\n   // undo above tx's and reset\n   generate_block( skip );\n   db.pop_block();\n  }\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( worker_create_test )\n{ try {\n   ACTOR(nathan);\n   upgrade_to_lifetime_member(nathan_id);\n   generate_block();\n\n   {\n      worker_create_operation op;\n      op.owner = nathan_id;\n      op.daily_pay = 1000;\n      op.initializer = vesting_balance_worker_initializer(1);\n      op.work_begin_date = db.head_block_time() + 10;\n      op.work_end_date = op.work_begin_date + fc::days(2);\n      trx.clear();\n      trx.operations.push_back(op);\n      REQUIRE_THROW_WITH_VALUE(op, daily_pay, -1);\n      REQUIRE_THROW_WITH_VALUE(op, daily_pay, 0);\n      REQUIRE_THROW_WITH_VALUE(op, owner, account_id_type(1000));\n      REQUIRE_THROW_WITH_VALUE(op, work_begin_date, db.head_block_time() - 10);\n      REQUIRE_THROW_WITH_VALUE(op, work_end_date, op.work_begin_date);\n      trx.operations.back() = op;\n      sign( trx, nathan_private_key );\n      PUSH_TX( db, trx );\n   }\n\n   const worker_object& worker = worker_id_type()(db);\n   BOOST_CHECK(worker.worker_account == nathan_id);\n   BOOST_CHECK(worker.daily_pay == 1000);\n   BOOST_CHECK(worker.work_begin_date == db.head_block_time() + 10);\n   BOOST_CHECK(worker.work_end_date == db.head_block_time() + 10 + fc::days(2));\n   BOOST_CHECK(worker.vote_for.type() == vote_id_type::worker);\n   BOOST_CHECK(worker.vote_against.type() == vote_id_type::worker);\n\n   const vesting_balance_object& balance = worker.worker.get<vesting_balance_worker_type>().balance(db);\n   BOOST_CHECK(balance.owner == nathan_id);\n   BOOST_CHECK(balance.balance == asset(0));\n   BOOST_CHECK(balance.policy.get<cdd_vesting_policy>().vesting_seconds == fc::days(1).to_seconds());\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( worker_pay_test )\n{ try {\n   INVOKE(worker_create_test);\n   GET_ACTOR(nathan);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   transfer(committee_account, nathan_id, asset(100000));\n\n   {\n      account_update_operation op;\n      op.account = nathan_id;\n      op.new_options = nathan_id(db).options;\n      op.new_options->votes.insert(worker_id_type()(db).vote_for);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n   {\n      asset_reserve_operation op;\n      op.payer = account_id_type();\n      op.amount_to_reserve = asset(GRAPHENE_MAX_SHARE_SUPPLY/2);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance(db).balance.amount.value, 0);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance(db).balance.amount.value, 1000);\n   generate_blocks(db.head_block_time() + fc::hours(12));\n\n   {\n      vesting_balance_withdraw_operation op;\n      op.vesting_balance = worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance;\n      op.amount = asset(500);\n      op.owner = nathan_id;\n      set_expiration( db, trx );\n      trx.operations.push_back(op);\n      sign( trx,  nathan_private_key );\n      PUSH_TX( db, trx );\n      trx.clear_signatures();\n      REQUIRE_THROW_WITH_VALUE(op, amount, asset(1));\n      trx.clear();\n   }\n\n   BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 100500);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance(db).balance.amount.value, 500);\n\n   {\n      account_update_operation op;\n      op.account = nathan_id;\n      op.new_options = nathan_id(db).options;\n      op.new_options->votes.erase(worker_id_type()(db).vote_for);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n\n   generate_blocks(db.head_block_time() + fc::hours(12));\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance(db).balance.amount.value, 500);\n\n   {\n      vesting_balance_withdraw_operation op;\n      op.vesting_balance = worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance;\n      op.amount = asset(500);\n      op.owner = nathan_id;\n      set_expiration( db, trx );\n      trx.operations.push_back(op);\n      REQUIRE_THROW_WITH_VALUE(op, amount, asset(500));\n      generate_blocks(db.head_block_time() + fc::hours(12));\n      set_expiration( db, trx );\n      REQUIRE_THROW_WITH_VALUE(op, amount, asset(501));\n      trx.operations.back() = op;\n      sign( trx,  nathan_private_key );\n      PUSH_TX( db, trx );\n      trx.clear_signatures();\n      trx.clear();\n   }\n\n   BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 101000);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<vesting_balance_worker_type>().balance(db).balance.amount.value, 0);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( refund_worker_test )\n{try{\n   ACTOR(nathan);\n   upgrade_to_lifetime_member(nathan_id);\n   generate_block();\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   set_expiration( db, trx );\n\n   {\n      worker_create_operation op;\n      op.owner = nathan_id;\n      op.daily_pay = 1000;\n      op.initializer = refund_worker_initializer();\n      op.work_begin_date = db.head_block_time() + 10;\n      op.work_end_date = op.work_begin_date + fc::days(2);\n      trx.clear();\n      trx.operations.push_back(op);\n      REQUIRE_THROW_WITH_VALUE(op, daily_pay, -1);\n      REQUIRE_THROW_WITH_VALUE(op, daily_pay, 0);\n      REQUIRE_THROW_WITH_VALUE(op, owner, account_id_type(1000));\n      REQUIRE_THROW_WITH_VALUE(op, work_begin_date, db.head_block_time() - 10);\n      REQUIRE_THROW_WITH_VALUE(op, work_end_date, op.work_begin_date);\n      trx.operations.back() = op;\n      sign( trx,  nathan_private_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   const worker_object& worker = worker_id_type()(db);\n   BOOST_CHECK(worker.worker_account == nathan_id);\n   BOOST_CHECK(worker.daily_pay == 1000);\n   BOOST_CHECK(worker.work_begin_date == db.head_block_time() + 10);\n   BOOST_CHECK(worker.work_end_date == db.head_block_time() + 10 + fc::days(2));\n   BOOST_CHECK(worker.vote_for.type() == vote_id_type::worker);\n   BOOST_CHECK(worker.vote_against.type() == vote_id_type::worker);\n\n   transfer(committee_account, nathan_id, asset(100000));\n\n   {\n      account_update_operation op;\n      op.account = nathan_id;\n      op.new_options = nathan_id(db).options;\n      op.new_options->votes.insert(worker_id_type()(db).vote_for);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n   {\n      asset_reserve_operation op;\n      op.payer = account_id_type();\n      op.amount_to_reserve = asset(GRAPHENE_MAX_SHARE_SUPPLY/2);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n\n   // auto supply = asset_id_type()(db).dynamic_data(db).current_supply;\n   verify_asset_supplies(db);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   verify_asset_supplies(db);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<refund_worker_type>().total_burned.value, 1000);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   verify_asset_supplies(db);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<refund_worker_type>().total_burned.value, 2000);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   BOOST_CHECK(!db.get(worker_id_type()).is_active(db.head_block_time()));\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<refund_worker_type>().total_burned.value, 2000);\n}FC_LOG_AND_RETHROW()}\n\n/**\n * Create a burn worker, vote it in, make sure funds are destroyed.\n */\n\nBOOST_AUTO_TEST_CASE( burn_worker_test )\n{try{\n   ACTOR(nathan);\n   upgrade_to_lifetime_member(nathan_id);\n   generate_block();\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   set_expiration( db, trx );\n\n   {\n      worker_create_operation op;\n      op.owner = nathan_id;\n      op.daily_pay = 1000;\n      op.initializer = burn_worker_initializer();\n      op.work_begin_date = db.head_block_time() + 10;\n      op.work_end_date = op.work_begin_date + fc::days(2);\n      trx.clear();\n      trx.operations.push_back(op);\n      REQUIRE_THROW_WITH_VALUE(op, daily_pay, -1);\n      REQUIRE_THROW_WITH_VALUE(op, daily_pay, 0);\n      REQUIRE_THROW_WITH_VALUE(op, owner, account_id_type(1000));\n      REQUIRE_THROW_WITH_VALUE(op, work_begin_date, db.head_block_time() - 10);\n      REQUIRE_THROW_WITH_VALUE(op, work_end_date, op.work_begin_date);\n      trx.operations.back() = op;\n      sign( trx,  nathan_private_key );\n      PUSH_TX( db, trx );\n      trx.clear();\n   }\n\n   const worker_object& worker = worker_id_type()(db);\n   BOOST_CHECK(worker.worker_account == nathan_id);\n   BOOST_CHECK(worker.daily_pay == 1000);\n   BOOST_CHECK(worker.work_begin_date == db.head_block_time() + 10);\n   BOOST_CHECK(worker.work_end_date == db.head_block_time() + 10 + fc::days(2));\n   BOOST_CHECK(worker.vote_for.type() == vote_id_type::worker);\n   BOOST_CHECK(worker.vote_against.type() == vote_id_type::worker);\n\n   transfer(committee_account, nathan_id, asset(100000));\n\n   {\n      account_update_operation op;\n      op.account = nathan_id;\n      op.new_options = nathan_id(db).options;\n      op.new_options->votes.insert(worker_id_type()(db).vote_for);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n   {\n      // refund some asset to fill up the pool\n      asset_reserve_operation op;\n      op.payer = account_id_type();\n      op.amount_to_reserve = asset(GRAPHENE_MAX_SHARE_SUPPLY/2);\n      trx.operations.push_back(op);\n      PUSH_TX( db, trx, ~0 );\n      trx.clear();\n   }\n\n   BOOST_CHECK_EQUAL( get_balance(GRAPHENE_NULL_ACCOUNT, asset_id_type()), 0 );\n   verify_asset_supplies(db);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   verify_asset_supplies(db);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<burn_worker_type>().total_burned.value, 1000);\n   BOOST_CHECK_EQUAL( get_balance(GRAPHENE_NULL_ACCOUNT, asset_id_type()), 1000 );\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   verify_asset_supplies(db);\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<burn_worker_type>().total_burned.value, 2000);\n   BOOST_CHECK_EQUAL( get_balance(GRAPHENE_NULL_ACCOUNT, asset_id_type()), 2000 );\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   BOOST_CHECK(!db.get(worker_id_type()).is_active(db.head_block_time()));\n   BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get<burn_worker_type>().total_burned.value, 2000);\n   BOOST_CHECK_EQUAL( get_balance(GRAPHENE_NULL_ACCOUNT, asset_id_type()), 2000 );\n}FC_LOG_AND_RETHROW()}\n\nBOOST_AUTO_TEST_CASE( force_settle_test )\n{\n   uint32_t skip = database::skip_witness_signature\n                 | database::skip_transaction_signatures\n                 | database::skip_transaction_dupe_check\n                 | database::skip_block_size_check\n                 | database::skip_tapos_check\n                 | database::skip_merkle_check\n                 ;\n\n   generate_block( skip );\n\n  for( int i=0; i<2; i++ )\n  {\n   if( i == 1 )\n   {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_342_TIME - mi, true, skip);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip);\n   }\n   set_expiration( db, trx );\n\n   int blocks = 0;\n   try\n   {\n      ACTORS( (nathan)(shorter1)(shorter2)(shorter3)(shorter4)(shorter5) );\n\n      int64_t initial_balance = 100000000;\n\n      transfer(account_id_type()(db), shorter1_id(db), asset(initial_balance));\n      transfer(account_id_type()(db), shorter2_id(db), asset(initial_balance));\n      transfer(account_id_type()(db), shorter3_id(db), asset(initial_balance));\n      transfer(account_id_type()(db), shorter4_id(db), asset(initial_balance));\n      transfer(account_id_type()(db), shorter5_id(db), asset(initial_balance));\n\n      asset_id_type bitusd_id = create_bitasset(\n         \"USDBIT\",\n         nathan_id,\n         100,\n         disable_force_settle\n         ).id;\n\n      asset_id_type core_id = asset_id_type();\n\n      auto update_bitasset_options = [&]( asset_id_type asset_id,\n         std::function< void(bitasset_options&) > update_function )\n      {\n         const asset_object& _asset = asset_id(db);\n         asset_update_bitasset_operation op;\n         op.asset_to_update = asset_id;\n         op.issuer = _asset.issuer;\n         op.new_options = (*_asset.bitasset_data_id)(db).options;\n         update_function( op.new_options );\n         signed_transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, ~0 );\n      } ;\n\n      auto update_asset_options = [&]( asset_id_type asset_id,\n         std::function< void(asset_options&) > update_function )\n      {\n         const asset_object& _asset = asset_id(db);\n         asset_update_operation op;\n         op.asset_to_update = asset_id;\n         op.issuer = _asset.issuer;\n         op.new_options = _asset.options;\n         update_function( op.new_options );\n         signed_transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, ~0 );\n      } ;\n\n      BOOST_TEST_MESSAGE( \"Update maximum_force_settlement_volume = 9000\" );\n\n      BOOST_CHECK( bitusd_id(db).is_market_issued() );\n      update_bitasset_options( bitusd_id, [&]( bitasset_options& new_options )\n      { new_options.maximum_force_settlement_volume = 9000; } );\n\n      BOOST_TEST_MESSAGE( \"Publish price feed\" );\n\n      update_feed_producers( bitusd_id, { nathan_id } );\n      {\n         price_feed feed;\n         feed.settlement_price = price( asset( 1, bitusd_id ), asset( 1, core_id ) );\n         publish_feed( bitusd_id, nathan_id, feed );\n      }\n\n      BOOST_TEST_MESSAGE( \"First short batch\" );\n\n      call_order_id_type call1_id = borrow( shorter1_id, asset(1000, bitusd_id), asset(2*1000, core_id) )->id;   // 2.0000\n      call_order_id_type call2_id = borrow( shorter2_id, asset(2000, bitusd_id), asset(2*1999, core_id) )->id;   // 1.9990\n      call_order_id_type call3_id = borrow( shorter3_id, asset(3000, bitusd_id), asset(2*2890, core_id) )->id;   // 1.9267\n      call_order_id_type call4_id = borrow( shorter4_id, asset(4000, bitusd_id), asset(2*3950, core_id) )->id;   // 1.9750\n      call_order_id_type call5_id = borrow( shorter5_id, asset(5000, bitusd_id), asset(2*4900, core_id) )->id;   // 1.9600\n\n      transfer( shorter1_id, nathan_id, asset(1000, bitusd_id) );\n      transfer( shorter2_id, nathan_id, asset(2000, bitusd_id) );\n      transfer( shorter3_id, nathan_id, asset(3000, bitusd_id) );\n      transfer( shorter4_id, nathan_id, asset(4000, bitusd_id) );\n      transfer( shorter5_id, nathan_id, asset(5000, bitusd_id) );\n\n      BOOST_CHECK_EQUAL( get_balance(nathan_id, bitusd_id), 15000);\n      BOOST_CHECK_EQUAL( get_balance(nathan_id, core_id), 0);\n      BOOST_CHECK_EQUAL( get_balance(shorter1_id, core_id), initial_balance-2000 );\n      BOOST_CHECK_EQUAL( get_balance(shorter2_id, core_id), initial_balance-3998 );\n      BOOST_CHECK_EQUAL( get_balance(shorter3_id, core_id), initial_balance-5780 );\n      BOOST_CHECK_EQUAL( get_balance(shorter4_id, core_id), initial_balance-7900 );\n      BOOST_CHECK_EQUAL( get_balance(shorter5_id, core_id), initial_balance-9800 );\n\n      BOOST_TEST_MESSAGE( \"Update force_settlement_delay_sec = 100, force_settlement_offset_percent = 1%\" );\n\n      update_bitasset_options( bitusd_id, [&]( bitasset_options& new_options )\n      { new_options.force_settlement_delay_sec = 100;\n        new_options.force_settlement_offset_percent = GRAPHENE_1_PERCENT; } );\n\n      // Force settlement is disabled; check that it fails\n      GRAPHENE_REQUIRE_THROW( force_settle( nathan_id, asset( 50, bitusd_id ) ), fc::exception );\n\n      update_asset_options( bitusd_id, [&]( asset_options& new_options )\n      { new_options.flags &= ~disable_force_settle; } );\n\n      // Can't settle more BitUSD than you own\n      GRAPHENE_REQUIRE_THROW( force_settle( nathan_id, asset( 999999, bitusd_id ) ), fc::exception );\n\n      // settle3 should be least collateralized order according to index\n      BOOST_CHECK( db.get_index_type<call_order_index>().indices().get<by_collateral>().begin()->id == call3_id );\n      BOOST_CHECK_EQUAL( call3_id(db).debt.value, 3000 );\n\n      BOOST_TEST_MESSAGE( \"Verify partial settlement of call\" );\n      // Partially settle a call\n      force_settlement_id_type settle_id = force_settle( nathan_id, asset( 50, bitusd_id ) ).get< object_id_type >();\n\n      // Call does not take effect immediately\n      BOOST_CHECK_EQUAL( get_balance(nathan_id, bitusd_id), 14950);\n      BOOST_CHECK_EQUAL( settle_id(db).balance.amount.value, 50);\n      BOOST_CHECK_EQUAL( call3_id(db).debt.value, 3000 );\n      BOOST_CHECK_EQUAL( call3_id(db).collateral.value, 5780 );\n      BOOST_CHECK( settle_id(db).owner == nathan_id );\n\n      // Wait for settlement to take effect\n      generate_blocks( settle_id(db).settlement_date, true, skip );\n      blocks += 2;\n\n      BOOST_CHECK(db.find(settle_id) == nullptr);\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 50 );\n      BOOST_CHECK_EQUAL( get_balance(nathan_id, bitusd_id), 14950);\n      BOOST_CHECK_EQUAL( get_balance(nathan_id, core_id), 49 );   // 1% force_settlement_offset_percent (rounded unfavorably)\n      BOOST_CHECK_EQUAL( call3_id(db).debt.value, 2950 );\n      BOOST_CHECK_EQUAL( call3_id(db).collateral.value, 5731 );  // 5731 == 5780-49\n\n      BOOST_CHECK( db.get_index_type<call_order_index>().indices().get<by_collateral>().begin()->id == call3_id );\n\n      BOOST_TEST_MESSAGE( \"Verify pending settlement is cancelled when asset's force_settle is disabled\" );\n      // Ensure pending settlement is cancelled when force settle is disabled\n      settle_id = force_settle( nathan_id, asset( 50, bitusd_id ) ).get< object_id_type >();\n\n      BOOST_CHECK( !db.get_index_type<force_settlement_index>().indices().empty() );\n      update_asset_options( bitusd_id, [&]( asset_options& new_options )\n      { new_options.flags |= disable_force_settle; } );\n      BOOST_CHECK(  db.get_index_type<force_settlement_index>().indices().empty() );\n      update_asset_options( bitusd_id, [&]( asset_options& new_options )\n      { new_options.flags &= ~disable_force_settle; } );\n\n      BOOST_TEST_MESSAGE( \"Perform iterative settlement\" );\n      settle_id = force_settle( nathan_id, asset( 12500, bitusd_id ) ).get< object_id_type >();\n\n      // c3 2950 : 5731   1.9427   fully settled\n      // c5 5000 : 9800   1.9600   fully settled\n      // c4 4000 : 7900   1.9750   fully settled\n      // c2 2000 : 3998   1.9990   550 settled\n      // c1 1000 : 2000   2.0000\n\n      generate_blocks( settle_id(db).settlement_date, true, skip );\n      blocks += 2;\n\n      int64_t call1_payout =                0;\n      int64_t call2_payout =       550*99/100;\n      int64_t call3_payout = 49 + 2950*99/100;\n      int64_t call4_payout =      4000*99/100;\n      int64_t call5_payout =      5000*99/100;\n\n      if( i == 1 ) // BSIP35: better rounding\n      {\n         call3_payout = 49 + (2950*99+100-1)/100; // round up\n         call4_payout =      (4000*99+100-1)/100; // round up\n         call5_payout =      (5000*99+100-1)/100; // round up\n      }\n\n      BOOST_CHECK_EQUAL( get_balance(shorter1_id, core_id), initial_balance-2*1000 );  // full collat still tied up\n      BOOST_CHECK_EQUAL( get_balance(shorter2_id, core_id), initial_balance-2*1999 );  // full collat still tied up\n      BOOST_CHECK_EQUAL( get_balance(shorter3_id, core_id), initial_balance-call3_payout );  // initial balance minus transfer to Nathan (as BitUSD)\n      BOOST_CHECK_EQUAL( get_balance(shorter4_id, core_id), initial_balance-call4_payout );  // initial balance minus transfer to Nathan (as BitUSD)\n      BOOST_CHECK_EQUAL( get_balance(shorter5_id, core_id), initial_balance-call5_payout );  // initial balance minus transfer to Nathan (as BitUSD)\n\n      BOOST_CHECK_EQUAL( get_balance(nathan_id, core_id),\n           call1_payout + call2_payout + call3_payout + call4_payout + call5_payout );\n\n      BOOST_CHECK( db.find(call3_id) == nullptr );\n      BOOST_CHECK( db.find(call4_id) == nullptr );\n      BOOST_CHECK( db.find(call5_id) == nullptr );\n\n      BOOST_REQUIRE( db.find(call1_id) != nullptr );\n      BOOST_REQUIRE( db.find(call2_id) != nullptr );\n\n      BOOST_CHECK_EQUAL( call1_id(db).debt.value, 1000 );\n      BOOST_CHECK_EQUAL( call1_id(db).collateral.value, 2000 );\n\n      BOOST_CHECK_EQUAL( call2_id(db).debt.value, 2000-550 );\n      BOOST_CHECK_EQUAL( call2_id(db).collateral.value, 3998-call2_payout );\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n\n   // undo above tx's and reset\n   generate_block( skip );\n   ++blocks;\n   while( blocks > 0 )\n   {\n      db.pop_block();\n      --blocks;\n   }\n  }\n}\n\nBOOST_AUTO_TEST_CASE( assert_op_test )\n{\n   try {\n   // create some objects\n   auto nathan_private_key = generate_private_key(\"nathan\");\n   public_key_type nathan_public_key = nathan_private_key.get_public_key();\n   account_id_type nathan_id = create_account(\"nathan\", nathan_public_key).id;\n\n   assert_operation op;\n\n   // nathan checks that his public key is equal to the given value.\n   op.fee_paying_account = nathan_id;\n   op.predicates.emplace_back(account_name_eq_lit_predicate{ nathan_id, \"nathan\" });\n   trx.operations.push_back(op);\n   sign( trx, nathan_private_key );\n   PUSH_TX( db, trx );\n\n   // nathan checks that his public key is not equal to the given value (fail)\n   trx.clear();\n   op.predicates.emplace_back(account_name_eq_lit_predicate{ nathan_id, \"dan\" });\n   trx.operations.push_back(op);\n   sign( trx, nathan_private_key );\n   GRAPHENE_CHECK_THROW( PUSH_TX( db, trx ), fc::exception );\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( balance_object_test )\n{ try {\n   // Intentionally overriding the fixture's db; I need to control genesis on this one.\n   database db;\n   const uint32_t skip_flags = database::skip_undo_history_check;\n   fc::temp_directory td( graphene::utilities::temp_directory_path() );\n   genesis_state.initial_balances.push_back({generate_private_key(\"n\").get_public_key(), GRAPHENE_SYMBOL, 1});\n   genesis_state.initial_balances.push_back({generate_private_key(\"x\").get_public_key(), GRAPHENE_SYMBOL, 1});\n   fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000;\n\n   auto n_key = generate_private_key(\"n\");\n   auto x_key = generate_private_key(\"x\");\n   auto v1_key = generate_private_key(\"v1\");\n   auto v2_key = generate_private_key(\"v2\");\n\n   genesis_state_type::initial_vesting_balance_type vest;\n   vest.owner = v1_key.get_public_key();\n   vest.asset_symbol = GRAPHENE_SYMBOL;\n   vest.amount = 500;\n   vest.begin_balance = vest.amount;\n   vest.begin_timestamp = starting_time;\n   vest.vesting_duration_seconds = 60;\n   genesis_state.initial_vesting_balances.push_back(vest);\n   vest.owner = v2_key.get_public_key();\n   vest.begin_timestamp -= fc::seconds(30);\n   vest.amount = 400;\n   genesis_state.initial_vesting_balances.push_back(vest);\n\n   genesis_state.initial_accounts.emplace_back(\"n\", n_key.get_public_key());\n\n   auto _sign = [&]( signed_transaction& tx, const private_key_type& key )\n   {  tx.sign( key, db.get_chain_id() );   };\n\n   db.open(td.path(), [this]{return genesis_state;}, \"TEST\");\n   const balance_object& balance = balance_id_type()(db);\n   BOOST_CHECK_EQUAL(balance.balance.amount.value, 1);\n   BOOST_CHECK_EQUAL(balance_id_type(1)(db).balance.amount.value, 1);\n\n   balance_claim_operation op;\n   op.deposit_to_account = db.get_index_type<account_index>().indices().get<by_name>().find(\"n\")->get_id();\n   op.total_claimed = asset(1);\n   op.balance_to_claim = balance_id_type(1);\n   op.balance_owner_key = x_key.get_public_key();\n   trx.operations = {op};\n   _sign( trx, n_key );\n   // Fail because I'm claiming from an address which hasn't signed\n   GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), tx_missing_other_auth);\n   trx.clear();\n   op.balance_to_claim = balance_id_type();\n   op.balance_owner_key = n_key.get_public_key();\n   trx.operations = {op};\n   _sign( trx, n_key );\n   PUSH_TX(db, trx);\n\n   // Not using fixture's get_balance() here because it uses fixture's db, not my override\n   BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 1);\n   BOOST_CHECK(db.find_object(balance_id_type()) == nullptr);\n   BOOST_CHECK(db.find_object(balance_id_type(1)) != nullptr);\n\n   auto slot = db.get_slot_at_time(starting_time);\n   db.generate_block(starting_time, db.get_scheduled_witness(slot), init_account_priv_key, skip_flags);\n   set_expiration( db, trx );\n\n   const balance_object& vesting_balance_1 = balance_id_type(2)(db);\n   const balance_object& vesting_balance_2 = balance_id_type(3)(db);\n   BOOST_CHECK(vesting_balance_1.is_vesting_balance());\n   BOOST_CHECK_EQUAL(vesting_balance_1.balance.amount.value, 500);\n   BOOST_CHECK_EQUAL(vesting_balance_1.available(db.head_block_time()).amount.value, 0);\n   BOOST_CHECK(vesting_balance_2.is_vesting_balance());\n   BOOST_CHECK_EQUAL(vesting_balance_2.balance.amount.value, 400);\n   BOOST_CHECK_EQUAL(vesting_balance_2.available(db.head_block_time()).amount.value, 150);\n\n   op.balance_to_claim = vesting_balance_1.id;\n   op.total_claimed = asset(1);\n   op.balance_owner_key = v1_key.get_public_key();\n   trx.clear();\n   trx.operations = {op};\n   _sign( trx, n_key );\n   _sign( trx, v1_key );\n   // Attempting to claim 1 from a balance with 0 available\n   GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_invalid_claim_amount);\n\n   op.balance_to_claim = vesting_balance_2.id;\n   op.total_claimed.amount = 151;\n   op.balance_owner_key = v2_key.get_public_key();\n   trx.operations = {op};\n   trx.clear_signatures();\n   _sign( trx, n_key );\n   _sign( trx, v2_key );\n   // Attempting to claim 151 from a balance with 150 available\n   GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_invalid_claim_amount);\n\n   op.balance_to_claim = vesting_balance_2.id;\n   op.total_claimed.amount = 100;\n   op.balance_owner_key = v2_key.get_public_key();\n   trx.operations = {op};\n   trx.clear_signatures();\n   _sign( trx, n_key );\n   _sign( trx, v2_key );\n   PUSH_TX(db, trx);\n   BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 101);\n   BOOST_CHECK_EQUAL(vesting_balance_2.balance.amount.value, 300);\n\n   op.total_claimed.amount = 10;\n   trx.operations = {op};\n   trx.clear_signatures();\n   _sign( trx, n_key );\n   _sign( trx, v2_key );\n   // Attempting to claim twice within a day\n   GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_claimed_too_often);\n\n   db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, skip_flags);\n   slot = db.get_slot_at_time(vesting_balance_1.vesting_policy->begin_timestamp + 60);\n   db.generate_block(db.get_slot_time(slot), db.get_scheduled_witness(slot), init_account_priv_key, skip_flags);\n   set_expiration( db, trx );\n\n   op.balance_to_claim = vesting_balance_1.id;\n   op.total_claimed.amount = 500;\n   op.balance_owner_key = v1_key.get_public_key();\n   trx.operations = {op};\n   trx.clear_signatures();\n   _sign( trx, n_key );\n   _sign( trx, v1_key );\n   PUSH_TX(db, trx);\n   BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr);\n   BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 601);\n\n   op.balance_to_claim = vesting_balance_2.id;\n   op.balance_owner_key = v2_key.get_public_key();\n   op.total_claimed.amount = 10;\n   trx.operations = {op};\n   trx.clear_signatures();\n   _sign( trx, n_key );\n   _sign( trx, v2_key );\n   // Attempting to claim twice within a day\n   GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_claimed_too_often);\n\n   db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, skip_flags);\n   slot = db.get_slot_at_time(db.head_block_time() + fc::days(1));\n   db.generate_block(db.get_slot_time(slot), db.get_scheduled_witness(slot), init_account_priv_key, skip_flags);\n   set_expiration( db, trx );\n\n   op.total_claimed = vesting_balance_2.balance;\n   trx.operations = {op};\n   trx.clear_signatures();\n   _sign( trx, n_key );\n   _sign( trx, v2_key );\n   PUSH_TX(db, trx);\n   BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr);\n   BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 901);\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(transfer_with_memo) {\n   try {\n      ACTOR(alice);\n      ACTOR(bob);\n      transfer(account_id_type(), alice_id, asset(1000));\n      BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 1000);\n\n      transfer_operation op;\n      op.from = alice_id;\n      op.to = bob_id;\n      op.amount = asset(500);\n      op.memo = memo_data();\n      op.memo->set_message(alice_private_key, bob_public_key, \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n      trx.operations = {op};\n      trx.sign(alice_private_key, db.get_chain_id());\n      PUSH_TX(db, trx);\n\n      BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 500);\n      BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 500);\n\n      auto memo = db.get_recent_transaction(trx.id()).operations.front().get<transfer_operation>().memo;\n      BOOST_CHECK(memo);\n      BOOST_CHECK_EQUAL(memo->get_message(bob_private_key, alice_public_key), \"Dear Bob,\\n\\nMoney!\\n\\nLove, Alice\");\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(zero_second_vbo)\n{\n   try\n   {\n      ACTOR(alice);\n      // don't pay witnesses so we have some worker budget to work with\n\n      transfer(account_id_type(), alice_id, asset(int64_t(100000) * 1100 * 1000 * 1000));\n      {\n         asset_reserve_operation op;\n         op.payer = alice_id;\n         op.amount_to_reserve = asset(int64_t(100000) * 1000 * 1000 * 1000);\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      }\n      enable_fees();\n      upgrade_to_lifetime_member(alice_id);\n      generate_block();\n\n      // Wait for a maintenance interval to ensure we have a full day's budget to work with.\n      // Otherwise we may not have enough to feed the witnesses and the worker will end up starved if we start late in the day.\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      auto check_vesting_1b = [&](vesting_balance_id_type vbid)\n      {\n         // this function checks that Alice can't draw any right now,\n         // but one block later, she can withdraw it all.\n\n         vesting_balance_withdraw_operation withdraw_op;\n         withdraw_op.vesting_balance = vbid;\n         withdraw_op.owner = alice_id;\n         withdraw_op.amount = asset(1);\n\n         signed_transaction withdraw_tx;\n         withdraw_tx.operations.push_back( withdraw_op );\n         sign(withdraw_tx, alice_private_key);\n         GRAPHENE_REQUIRE_THROW( PUSH_TX( db, withdraw_tx ), fc::exception );\n\n         generate_block();\n         withdraw_tx = signed_transaction();\n         withdraw_op.amount = asset(500);\n         withdraw_tx.operations.push_back( withdraw_op );\n         set_expiration( db, withdraw_tx );\n         sign(withdraw_tx, alice_private_key);\n         PUSH_TX( db, withdraw_tx );\n      };\n\n      // This block creates a zero-second VBO with a vesting_balance_create_operation.\n      {\n         cdd_vesting_policy_initializer pinit;\n         pinit.vesting_seconds = 0;\n\n         vesting_balance_create_operation create_op;\n         create_op.creator = alice_id;\n         create_op.owner = alice_id;\n         create_op.amount = asset(500);\n         create_op.policy = pinit;\n\n         signed_transaction create_tx;\n         create_tx.operations.push_back( create_op );\n         set_expiration( db, create_tx );\n         sign(create_tx, alice_private_key);\n\n         processed_transaction ptx = PUSH_TX( db, create_tx );\n         vesting_balance_id_type vbid = ptx.operation_results[0].get<object_id_type>();\n         check_vesting_1b( vbid );\n      }\n\n      // This block creates a zero-second VBO with a worker_create_operation.\n      {\n         worker_create_operation create_op;\n         create_op.owner = alice_id;\n         create_op.work_begin_date = db.head_block_time();\n         create_op.work_end_date = db.head_block_time() + fc::days(1000);\n         create_op.daily_pay = share_type( 10000 );\n         create_op.name = \"alice\";\n         create_op.url = \"\";\n         create_op.initializer = vesting_balance_worker_initializer(0);\n         signed_transaction create_tx;\n         create_tx.operations.push_back(create_op);\n         set_expiration( db, create_tx );\n         sign(create_tx, alice_private_key);\n         processed_transaction ptx = PUSH_TX( db, create_tx );\n         worker_id_type wid = ptx.operation_results[0].get<object_id_type>();\n\n         // vote it in\n         account_update_operation vote_op;\n         vote_op.account = alice_id;\n         vote_op.new_options = alice_id(db).options;\n         vote_op.new_options->votes.insert(wid(db).vote_for);\n         signed_transaction vote_tx;\n         vote_tx.operations.push_back(vote_op);\n         set_expiration( db, vote_tx );\n         sign( vote_tx, alice_private_key );\n         PUSH_TX( db, vote_tx );\n\n         // vote it in, wait for one maint. for vote to take effect\n         vesting_balance_id_type vbid = wid(db).worker.get<vesting_balance_worker_type>().balance;\n         // wait for another maint. for worker to be paid\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(0) );\n         generate_block();\n         BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(10000) );\n\n         /*\n         db.get_index_type< simple_index<budget_record_object> >().inspect_all_objects(\n            [&](const object& o)\n            {\n               ilog( \"budget: ${brec}\", (\"brec\", static_cast<const budget_record_object&>(o)) );\n            });\n         */\n      }\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( vbo_withdraw_different )\n{\n   try\n   {\n      ACTORS((alice)(izzy));\n      // don't pay witnesses so we have some worker budget to work with\n\n      // transfer(account_id_type(), alice_id, asset(1000));\n\n      asset_id_type stuff_id = create_user_issued_asset( \"STUFF\", izzy_id(db), 0 ).id;\n      issue_uia( alice_id, asset( 1000, stuff_id ) );\n\n      // deposit STUFF with linear vesting policy\n      vesting_balance_id_type vbid;\n      {\n         linear_vesting_policy_initializer pinit;\n         pinit.begin_timestamp = db.head_block_time();\n         pinit.vesting_cliff_seconds    = 30;\n         pinit.vesting_duration_seconds = 30;\n\n         vesting_balance_create_operation create_op;\n         create_op.creator = alice_id;\n         create_op.owner = alice_id;\n         create_op.amount = asset(100, stuff_id);\n         create_op.policy = pinit;\n\n         signed_transaction create_tx;\n         create_tx.operations.push_back( create_op );\n         set_expiration( db, create_tx );\n         sign(create_tx, alice_private_key);\n\n         processed_transaction ptx = PUSH_TX( db, create_tx );\n         vbid = ptx.operation_results[0].get<object_id_type>();\n      }\n\n      // wait for VB to mature\n      generate_blocks( 30 );\n\n      BOOST_CHECK( vbid(db).get_allowed_withdraw( db.head_block_time() ) == asset(100, stuff_id) );\n\n      // bad withdrawal op (wrong asset)\n      {\n         vesting_balance_withdraw_operation op;\n\n         op.vesting_balance = vbid;\n         op.amount = asset(100);\n         op.owner = alice_id;\n\n         signed_transaction withdraw_tx;\n         withdraw_tx.operations.push_back(op);\n         set_expiration( db, withdraw_tx );\n         sign( withdraw_tx, alice_private_key );\n         GRAPHENE_CHECK_THROW( PUSH_TX( db, withdraw_tx ), fc::exception );\n      }\n\n      // good withdrawal op\n      {\n         vesting_balance_withdraw_operation op;\n\n         op.vesting_balance = vbid;\n         op.amount = asset(100, stuff_id);\n         op.owner = alice_id;\n\n         signed_transaction withdraw_tx;\n         withdraw_tx.operations.push_back(op);\n         set_expiration( db, withdraw_tx );\n         sign( withdraw_tx, alice_private_key );\n         PUSH_TX( db, withdraw_tx );\n      }\n   }\n   FC_LOG_AND_RETHROW()\n}\n\n// TODO:  Write linear VBO tests\n\nBOOST_AUTO_TEST_CASE( top_n_special )\n{\n   ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) );\n\n   try\n   {\n      {\n         //\n         // Izzy (issuer)\n         // Stan (special authority)\n         // Alice, Bob, Chloe, Dan (ABCD)\n         //\n\n         asset_id_type topn_id = create_user_issued_asset( \"TOPN\", izzy_id(db), 0 ).id;\n         authority stan_owner_auth = stan_id(db).owner;\n         authority stan_active_auth = stan_id(db).active;\n\n         // set SA, wait for maint interval\n         // TODO:  account_create_operation\n         // TODO:  multiple accounts with different n for same asset\n\n         {\n            top_holders_special_authority top2, top3;\n\n            top2.num_top_holders = 2;\n            top2.asset = topn_id;\n\n            top3.num_top_holders = 3;\n            top3.asset = topn_id;\n\n            account_update_operation op;\n            op.account = stan_id;\n            op.extensions.value.active_special_authority = top3;\n            op.extensions.value.owner_special_authority = top2;\n\n            signed_transaction tx;\n            tx.operations.push_back( op );\n\n            set_expiration( db, tx );\n            sign( tx, stan_private_key );\n\n            PUSH_TX( db, tx );\n\n            // TODO:  Check special_authority is properly set\n            // TODO:  Do it in steps\n         }\n\n         // wait for maint interval\n         // make sure we don't have any authority as account hasn't gotten distributed yet\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         BOOST_CHECK( stan_id(db).owner  == stan_owner_auth );\n         BOOST_CHECK( stan_id(db).active == stan_active_auth );\n\n         // issue some to Alice, make sure she gets control of Stan\n\n         // we need to set_expiration() before issue_uia() because the latter doens't call it #11\n         set_expiration( db, trx );  // #11\n         issue_uia( alice_id, asset( 1000, topn_id ) );\n\n         BOOST_CHECK( stan_id(db).owner  == stan_owner_auth );\n         BOOST_CHECK( stan_id(db).active == stan_active_auth );\n\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         /*  NOTE - this was an old check from an earlier implementation that only allowed SA for LTM's\n         // no boost yet, we need to upgrade to LTM before mechanics apply to Stan\n         BOOST_CHECK( stan_id(db).owner  == stan_owner_auth );\n         BOOST_CHECK( stan_id(db).active == stan_active_auth );\n\n         set_expiration( db, trx );  // #11\n         upgrade_to_lifetime_member(stan_id);\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         */\n\n         BOOST_CHECK( stan_id(db).owner  == authority(  501, alice_id, 1000 ) );\n         BOOST_CHECK( stan_id(db).active == authority(  501, alice_id, 1000 ) );\n\n         // give asset to Stan, make sure owner doesn't change at all\n         set_expiration( db, trx );  // #11\n         transfer( alice_id, stan_id, asset( 1000, topn_id ) );\n\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         BOOST_CHECK( stan_id(db).owner  == authority(  501, alice_id, 1000 ) );\n         BOOST_CHECK( stan_id(db).active == authority(  501, alice_id, 1000 ) );\n\n         set_expiration( db, trx );  // #11\n         issue_uia( chloe_id, asset( 131000, topn_id ) );\n\n         // now Chloe has 131,000 and Stan has 1k.  Make sure change occurs at next maintenance interval.\n         // NB, 131072 is a power of 2; the number 131000 was chosen so that we need a bitshift, but\n         // if we put the 1000 from Stan's balance back into play, we need a different bitshift.\n\n         // we use Chloe so she can be displaced by Bob later (showing the tiebreaking logic).\n\n         // Check Alice is still in control, because we're deferred to next maintenance interval\n         BOOST_CHECK( stan_id(db).owner  == authority(  501, alice_id, 1000 ) );\n         BOOST_CHECK( stan_id(db).active == authority(  501, alice_id, 1000 ) );\n\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         BOOST_CHECK( stan_id(db).owner  == authority( 32751, chloe_id, 65500 ) );\n         BOOST_CHECK( stan_id(db).active == authority( 32751, chloe_id, 65500 ) );\n\n         // put Alice's stake back in play\n         set_expiration( db, trx );  // #11\n         transfer( stan_id, alice_id, asset( 1000, topn_id ) );\n\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         BOOST_CHECK( stan_id(db).owner  == authority( 33001, alice_id, 500, chloe_id, 65500 ) );\n         BOOST_CHECK( stan_id(db).active == authority( 33001, alice_id, 500, chloe_id, 65500 ) );\n\n         // issue 200,000 to Dan to cause another bitshift.\n         set_expiration( db, trx );  // #11\n         issue_uia( dan_id, asset( 200000, topn_id ) );\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         // 200000 Dan\n         // 131000 Chloe\n         // 1000 Alice\n\n         BOOST_CHECK( stan_id(db).owner  == authority( 41376,                chloe_id, 32750, dan_id, 50000 ) );\n         BOOST_CHECK( stan_id(db).active == authority( 41501, alice_id, 250, chloe_id, 32750, dan_id, 50000 ) );\n\n         // have Alice send all but 1 back to Stan, verify that we clamp Alice at one vote\n         set_expiration( db, trx );  // #11\n         transfer( alice_id, stan_id, asset( 999, topn_id ) );\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         BOOST_CHECK( stan_id(db).owner  == authority( 41376,                chloe_id, 32750, dan_id, 50000 ) );\n         BOOST_CHECK( stan_id(db).active == authority( 41376, alice_id,   1, chloe_id, 32750, dan_id, 50000 ) );\n\n         // send 131k to Bob so he's tied with Chloe, verify he displaces Chloe in top2\n         set_expiration( db, trx );  // #11\n         issue_uia( bob_id, asset( 131000, topn_id ) );\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         BOOST_CHECK( stan_id(db).owner  == authority( 41376, bob_id, 32750,                  dan_id, 50000 ) );\n         BOOST_CHECK( stan_id(db).active == authority( 57751, bob_id, 32750, chloe_id, 32750, dan_id, 50000 ) );\n\n         // TODO more rounding checks\n      }\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( buyback )\n{\n   ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin) );\n   upgrade_to_lifetime_member(philbin_id);\n\n   generate_blocks( HARDFORK_555_TIME );\n\n   try\n   {\n      {\n         //\n         // Izzy (issuer)\n         // Alice, Bob, Chloe, Dan (ABCD)\n         // Rex (recycler -- buyback account)\n         // Philbin (registrar)\n         //\n\n         asset_id_type nono_id = create_user_issued_asset( \"NONO\", izzy_id(db), 0 ).id;\n         asset_id_type buyme_id = create_user_issued_asset( \"BUYME\", izzy_id(db), 0 ).id;\n\n         // Create a buyback account\n         account_id_type rex_id;\n         {\n            buyback_account_options bbo;\n            bbo.asset_to_buy = buyme_id;\n            bbo.asset_to_buy_issuer = izzy_id;\n            bbo.markets.emplace( asset_id_type() );\n            account_create_operation create_op = make_account( \"rex\" );\n            create_op.registrar = philbin_id;\n            create_op.extensions.value.buyback_options = bbo;\n            create_op.owner = authority::null_authority();\n            create_op.active = authority::null_authority();\n\n            // Let's break it...\n\n            signed_transaction tx;\n            tx.operations.push_back( create_op );\n            set_expiration( db, tx );\n\n            tx.operations.back().get< account_create_operation >().extensions.value.buyback_options->asset_to_buy_issuer = alice_id;\n            sign( tx, alice_private_key );\n            sign( tx, philbin_private_key );\n\n            // Alice and Philbin signed, but asset issuer is invalid\n            GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), account_create_buyback_incorrect_issuer );\n\n            tx.clear_signatures();\n            tx.operations.back().get< account_create_operation >().extensions.value.buyback_options->asset_to_buy_issuer = izzy_id;\n            sign( tx, philbin_private_key );\n\n            // Izzy didn't sign\n            GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), tx_missing_active_auth );\n            sign( tx, izzy_private_key );\n\n            // OK\n            processed_transaction ptx = PUSH_TX( db, tx );\n            rex_id = ptx.operation_results.back().get< object_id_type >();\n\n            // Try to create another account rex2 which is bbo on same asset\n            tx.clear_signatures();\n            tx.operations.back().get< account_create_operation >().name = \"rex2\";\n            sign( tx, izzy_private_key );\n            sign( tx, philbin_private_key );\n            GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), account_create_buyback_already_exists );\n         }\n\n         // issue some BUYME to Alice\n         // we need to set_expiration() before issue_uia() because the latter doens't call it #11\n         set_expiration( db, trx );  // #11\n         issue_uia( alice_id, asset( 1000, buyme_id ) );\n         issue_uia( alice_id, asset( 1000, nono_id ) );\n\n         // Alice wants to sell 100 BUYME for 1000 BTS, a middle price.\n         limit_order_id_type order_id_mid = create_sell_order( alice_id, asset( 100, buyme_id ), asset( 1000, asset_id_type() ) )->id;\n\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         generate_block();\n\n         // no success because buyback has none for sale\n         BOOST_CHECK( order_id_mid(db).for_sale == 100 );\n\n         // but we can send some to buyback\n         fund( rex_id(db), asset( 100, asset_id_type() ) );\n         // no action until next maint\n         BOOST_CHECK( order_id_mid(db).for_sale == 100 );\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         generate_block();\n\n         // partial fill, Alice now sells 90 BUYME for 900 BTS.\n         BOOST_CHECK( order_id_mid(db).for_sale == 90 );\n\n         // TODO check burn amount\n\n         // aagh more state in trx\n         set_expiration( db, trx );  // #11\n\n         // Selling 10 BUYME for 50 BTS, a low price.\n         limit_order_id_type order_id_low  = create_sell_order( alice_id, asset( 10, buyme_id ), asset(  50, asset_id_type() ) )->id;\n         // Selling 10 BUYME for 150 BTS, a high price.\n         limit_order_id_type order_id_high = create_sell_order( alice_id, asset( 10, buyme_id ), asset( 150, asset_id_type() ) )->id;\n\n         fund( rex_id(db), asset( 250, asset_id_type() ) );\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n         generate_block();\n\n         BOOST_CHECK( db.find( order_id_low  ) == nullptr );\n         BOOST_CHECK( db.find( order_id_mid  ) != nullptr );\n         BOOST_CHECK( db.find( order_id_high ) != nullptr );\n\n         // 250 CORE in rex                 90 BUYME in mid order    10 BUYME in low order\n         //  50 CORE goes to low order, buy 10 for 50 CORE\n         // 200 CORE goes to mid order, buy 20 for 200 CORE\n         //                                 70 BUYME in mid order     0 BUYME in low order\n\n         idump( (order_id_mid(db)) );\n         BOOST_CHECK( order_id_mid(db).for_sale == 70 );\n         BOOST_CHECK( order_id_high(db).for_sale == 10 );\n\n         BOOST_CHECK( get_balance( rex_id, asset_id_type() ) == 0 );\n\n         // clear out the books -- 700 left on mid order, 150 left on high order, so 2000 BTS should result in 1150 left over\n\n         fund( rex_id(db), asset( 2000, asset_id_type() ) );\n         generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n         idump( (get_balance( rex_id, asset_id_type() )) );\n\n         BOOST_CHECK( get_balance( rex_id, asset_id_type() ) == 1150 );\n\n         GRAPHENE_CHECK_THROW( transfer( alice_id, rex_id, asset( 1, nono_id ) ), fc::exception );\n         // TODO: Check cancellation works for account which is BTS-restricted\n      }\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/pob_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 Abit More, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"../common/database_fixture.hpp\"\n\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/ticket_object.hpp>\n#include <graphene/chain/proposal_object.hpp>\n\n#include <boost/test/unit_test.hpp>\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( pob_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( hardfork_time_test )\n{\n   try {\n\n      // Proceeds to a recent hard fork\n      generate_blocks( HARDFORK_CORE_1270_TIME );\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      // Before the hard fork, unable to create a ticket or update a ticket, or do any of them with proposals\n      BOOST_CHECK_THROW( create_ticket( sam_id, lock_180_days, asset(1) ), fc::exception );\n      ticket_object tmp_ticket;\n      tmp_ticket.account = sam_id;\n      BOOST_CHECK_THROW( update_ticket( tmp_ticket, lock_360_days, asset(1) ), fc::exception );\n\n      ticket_create_operation cop = make_ticket_create_op( sam_id, lock_720_days, asset(2) );\n      BOOST_CHECK_THROW( propose( cop ), fc::exception );\n\n      ticket_update_operation uop = make_ticket_update_op( tmp_ticket, lock_720_days, {} );\n      BOOST_CHECK_THROW( propose( uop ), fc::exception );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( validation_and_basic_logic_test )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam)(ted));\n\n      auto fee_amount = 50 * GRAPHENE_BLOCKCHAIN_PRECISION;\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n      fund( ted, asset(init_amount) );\n\n      const asset_object& usd = create_user_issued_asset( \"MYUSD\" );\n      issue_uia( sam, usd.amount(init_amount) );\n      issue_uia( ted, usd.amount(init_amount) );\n\n      // Able to propose\n      {\n         ticket_create_operation cop = make_ticket_create_op( sam_id, lock_720_days, asset(2) );\n         propose( cop );\n\n         ticket_object tmp_ticket;\n         tmp_ticket.account = sam_id;\n         ticket_update_operation uop = make_ticket_update_op( tmp_ticket, lock_720_days, {} );\n         propose( uop );\n      }\n\n      // Unable to create a ticket with invalid data\n      // zero amount\n      BOOST_CHECK_THROW( create_ticket( sam_id, lock_180_days, asset(0) ), fc::exception );\n      // negative amount\n      BOOST_CHECK_THROW( create_ticket( sam_id, lock_180_days, asset(-1) ), fc::exception );\n      // non-core asset\n      BOOST_CHECK_THROW( create_ticket( sam_id, lock_180_days, usd.amount(1) ), fc::exception );\n      // target type liquid\n      BOOST_CHECK_THROW( create_ticket( sam_id, liquid, asset(1) ), fc::exception );\n      // target type too big\n      BOOST_CHECK_THROW( create_ticket( sam_id, TICKET_TYPE_COUNT, asset(1) ), fc::exception );\n      // target type too big\n      {\n         ticket_create_operation cop = make_ticket_create_op( sam_id, lock_180_days, asset(1) );\n         cop.target_type = static_cast<uint8_t>(TICKET_TYPE_COUNT) + 1;\n         trx.operations.clear();\n         trx.operations.push_back( cop );\n\n         for( auto& o : trx.operations ) db.current_fee_schedule().set_fee(o);\n         set_expiration( db, trx );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      }\n\n      // enable and update fee schedule\n      enable_fees();\n      db.modify(global_property_id_type()(db), [](global_property_object& gpo)\n      {\n         auto& fee_params = gpo.parameters.get_mutable_fees().parameters;\n\n         auto itr = fee_params.find( ticket_create_operation::fee_parameters_type() );\n         itr->get<ticket_create_operation::fee_parameters_type>().fee = 1;\n\n         itr = fee_params.find( ticket_update_operation::fee_parameters_type() );\n         itr->get<ticket_update_operation::fee_parameters_type>().fee = 2;\n      });\n\n      int64_t expected_balance = init_amount;\n\n      // Able to create a ticket with valid data\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_180_days, asset(1) );\n      BOOST_CHECK( tick_1.account == sam_id );\n      BOOST_CHECK( tick_1.target_type == lock_180_days );\n      BOOST_CHECK( tick_1.amount == asset(1) );\n      expected_balance -= (1 + fee_amount);\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n\n      const ticket_object& tick_2 = create_ticket( sam_id, lock_360_days, asset(1000) );\n      BOOST_CHECK( tick_2.account == sam_id );\n      BOOST_CHECK( tick_2.target_type == lock_360_days );\n      BOOST_CHECK( tick_2.amount == asset(1000) );\n      expected_balance -= (1000 + fee_amount);\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n\n      const ticket_object& tick_3 = create_ticket( sam_id, lock_720_days, asset(10) );\n      BOOST_CHECK( tick_3.account == sam_id );\n      BOOST_CHECK( tick_3.target_type == lock_720_days );\n      BOOST_CHECK( tick_3.amount == asset(10) );\n      expected_balance -= (10 + fee_amount);\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n\n      const ticket_object& tick_4 = create_ticket( sam_id, lock_forever, asset(100000) );\n      BOOST_CHECK( tick_4.account == sam_id );\n      BOOST_CHECK( tick_4.target_type == lock_forever );\n      BOOST_CHECK( tick_4.amount == asset(100000) );\n      expected_balance -= (100000 + fee_amount);\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n\n      // Unable to update a ticket with invalid data\n      // zero amount\n      BOOST_CHECK_THROW( update_ticket( tick_1, liquid, asset(0) ), fc::exception );\n      // negative amount\n      BOOST_CHECK_THROW( update_ticket( tick_1, liquid, asset(-1) ), fc::exception );\n      // non-core asset\n      BOOST_CHECK_THROW( update_ticket( tick_1, liquid, asset(1, usd.id) ), fc::exception );\n      // too big amount\n      BOOST_CHECK_THROW( update_ticket( tick_1, liquid, asset(2) ), fc::exception );\n      // target type unchanged\n      BOOST_CHECK_THROW( update_ticket( tick_1, lock_180_days, {} ), fc::exception );\n      // target type unchanged\n      BOOST_CHECK_THROW( update_ticket( tick_1, lock_180_days, asset(1) ), fc::exception );\n      // target type too big\n      BOOST_CHECK_THROW( update_ticket( tick_1, TICKET_TYPE_COUNT, {} ), fc::exception );\n      {\n         // target type too big\n         ticket_update_operation uop = make_ticket_update_op( tick_1, liquid, asset(1) );\n         uop.target_type = static_cast<uint8_t>(TICKET_TYPE_COUNT) + 1;\n         trx.operations.clear();\n         trx.operations.push_back( uop );\n\n         for( auto& o : trx.operations ) db.current_fee_schedule().set_fee(o);\n         set_expiration( db, trx );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n\n         // account mismatch\n         uop.target_type = liquid;\n         uop.account = ted_id;\n         trx.operations.clear();\n         trx.operations.push_back( uop );\n         BOOST_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception );\n      }\n\n      ticket_id_type tick_1_id = tick_1.id;\n      ticket_id_type tick_2_id = tick_2.id;\n      ticket_id_type tick_4_id = tick_4.id;\n\n      // Update ticket 1 to liquid\n      generic_operation_result result = update_ticket( tick_1, liquid, asset(1) );\n      BOOST_REQUIRE( db.find( tick_1_id ) );\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).amount == asset(1) );\n      expected_balance -= fee_amount;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n      BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u );\n      BOOST_CHECK( *result.updated_objects.begin() == tick_1_id );\n      BOOST_CHECK_EQUAL( result.removed_objects.size(), 0u );\n\n      // target type unchanged\n      BOOST_CHECK_THROW( update_ticket( tick_1, liquid, {} ), fc::exception );\n\n      // Update ticket 1 to lock_forever\n      result = update_ticket( tick_1, lock_forever, {} );\n      BOOST_REQUIRE( db.find( tick_1_id ) );\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).amount == asset(1) );\n      expected_balance -= fee_amount;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n      BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u );\n      BOOST_CHECK( *result.updated_objects.begin() == tick_1_id );\n      BOOST_CHECK_EQUAL( result.removed_objects.size(), 0u );\n\n      // Update 3 CORE in ticket 2 to lock_180_days\n      result = update_ticket( tick_2, lock_180_days, asset(3) );\n      BOOST_REQUIRE( db.find( tick_2_id ) );\n      BOOST_CHECK( tick_2_id(db).account == sam_id );\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days ); // target type of the remaining ticket is unchanged\n      BOOST_CHECK( tick_2_id(db).amount == asset(1000-3) );\n      expected_balance -= fee_amount;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n      BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u );\n      BOOST_CHECK( *result.updated_objects.begin() == tick_2_id );\n      BOOST_CHECK_EQUAL( result.removed_objects.size(), 0u );\n\n      ticket_id_type new_ticket_id = *result.new_objects.begin();\n      BOOST_CHECK( new_ticket_id > tick_4_id );\n      BOOST_REQUIRE( db.find( new_ticket_id ) );\n      BOOST_CHECK( new_ticket_id(db).account == sam_id );\n      BOOST_CHECK( new_ticket_id(db).target_type == lock_180_days ); // target type of the new ticket is set\n      BOOST_CHECK( new_ticket_id(db).amount == asset(3) );\n\n      generate_block();\n\n      BOOST_REQUIRE( db.find( tick_1_id ) );\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).amount == asset(1) );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, expected_balance );\n\n      BOOST_REQUIRE( db.find( tick_2_id ) );\n      BOOST_CHECK( tick_2_id(db).account == sam_id );\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).amount == asset(1000-3) );\n\n      BOOST_REQUIRE( db.find( new_ticket_id ) );\n      BOOST_CHECK( new_ticket_id(db).account == sam_id );\n      BOOST_CHECK( new_ticket_id(db).target_type == lock_180_days );\n      BOOST_CHECK( new_ticket_id(db).amount == asset(3) );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_180_ticket )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_180_days, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be stable now\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_360_ticket )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_360_days, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be stable now\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_720_ticket )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_720_days, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be stable now\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_720_ticket_if_blocks_missed )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_720_days, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 800 days passed\n      generate_blocks( db.head_block_time() + fc::days(800) );\n      set_expiration( db, trx );\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_forever_ticket )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_forever, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should have reached the target\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // no longer be able to update ticket\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_forever_ticket_if_blocks_missed )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_forever, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 60 days passed\n      generate_blocks( db.head_block_time() + fc::days(60) );\n      set_expiration( db, trx );\n\n      // ticket should have reached the target\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // no longer be able to update ticket\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( one_lock_forever_ticket_if_too_many_blocks_missed )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_forever, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n\n      // 1060 days passed\n      generate_blocks( db.head_block_time() + fc::days(1060) );\n      set_expiration( db, trx );\n\n      // check ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // no longer be able to update ticket\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n\n      BOOST_REQUIRE( tick_1_id == ticket_id_type() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( upgrade_lock_180_ticket_to_360 )\n{ try {\n\n      INVOKE( one_lock_180_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_360_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be upgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( upgrade_lock_180_ticket_to_720 )\n{ try {\n\n      INVOKE( one_lock_180_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be upgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be upgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( upgrade_lock_180_ticket_to_forever )\n{ try {\n\n      INVOKE( one_lock_180_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_forever, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be upgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should have reached the target\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( upgrade_lock_360_ticket_to_720 )\n{ try {\n\n      INVOKE( one_lock_360_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be upgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( upgrade_lock_360_ticket_to_forever )\n{ try {\n\n      INVOKE( one_lock_360_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_forever, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be upgraded now, and still charging\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should have reached the target\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( upgrade_lock_720_ticket_to_forever )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_forever, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should have reached the target\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( withdraw_lock_180_ticket )\n{ try {\n\n      INVOKE( one_lock_180_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( withdraw_lock_360_ticket )\n{ try {\n\n      INVOKE( one_lock_360_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( withdraw_lock_720_ticket )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 359 days passed\n      generate_blocks( db.head_block_time() + fc::days(359) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( withdraw_lock_720_ticket_if_blocks_missed )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 900 days passed\n      generate_blocks( db.head_block_time() + fc::days(900) );\n      set_expiration( db, trx );\n\n      // the ticket should be freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( downgrade_lock_720_ticket_to_180 )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), lock_180_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 359 days passed\n      generate_blocks( db.head_block_time() + fc::days(359) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded, and is stable now\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( downgrade_lock_720_ticket_to_360 )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), lock_360_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 359 days passed\n      generate_blocks( db.head_block_time() + fc::days(359) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded, and is stable now\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( downgrade_lock_360_ticket_to_180 )\n{ try {\n\n      INVOKE( one_lock_360_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // withdraw the ticket\n      auto result = update_ticket( tick_1_id(db), lock_180_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 179 days passed\n      generate_blocks( db.head_block_time() + fc::days(179) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100  * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have downgraded, and is stable now\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( forever_ticket_auto_update )\n{ try {\n\n      INVOKE( one_lock_forever_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // can not update ticket\n      auto check_no_update = [&]()\n      {\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      };\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n      check_no_update();\n\n      for( int8_t i = 0; i < 4; ++i )\n      {\n         // 179 days passed\n         generate_blocks( db.head_block_time() + fc::days(179) );\n         set_expiration( db, trx );\n\n         // no change\n         BOOST_CHECK( tick_1_id(db).account == sam_id );\n         BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n         BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n         BOOST_CHECK( tick_1_id(db).status == withdrawing );\n         BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n         BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 * (4-i) );\n         BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n         check_no_update();\n\n         // 1 day passed\n         generate_blocks( db.head_block_time() + fc::days(1) );\n         set_expiration( db, trx );\n\n         // the ticket should have been updated\n         BOOST_CHECK( tick_1_id(db).account == sam_id );\n         BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n         BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n         if( i < 3 ) {\n            BOOST_CHECK( tick_1_id(db).status == withdrawing );\n         } else {\n            BOOST_CHECK( tick_1_id(db).status == stable );\n            BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n            BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n         }\n         BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n         BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 * (3-i) );\n         BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n         check_no_update();\n      }\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n      check_no_update();\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( forever_ticket_auto_update_if_blocks_missed )\n{ try {\n\n      INVOKE( one_lock_forever_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // can not update ticket\n      auto check_no_update = [&]()\n      {\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_forever, asset(100) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n         BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      };\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n      check_no_update();\n\n      // 750 days passed\n      generate_blocks( db.head_block_time() + fc::days(750) );\n      set_expiration( db, trx );\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 0 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n      check_no_update();\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( cancel_charging_from_liquid )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n\n      // create a ticket\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_360_days, asset(100) );\n      ticket_id_type tick_1_id = tick_1.id;\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      sam_balance -= 100;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_360_days, asset(100) ), fc::exception );\n\n      // cancel charging\n      auto result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 6 day passed\n      generate_blocks( db.head_block_time() + fc::days(6) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should be freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( cancel_charging_from_non_liquid )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_forever, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // cancel charging\n      result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 6 day passed\n      generate_blocks( db.head_block_time() + fc::days(6) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // ticket should be stable now\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_720_days, asset(100) ), fc::exception );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( update_from_charging_to_withdrawing )\n{ try {\n\n      INVOKE( one_lock_720_ticket );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_forever, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // update target\n      result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // unable to update ticket if not to change target type\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, {} ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(1) ), fc::exception );\n      BOOST_CHECK_THROW( update_ticket( tick_1_id(db), liquid, asset(100) ), fc::exception );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_step_1 )\n{ try {\n\n      INVOKE( update_from_charging_to_withdrawing );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // 114 days passed\n      generate_blocks( db.head_block_time() + fc::days(114) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // upgrade the ticket\n      auto result = update_ticket( tick_1_id(db), lock_forever, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time != time_point_sec::maximum() );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_wait )\n{ try {\n\n      INVOKE( update_from_withdrawing_to_charging_step_1 );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time != time_point_sec::maximum() );\n      auto down_time = tick_1_id(db).next_type_downgrade_time;\n\n      // 14 days passed\n      generate_blocks( db.head_block_time() + fc::days(14) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // the ticket should have upgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 8 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n      BOOST_CHECK( tick_1_id(db).account == sam_id );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again )\n{ try {\n\n      INVOKE( update_from_withdrawing_to_charging_step_1 );\n\n      GET_ACTOR( sam );\n\n      int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value;\n\n      ticket_id_type tick_1_id; // default value\n\n      // check the ticket\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time != time_point_sec::maximum() );\n      auto down_time = tick_1_id(db).next_type_downgrade_time;\n\n      // 6 days passed\n      generate_blocks( db.head_block_time() + fc::days(6) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n\n      // downgrade again\n      auto result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      // current type should not change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == down_time );\n\n      // X days passed, now about to downgrade\n      generate_blocks( down_time - fc::days(1) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == down_time );\n\n      // upgrade again\n      result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n\n      // 6 days passed\n      generate_blocks( db.head_block_time() + fc::days(6) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n      // should have downgraded if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // downgrade again\n      result = update_ticket( tick_1_id(db), lock_180_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n      BOOST_CHECK_EQUAL( result.updated_objects.size(), 1u );\n\n      // the ticket should have downgraded\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == down_time + 180*86400 );\n\n      // X days passed, now about to downgrade\n      generate_blocks( down_time + 180*86400 - fc::days(1) );\n      set_expiration( db, trx );\n\n      // upgrade again\n      result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 );\n\n      // 6 days passed\n      generate_blocks( db.head_block_time() + fc::days(6) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(100) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 );\n      // should have downgraded if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // partially cancel charging\n      result = update_ticket( tick_1_id(db), lock_180_days, asset(10) );\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n      BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u );\n      BOOST_CHECK_EQUAL( result.removed_objects.size(), 0u );\n      BOOST_CHECK( *result.updated_objects.begin() == tick_1_id );\n\n      // the new ticket is stable\n      ticket_id_type tick_2_id = *result.new_objects.begin();\n\n      BOOST_REQUIRE( db.find( tick_2_id ) );\n      BOOST_CHECK( tick_2_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_2_id(db).status == stable );\n      BOOST_CHECK( tick_2_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 10 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // check the remainder\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 );\n      // should have downgraded if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // generate a block\n      generate_block();\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 );\n      // should have downgraded if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // cancel charging\n      result = update_ticket( tick_1_id(db), lock_180_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n      BOOST_CHECK_EQUAL( result.updated_objects.size(), 1u );\n\n      // the ticket is now stable\n      BOOST_CHECK( tick_1_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_1_id(db).status == stable );\n      BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 * 2 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // downgrade again\n      result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time != time_point_sec::maximum() );\n      down_time = tick_1_id(db).next_type_downgrade_time;\n\n      // X days passed, 30 days to downgrade\n      generate_blocks( down_time - fc::days(30) );\n      set_expiration( db, trx );\n\n      // upgrade again\n      result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n\n      // downgrade again\n      result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n\n      // X days passed, now about to free\n      generate_blocks( down_time - fc::days(1) );\n      set_expiration( db, trx );\n\n      // upgrade again\n      result = update_ticket( tick_1_id(db), lock_720_days, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n\n      // 6 days passed\n      generate_blocks( db.head_block_time() + fc::days(6) );\n      set_expiration( db, trx );\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(90) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n      // should have freed if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // partially cancel charging\n      result = update_ticket( tick_1_id(db), liquid, asset(15) );\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n      BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u );\n      BOOST_REQUIRE_EQUAL( result.removed_objects.size(), 1u );\n      BOOST_CHECK( *result.updated_objects.begin() == tick_1_id );\n\n      // the new created ticket is freed already\n      ticket_id_type tick_3_id = *result.new_objects.begin();\n      BOOST_CHECK( *result.removed_objects.begin() == tick_3_id );\n      BOOST_CHECK( !db.find( tick_3_id ) );\n\n      // check the remainder\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(75) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 75 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 15 );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n      // should have freed if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // generate a block\n      generate_block();\n\n      // no change\n      BOOST_CHECK( tick_1_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == charging );\n      BOOST_CHECK( tick_1_id(db).amount == asset(75) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 75 );\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 15 );\n\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time );\n      // should have freed if not changed to upgrade\n      BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() );\n\n      // cancel charging\n      result = update_ticket( tick_1_id(db), liquid, {} );\n      BOOST_CHECK_EQUAL( result.new_objects.size(), 0u );\n      BOOST_CHECK_EQUAL( result.updated_objects.size(), 0u );\n      BOOST_REQUIRE_EQUAL( result.removed_objects.size(), 1u );\n      BOOST_CHECK( *result.removed_objects.begin() == tick_1_id );\n\n      // the ticket is freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 90 );\n\n      // generate a block\n      generate_block();\n\n      // the ticket is freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 90 );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( multiple_tickets )\n{ try {\n\n      // Pass the hard fork time\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      set_expiration( db, trx );\n\n      ACTORS((sam)(ted));\n\n      auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION;\n      fund( sam, asset(init_amount) );\n      fund( ted, asset(init_amount) );\n\n      int64_t sam_balance = init_amount;\n      int64_t ted_balance = init_amount;\n\n      // Sam create some tickets\n      const ticket_object& tick_1 = create_ticket( sam_id, lock_180_days, asset(1) );\n      BOOST_CHECK( tick_1.account == sam_id );\n      BOOST_CHECK( tick_1.target_type == lock_180_days );\n      BOOST_CHECK( tick_1.current_type == liquid );\n      BOOST_CHECK( tick_1.status == charging );\n      BOOST_CHECK( tick_1.amount == asset(1) );\n      BOOST_CHECK_EQUAL( tick_1.value.value, 1 );\n      sam_balance -= 1;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      const ticket_object& tick_2 = create_ticket( sam_id, lock_360_days, asset(1000) );\n      BOOST_CHECK( tick_2.account == sam_id );\n      BOOST_CHECK( tick_2.target_type == lock_360_days );\n      BOOST_CHECK( tick_2.current_type == liquid );\n      BOOST_CHECK( tick_2.status == charging );\n      BOOST_CHECK( tick_2.amount == asset(1000) );\n      BOOST_CHECK_EQUAL( tick_2.value.value, 1000 );\n      sam_balance -= 1000;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      const ticket_object& tick_3 = create_ticket( sam_id, lock_720_days, asset(10) );\n      BOOST_CHECK( tick_3.account == sam_id );\n      BOOST_CHECK( tick_3.target_type == lock_720_days );\n      BOOST_CHECK( tick_3.current_type == liquid );\n      BOOST_CHECK( tick_3.status == charging );\n      BOOST_CHECK( tick_3.amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3.value.value, 10 );\n      sam_balance -= 10;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      // Ted create a ticket\n      const ticket_object& tick_4 = create_ticket( ted_id, lock_forever, asset(100000) );\n      BOOST_CHECK( tick_4.account == ted_id );\n      BOOST_CHECK( tick_4.target_type == lock_forever );\n      BOOST_CHECK( tick_4.current_type == liquid );\n      BOOST_CHECK( tick_4.status == charging );\n      BOOST_CHECK( tick_4.amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4.value.value, 100000 );\n      ted_balance -= 100000;\n      BOOST_CHECK_EQUAL( db.get_balance( ted_id, asset_id_type() ).amount.value, ted_balance );\n\n      ticket_id_type tick_1_id = tick_1.id;\n      ticket_id_type tick_2_id = tick_2.id;\n      ticket_id_type tick_3_id = tick_3.id;\n      ticket_id_type tick_4_id = tick_4.id;\n\n      // one day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // Update ticket 1 to liquid\n      generic_operation_result result = update_ticket( tick_1_id(db), liquid, asset(1) );\n      BOOST_REQUIRE( db.find( tick_1_id ) );\n      BOOST_CHECK( tick_1_id(db).target_type == liquid );\n      BOOST_CHECK( tick_1_id(db).current_type == liquid );\n      BOOST_CHECK( tick_1_id(db).status == withdrawing );\n      BOOST_CHECK( tick_1_id(db).amount == asset(1) );\n      BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 1 );\n\n      // Update 30 CORE in ticket 2 to lock_180_days\n      result = update_ticket( tick_2_id(db), lock_180_days, asset(30) );\n      BOOST_REQUIRE( db.find( tick_2_id ) );\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days ); // target type of the remaining ticket is unchanged\n      BOOST_CHECK( tick_2_id(db).current_type == liquid );\n      BOOST_CHECK( tick_2_id(db).status == charging );\n      BOOST_CHECK( tick_2_id(db).amount == asset(970) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 970 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_5_id = *result.new_objects.begin();\n      BOOST_REQUIRE( db.find( tick_5_id ) );\n      BOOST_CHECK( tick_5_id(db).target_type == lock_180_days ); // target type of the new ticket is set\n      BOOST_CHECK( tick_5_id(db).current_type == liquid );\n      BOOST_CHECK( tick_5_id(db).status == charging );\n      BOOST_CHECK( tick_5_id(db).amount == asset(30) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 30 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == liquid );\n      BOOST_CHECK( tick_3_id(db).status == charging );\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == liquid );\n      BOOST_CHECK( tick_4_id(db).status == charging );\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 );\n\n      // 7 days passed\n      generate_blocks( db.head_block_time() + fc::days(7) );\n      set_expiration( db, trx );\n\n      // ticket 1 should have been freed\n      BOOST_CHECK( !db.find( tick_1_id ) );\n      sam_balance += 1;\n      BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == liquid );\n      BOOST_CHECK( tick_2_id(db).status == charging );\n      BOOST_CHECK( tick_2_id(db).amount == asset(970) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 970 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == liquid );\n      BOOST_CHECK( tick_3_id(db).status == charging );\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == liquid );\n      BOOST_CHECK( tick_4_id(db).status == charging );\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 );\n\n      BOOST_CHECK( tick_5_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).current_type == liquid );\n      BOOST_CHECK( tick_5_id(db).status == charging );\n      BOOST_CHECK( tick_5_id(db).amount == asset(30) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 30 );\n\n      // 7 days passed\n      generate_blocks( db.head_block_time() + fc::days(7) );\n      set_expiration( db, trx );\n\n      // ticket 2,3,4,5 should have upgraded\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_2_id(db).status == charging );\n      BOOST_CHECK( tick_2_id(db).amount == asset(970) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 970 * 2 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_3_id(db).status == charging );\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 * 2 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_4_id(db).status == charging );\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 * 2 );\n\n      BOOST_CHECK( tick_5_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).status == stable );\n      BOOST_CHECK( tick_5_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).amount == asset(30) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 30 * 2 );\n\n      // split ticket 2, cancel upgrade of some\n      result = update_ticket( tick_2_id(db), lock_180_days, asset(50) );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_2_id(db).status == charging ); // 15 days to finish\n      BOOST_CHECK( tick_2_id(db).amount == asset(920) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 920 * 2 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_6_id = *result.new_objects.begin();\n      BOOST_CHECK( tick_6_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).status == withdrawing ); // 7 days to finish\n      BOOST_CHECK( tick_6_id(db).amount == asset(50) );\n      BOOST_CHECK_EQUAL( tick_6_id(db).value.value, 50 * 2 );\n\n      // split ticket 2 again, downgrade some\n      result = update_ticket( tick_2_id(db), liquid, asset(20) );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_2_id(db).status == charging ); // 15 days to finish\n      BOOST_CHECK( tick_2_id(db).amount == asset(900) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 900 * 2 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_7_id = *result.new_objects.begin();\n      BOOST_CHECK( tick_7_id(db).target_type == liquid );\n      BOOST_CHECK( tick_7_id(db).current_type == liquid );\n      BOOST_CHECK( tick_7_id(db).status == withdrawing ); // 180 days to finish\n      BOOST_CHECK( tick_7_id(db).amount == asset(20) );\n      BOOST_CHECK_EQUAL( tick_7_id(db).value.value, 20 );\n\n      // 2 days passed\n      generate_blocks( db.head_block_time() + fc::days(2) );\n      set_expiration( db, trx );\n\n      // split ticket 5\n      result = update_ticket( tick_5_id(db), liquid, asset(12) );\n\n      BOOST_CHECK( tick_5_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).status == stable );\n      BOOST_CHECK( tick_5_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).amount == asset(18) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 18 * 2 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_51_id = *result.new_objects.begin();\n      BOOST_CHECK( tick_51_id(db).target_type == liquid );\n      BOOST_CHECK( tick_51_id(db).current_type == liquid );\n      BOOST_CHECK( tick_51_id(db).status == withdrawing ); // 180 days to finish\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 );\n\n      // split ticket 5 again\n      result = update_ticket( tick_5_id(db), lock_forever, asset(13) );\n\n      BOOST_CHECK( tick_5_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).status == stable );\n      BOOST_CHECK( tick_5_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).amount == asset(5) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 5 * 2 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_52_id = *result.new_objects.begin();\n      BOOST_CHECK( tick_52_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_52_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_52_id(db).status == charging ); // 15 days to next step\n      BOOST_CHECK( tick_52_id(db).amount == asset(13) );\n      BOOST_CHECK_EQUAL( tick_52_id(db).value.value, 13 * 2 );\n\n      // 1 day passed\n      generate_blocks( db.head_block_time() + fc::days(1) );\n      set_expiration( db, trx );\n\n      // check ticket 5\n      BOOST_CHECK( tick_5_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_5_id(db).status == stable );\n      BOOST_CHECK( tick_5_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_5_id(db).amount == asset(5) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 5 * 2 );\n\n      // downgrade ticket 5\n      result = update_ticket( tick_5_id(db), liquid, {} );\n\n      BOOST_CHECK( tick_5_id(db).target_type == liquid );\n      BOOST_CHECK( tick_5_id(db).current_type == liquid );\n      BOOST_CHECK( tick_5_id(db).status == withdrawing ); // 180 days to finish\n      BOOST_CHECK( tick_5_id(db).amount == asset(5) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 5 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 0u );\n\n      // check ticket 51\n      BOOST_CHECK( tick_51_id(db).target_type == liquid );\n      BOOST_CHECK( tick_51_id(db).current_type == liquid );\n      BOOST_CHECK( tick_51_id(db).status == withdrawing ); // 179 days to finish\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 );\n\n      // cancel downgrading ticket 51\n      result = update_ticket( tick_51_id(db), lock_180_days, asset(12) );\n\n      BOOST_CHECK( tick_51_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).current_type == liquid );\n      BOOST_CHECK( tick_51_id(db).status == charging ); // 15 days to finish\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 0u );\n\n      // check ticket 7\n      BOOST_CHECK( tick_7_id(db).target_type == liquid );\n      BOOST_CHECK( tick_7_id(db).current_type == liquid );\n      BOOST_CHECK( tick_7_id(db).status == withdrawing ); // 177 days to finish\n      BOOST_CHECK( tick_7_id(db).amount == asset(20) );\n      BOOST_CHECK_EQUAL( tick_7_id(db).value.value, 20 );\n\n      // partly cancel downgrading ticket 7\n      result = update_ticket( tick_7_id(db), lock_180_days, asset(17) );\n\n      BOOST_CHECK( tick_7_id(db).target_type == liquid );\n      BOOST_CHECK( tick_7_id(db).current_type == liquid );\n      BOOST_CHECK( tick_7_id(db).status == withdrawing ); // 177 days to finish\n      BOOST_CHECK( tick_7_id(db).amount == asset(3) );\n      BOOST_CHECK_EQUAL( tick_7_id(db).value.value, 3 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_8_id = *result.new_objects.begin();\n      BOOST_CHECK( tick_8_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).current_type == liquid );\n      BOOST_CHECK( tick_8_id(db).status == charging ); // 15 days to finish\n      BOOST_CHECK( tick_8_id(db).amount == asset(17) );\n      BOOST_CHECK_EQUAL( tick_8_id(db).value.value, 17 );\n\n      // downgrade some amount of ticket 6\n      result = update_ticket( tick_6_id(db), liquid, asset(23) );\n\n      BOOST_CHECK( tick_6_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).status == withdrawing ); // 4 days to finish\n      BOOST_CHECK( tick_6_id(db).amount == asset(27) );\n      BOOST_CHECK_EQUAL( tick_6_id(db).value.value, 27 * 2 );\n\n      BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u );\n\n      ticket_id_type tick_9_id = *result.new_objects.begin();\n      BOOST_CHECK( tick_9_id(db).target_type == liquid );\n      BOOST_CHECK( tick_9_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_9_id(db).status == withdrawing ); // 4 days to next step\n      BOOST_CHECK( tick_9_id(db).amount == asset(23) );\n      BOOST_CHECK_EQUAL( tick_9_id(db).value.value, 23 * 2 );\n\n      // 4 days passed\n      generate_blocks( db.head_block_time() + fc::days(4) );\n      set_expiration( db, trx );\n\n      // ticket 6 should be stable now, ticket 9 should have entered the next step, others no change\n      BOOST_CHECK( tick_6_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).status == stable );\n      BOOST_CHECK( tick_6_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).amount == asset(27) );\n      BOOST_CHECK_EQUAL( tick_6_id(db).value.value, 27 * 2 );\n\n      BOOST_CHECK( tick_9_id(db).target_type == liquid );\n      BOOST_CHECK( tick_9_id(db).current_type == liquid );\n      BOOST_CHECK( tick_9_id(db).status == withdrawing ); // 180 days to next step\n      BOOST_CHECK( tick_9_id(db).amount == asset(23) );\n      BOOST_CHECK_EQUAL( tick_9_id(db).value.value, 23 );\n\n      BOOST_CHECK( tick_7_id(db).target_type == liquid );\n      BOOST_CHECK( tick_7_id(db).current_type == liquid );\n      BOOST_CHECK( tick_7_id(db).status == withdrawing ); // 173 days to finish\n      BOOST_CHECK( tick_7_id(db).amount == asset(3) );\n      BOOST_CHECK_EQUAL( tick_7_id(db).value.value, 3 );\n\n      BOOST_CHECK( tick_8_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).current_type == liquid );\n      BOOST_CHECK( tick_8_id(db).status == charging ); // 11 days to finish\n      BOOST_CHECK( tick_8_id(db).amount == asset(17) );\n      BOOST_CHECK_EQUAL( tick_8_id(db).value.value, 17 );\n\n      BOOST_CHECK( tick_5_id(db).target_type == liquid );\n      BOOST_CHECK( tick_5_id(db).current_type == liquid );\n      BOOST_CHECK( tick_5_id(db).status == withdrawing ); // 176 days to finish\n      BOOST_CHECK( tick_5_id(db).amount == asset(5) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 5 );\n\n      BOOST_CHECK( tick_51_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).current_type == liquid );\n      BOOST_CHECK( tick_51_id(db).status == charging ); // 11 days to finish\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 );\n\n      BOOST_CHECK( tick_52_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_52_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_52_id(db).status == charging ); // 10 days to finish\n      BOOST_CHECK( tick_52_id(db).amount == asset(13) );\n      BOOST_CHECK_EQUAL( tick_52_id(db).value.value, 13 * 2 );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_2_id(db).status == charging ); // 8 days to finish\n      BOOST_CHECK( tick_2_id(db).amount == asset(900) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 900 * 2 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_3_id(db).status == charging ); // 8 days to next step\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 * 2 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_4_id(db).status == charging ); // 8 days to next step\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 * 2 );\n\n      // 8 days passed\n      generate_blocks( db.head_block_time() + fc::days(8) );\n      set_expiration( db, trx );\n\n      // ticket 2,3,4 should be updated\n      BOOST_CHECK( tick_6_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).status == stable );\n      BOOST_CHECK( tick_6_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).amount == asset(27) );\n      BOOST_CHECK_EQUAL( tick_6_id(db).value.value, 27 * 2 );\n\n      BOOST_CHECK( tick_9_id(db).target_type == liquid );\n      BOOST_CHECK( tick_9_id(db).current_type == liquid );\n      BOOST_CHECK( tick_9_id(db).status == withdrawing ); // 172 days to finish\n      BOOST_CHECK( tick_9_id(db).amount == asset(23) );\n      BOOST_CHECK_EQUAL( tick_9_id(db).value.value, 23 );\n\n      BOOST_CHECK( tick_7_id(db).target_type == liquid );\n      BOOST_CHECK( tick_7_id(db).current_type == liquid );\n      BOOST_CHECK( tick_7_id(db).status == withdrawing ); // 165 days to finish\n      BOOST_CHECK( tick_7_id(db).amount == asset(3) );\n      BOOST_CHECK_EQUAL( tick_7_id(db).value.value, 3 );\n\n      BOOST_CHECK( tick_8_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).current_type == liquid );\n      BOOST_CHECK( tick_8_id(db).status == charging ); // 3 days to finish\n      BOOST_CHECK( tick_8_id(db).amount == asset(17) );\n      BOOST_CHECK_EQUAL( tick_8_id(db).value.value, 17 );\n\n      BOOST_CHECK( tick_5_id(db).target_type == liquid );\n      BOOST_CHECK( tick_5_id(db).current_type == liquid );\n      BOOST_CHECK( tick_5_id(db).status == withdrawing ); // 168 days to finish\n      BOOST_CHECK( tick_5_id(db).amount == asset(5) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 5 );\n\n      BOOST_CHECK( tick_51_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).current_type == liquid );\n      BOOST_CHECK( tick_51_id(db).status == charging ); // 3 days to finish\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 );\n\n      BOOST_CHECK( tick_52_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_52_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_52_id(db).status == charging ); // 2 days to finish\n      BOOST_CHECK( tick_52_id(db).amount == asset(13) );\n      BOOST_CHECK_EQUAL( tick_52_id(db).value.value, 13 * 2 );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).status == stable );\n      BOOST_CHECK( tick_2_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).amount == asset(900) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 900 * 4 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_3_id(db).status == charging ); // 15 days to next step\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 * 4 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_4_id(db).status == charging ); // 15 days to next step\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 * 4 );\n\n      // 3 days passed\n      generate_blocks( db.head_block_time() + fc::days(3) );\n      set_expiration( db, trx );\n\n      // ticket 8,51,52 should be updated\n      BOOST_CHECK( tick_6_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).status == stable );\n      BOOST_CHECK( tick_6_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).amount == asset(27) );\n      BOOST_CHECK_EQUAL( tick_6_id(db).value.value, 27 * 2 );\n\n      BOOST_CHECK( tick_9_id(db).target_type == liquid );\n      BOOST_CHECK( tick_9_id(db).current_type == liquid );\n      BOOST_CHECK( tick_9_id(db).status == withdrawing ); // 169 days to finish\n      BOOST_CHECK( tick_9_id(db).amount == asset(23) );\n      BOOST_CHECK_EQUAL( tick_9_id(db).value.value, 23 );\n\n      BOOST_CHECK( tick_7_id(db).target_type == liquid );\n      BOOST_CHECK( tick_7_id(db).current_type == liquid );\n      BOOST_CHECK( tick_7_id(db).status == withdrawing ); // 162 days to finish\n      BOOST_CHECK( tick_7_id(db).amount == asset(3) );\n      BOOST_CHECK_EQUAL( tick_7_id(db).value.value, 3 );\n\n      BOOST_CHECK( tick_8_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).status == stable );\n      BOOST_CHECK( tick_8_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_8_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_8_id(db).amount == asset(17) );\n      BOOST_CHECK_EQUAL( tick_8_id(db).value.value, 17 * 2 );\n\n      BOOST_CHECK( tick_5_id(db).target_type == liquid );\n      BOOST_CHECK( tick_5_id(db).current_type == liquid );\n      BOOST_CHECK( tick_5_id(db).status == withdrawing ); // 165 days to finish\n      BOOST_CHECK( tick_5_id(db).amount == asset(5) );\n      BOOST_CHECK_EQUAL( tick_5_id(db).value.value, 5 );\n\n      BOOST_CHECK( tick_51_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).status == stable );\n      BOOST_CHECK( tick_51_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_51_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 * 2 );\n\n      BOOST_CHECK( tick_52_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_52_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_52_id(db).status == charging ); // 14 days to next step\n      BOOST_CHECK( tick_52_id(db).amount == asset(13) );\n      BOOST_CHECK_EQUAL( tick_52_id(db).value.value, 13 * 4 );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).status == stable );\n      BOOST_CHECK( tick_2_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).amount == asset(900) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 900 * 4 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_3_id(db).status == charging ); // 12 days to finish\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 * 4 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_4_id(db).status == charging ); // 12 days to next step\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 * 4 );\n\n      // 170 days passed\n      generate_blocks( db.head_block_time() + fc::days(170) );\n      set_expiration( db, trx );\n\n      // check tickets\n      BOOST_CHECK( tick_6_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_6_id(db).status == stable );\n      BOOST_CHECK( tick_6_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_6_id(db).amount == asset(27) );\n      BOOST_CHECK_EQUAL( tick_6_id(db).value.value, 27 * 2 );\n\n      BOOST_CHECK( !db.find( tick_9_id ) );\n      BOOST_CHECK( !db.find( tick_7_id ) );\n\n      BOOST_CHECK( tick_8_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_8_id(db).status == stable );\n      BOOST_CHECK( tick_8_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_8_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_8_id(db).amount == asset(17) );\n      BOOST_CHECK_EQUAL( tick_8_id(db).value.value, 17 * 2 );\n\n      BOOST_CHECK( !db.find( tick_5_id ) );\n\n      BOOST_CHECK( tick_51_id(db).target_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).current_type == lock_180_days );\n      BOOST_CHECK( tick_51_id(db).status == stable );\n      BOOST_CHECK( tick_51_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_51_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_51_id(db).amount == asset(12) );\n      BOOST_CHECK_EQUAL( tick_51_id(db).value.value, 12 * 2 );\n\n      BOOST_CHECK( tick_52_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_52_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_52_id(db).status == withdrawing ); // 39 days to next step\n      BOOST_CHECK( tick_52_id(db).amount == asset(13) );\n      BOOST_CHECK_EQUAL( tick_52_id(db).value.value, 13 * 8 );\n\n      BOOST_CHECK( tick_2_id(db).target_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).current_type == lock_360_days );\n      BOOST_CHECK( tick_2_id(db).status == stable );\n      BOOST_CHECK( tick_2_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_2_id(db).amount == asset(900) );\n      BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 900 * 4 );\n\n      BOOST_CHECK( tick_3_id(db).target_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).current_type == lock_720_days );\n      BOOST_CHECK( tick_3_id(db).status == stable );\n      BOOST_CHECK( tick_3_id(db).next_auto_update_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_3_id(db).next_type_downgrade_time == time_point_sec::maximum() );\n      BOOST_CHECK( tick_3_id(db).amount == asset(10) );\n      BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 10 * 8 );\n\n      BOOST_CHECK( tick_4_id(db).target_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).current_type == lock_forever );\n      BOOST_CHECK( tick_4_id(db).status == withdrawing ); // 37 days to next step\n      BOOST_CHECK( tick_4_id(db).amount == asset(100000) );\n      BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 100000 * 8 );\n\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/serialization_tests.cpp",
    "content": "/*\n * Copyright (c) 2015 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n\n\n#include <fc/crypto/digest.hpp>\n#include <fc/crypto/elliptic.hpp>\n#include <fc/reflect/variant.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\n\nBOOST_FIXTURE_TEST_SUITE( operation_unit_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( serialization_raw_test )\n{\n   try {\n      make_account();\n      transfer_operation op;\n      op.from = account_id_type(1);\n      op.to = account_id_type(2);\n      op.amount = asset(100);\n      trx.operations.push_back( op );\n      auto packed = fc::raw::pack( trx );\n      signed_transaction unpacked = fc::raw::unpack<signed_transaction>(packed);\n      unpacked.validate();\n      BOOST_CHECK( digest(trx) == digest(unpacked) );\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\nBOOST_AUTO_TEST_CASE( serialization_json_test )\n{\n   try {\n      make_account();\n      transfer_operation op;\n      op.from = account_id_type(1);\n      op.to = account_id_type(2);\n      op.amount = asset(100);\n      trx.operations.push_back( op );\n      fc::variant packed(trx, GRAPHENE_MAX_NESTED_OBJECTS);\n      signed_transaction unpacked = packed.as<signed_transaction>( GRAPHENE_MAX_NESTED_OBJECTS );\n      unpacked.validate();\n      BOOST_CHECK( digest(trx) == digest(unpacked) );\n   } catch (fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( json_tests )\n{\n   try {\n   auto var = fc::json::variants_from_string( \"10.6 \" );\n   var = fc::json::variants_from_string( \"10.5\" );\n   } catch ( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( extension_serialization_test )\n{\n   try\n   {\n      buyback_account_options bbo;\n      bbo.asset_to_buy = asset_id_type(1000);\n      bbo.asset_to_buy_issuer = account_id_type(2000);\n      bbo.markets.emplace( asset_id_type() );\n      bbo.markets.emplace( asset_id_type(777) );\n      account_create_operation create_op = make_account( \"rex\" );\n      create_op.registrar = account_id_type(1234);\n      create_op.extensions.value.buyback_options = bbo;\n\n      auto packed = fc::raw::pack( create_op );\n      account_create_operation unpacked = fc::raw::unpack<account_create_operation>(packed);\n\n      ilog( \"original: ${x}\", (\"x\", create_op) );\n      ilog( \"unpacked: ${x}\", (\"x\", unpacked) );\n   }\n   catch ( const fc::exception& e )\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/settle_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n\n#include <graphene/protocol/market.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( settle_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( settle_rounding_test )\n{\n   try {\n      // get around Graphene issue #615 feed expiration bug\n      generate_blocks(HARDFORK_615_TIME);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      set_expiration( db, trx );\n\n      ACTORS((paul)(michael)(rachel)(alice)(bob)(ted)(joe)(jim));\n\n      // create assets\n      const auto& bitusd = create_bitasset(\"USDBIT\", paul_id);\n      const auto& bitcny = create_bitasset(\"CNYBIT\", paul_id);\n      const auto& core   = asset_id_type()(db);\n      asset_id_type bitusd_id = bitusd.id;\n      asset_id_type bitcny_id = bitcny.id;\n      asset_id_type core_id = core.id;\n\n      // fund accounts\n      transfer(committee_account, michael_id, asset( 100000000 ) );\n      transfer(committee_account, paul_id, asset(10000000));\n      transfer(committee_account, alice_id, asset(10000000));\n      transfer(committee_account, bob_id, asset(10000000));\n      transfer(committee_account, jim_id, asset(10000000));\n\n      // add a feed to asset\n      update_feed_producers( bitusd, {paul.id} );\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(5);\n      publish_feed( bitusd, paul, current_feed );\n\n      // paul gets some bitusd\n      const call_order_object& call_paul = *borrow( paul, bitusd.amount(1000), core.amount(100) );\n      call_order_id_type call_paul_id = call_paul.id;\n      BOOST_REQUIRE_EQUAL( get_balance( paul, bitusd ), 1000 );\n\n      // and transfer some to rachel\n      transfer(paul.id, rachel.id, asset(200, bitusd.id));\n\n      BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael, bitusd), 0);\n      BOOST_CHECK_EQUAL(get_balance(michael, core), 100000000);\n\n      // michael gets some bitusd\n      const call_order_object& call_michael = *borrow(michael, bitusd.amount(6), core.amount(8));\n      call_order_id_type call_michael_id = call_michael.id;\n\n      // add settle order and check rounding issue\n      operation_result result = force_settle(rachel, bitusd.amount(4));\n\n      force_settlement_id_type settle_id = result.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id(db).balance.amount.value, 4 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), 196);\n      BOOST_CHECK_EQUAL(get_balance(michael, bitusd), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael, core), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul, core), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul, bitusd), 800);\n\n      BOOST_CHECK_EQUAL( 1000, call_paul.debt.value );\n      BOOST_CHECK_EQUAL( 100, call_paul.collateral.value );\n      BOOST_CHECK_EQUAL( 6, call_michael.debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael.collateral.value );\n\n      generate_blocks( db.head_block_time() + fc::hours(20) );\n      set_expiration( db, trx );\n\n      // default feed and settlement expires at the same time\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 100 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      // now yes expire settlement\n      generate_blocks( db.head_block_time() + fc::hours(6) );\n\n      // checks\n      BOOST_CHECK( !db.find( settle_id ) );\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0); // rachel paid 4 usd and got nothing\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 196);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 800);\n\n      BOOST_CHECK_EQUAL( 996, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 100, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 6, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 1002 ); // 1000 + 6 - 4\n\n      // settle more and check rounding issue\n      // by default 20% of total supply can be settled per maintenance interval, here we test less than it\n      set_expiration( db, trx );\n      operation_result result2 = force_settle(rachel_id(db), bitusd_id(db).amount(34));\n\n      force_settlement_id_type settle_id2 = result2.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id2(db).balance.amount.value, 34 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 162); // 196-34\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 800);\n\n      BOOST_CHECK_EQUAL( 996, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 100, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 6, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).collateral.value );\n\n      generate_blocks( db.head_block_time() + fc::hours(10) );\n      set_expiration( db, trx );\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 100 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      // now yes expire settlement\n      generate_blocks( db.head_block_time() + fc::hours(16) );\n      set_expiration( db, trx );\n\n      // checks\n      BOOST_CHECK( !db.find( settle_id2 ) );\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 1); // rachel got 1 core and paid 34 usd\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 162);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 800);\n\n      BOOST_CHECK_EQUAL( 962, call_paul_id(db).debt.value ); // 996 - 34\n      BOOST_CHECK_EQUAL( 99, call_paul_id(db).collateral.value ); // 100 - 1\n      BOOST_CHECK_EQUAL( 6, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 968 ); // 1002 - 34\n\n      // prepare for more tests\n      transfer(paul_id, rachel_id, asset(300, bitusd_id));\n      borrow(michael_id(db), bitusd_id(db).amount(2), core_id(db).amount(3));\n\n      // settle even more and check rounding issue\n      // by default 20% of total supply can be settled per maintenance interval, here we test more than it\n      const operation_result result3 = force_settle(rachel_id(db), bitusd_id(db).amount(3));\n      const operation_result result4 = force_settle(rachel_id(db), bitusd_id(db).amount(434));\n      const operation_result result5 = force_settle(rachel_id(db), bitusd_id(db).amount(5));\n\n      force_settlement_id_type settle_id3 = result3.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id3(db).balance.amount.value, 3 );\n\n      force_settlement_id_type settle_id4 = result4.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 434 );\n\n      force_settlement_id_type settle_id5 = result5.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 1);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20); // 162 + 300 - 3 - 434 - 5\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 8); // 6 + 2\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999989); // 99999992 - 3\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500); // 800 - 300\n\n      BOOST_CHECK_EQUAL( 962, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 99, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).debt.value ); // 6 + 2\n      BOOST_CHECK_EQUAL( 11, call_michael_id(db).collateral.value ); // 8 + 3\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 970 ); // 968 + 2\n\n      generate_blocks( db.head_block_time() + fc::hours(4) );\n      set_expiration( db, trx );\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // now yes expire settlement\n      generate_blocks( db.head_block_time() + fc::hours(22) );\n      set_expiration( db, trx );\n\n      // checks\n      // maximum amount that can be settled now is round_down(970 * 20%) = 194.\n      // settle_id3 (amount was 3) will be filled and get nothing.\n      // settle_id4 will pay 194 - 3 = 191 usd, will get round_down(191*5/101) = 9 core\n      BOOST_CHECK( !db.find( settle_id3 ) );\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 243 ); // 434 - 191\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10); // 1 + 9\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20); // no change\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 8);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999989);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 768, call_paul_id(db).debt.value ); // 962 - 3 - 191\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value ); // 99 - 9\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 11, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 776 ); // 970 - 3 - 191\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 194 ); // 3 + 191\n\n      generate_block();\n\n      // michael borrows more\n      set_expiration( db, trx );\n      borrow(michael_id(db), bitusd_id(db).amount(18), core_id(db).amount(200));\n\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 243 );\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 26); // 8 + 18\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999789); // 99999989 - 200\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 768, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 26, call_michael_id(db).debt.value ); // 8 + 18\n      BOOST_CHECK_EQUAL( 211, call_michael_id(db).collateral.value ); // 11 + 200\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 794 ); // 776 + 18\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 194 );\n\n      generate_block();\n\n      // maximum amount that can be settled now is round_down((794+194) * 20%) = 197,\n      //   already settled 194, so 197 - 194 = 3 more usd can be settled,\n      //   so settle_id3 will pay 3 usd and get nothing\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 240 ); // 243 - 3\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 26);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999789);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 765, call_paul_id(db).debt.value ); // 768 - 3\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 26, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 211, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 791 ); // 794 - 3\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 197 ); // 194 + 3\n\n      // michael borrows a little more\n      set_expiration( db, trx );\n      borrow(michael_id(db), bitusd_id(db).amount(20), core_id(db).amount(20));\n\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 240 );\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46); // 26 + 20\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769); // 99999789 - 20\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 765, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value ); // 26 + 20\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value ); // 211 + 20\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 811 ); // 791 + 20\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 197 );\n\n      generate_block();\n\n      // maximum amount that can be settled now is round_down((811+197) * 20%) = 201,\n      //   already settled 197, so 201 - 197 = 4 more usd can be settled,\n      //   so settle_id4 will pay 4 usd and get nothing\n\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 236 ); // 240 - 4\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 761, call_paul_id(db).debt.value ); // 765 - 4\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 807 ); // 811 - 4\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 201 ); // 197 + 4\n\n      generate_block();\n\n      // jim borrow some cny\n      call_order_id_type call_jim_id = borrow(jim_id(db), bitcny_id(db).amount(2000), core_id(db).amount(2000))->id;\n\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 2000);\n\n      // jim transfer some cny to joe\n      transfer(jim_id, joe_id, asset(1500, bitcny_id));\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 1500);\n\n      generate_block();\n\n      // give ted some usd\n      transfer(paul_id, ted_id, asset(100, bitusd_id));\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 100); // new: 100\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400); // 500 - 100\n\n      // ted settle\n      const operation_result result6 = force_settle(ted_id(db), bitusd_id(db).amount(20));\n      const operation_result result7 = force_settle(ted_id(db), bitusd_id(db).amount(21));\n      const operation_result result8 = force_settle(ted_id(db), bitusd_id(db).amount(22));\n\n      force_settlement_id_type settle_id6 = result6.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id6(db).balance.amount.value, 20 );\n\n      force_settlement_id_type settle_id7 = result7.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id7(db).balance.amount.value, 21 );\n\n      force_settlement_id_type settle_id8 = result8.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 22 );\n\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37); // 100 - 20 - 21 - 22\n\n      // joe settle\n      const operation_result result101 = force_settle(joe_id(db), bitcny_id(db).amount(100));\n      const operation_result result102 = force_settle(joe_id(db), bitcny_id(db).amount(1000));\n      const operation_result result103 = force_settle(joe_id(db), bitcny_id(db).amount(300));\n\n      force_settlement_id_type settle_id101 = result101.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id101(db).balance.amount.value, 100 );\n\n      force_settlement_id_type settle_id102 = result102.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 1000 );\n\n      force_settlement_id_type settle_id103 = result103.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 );\n\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 100); // 1500 - 100 - 1000 - 300\n\n      generate_block();\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // get to another maintenance interval\n      generate_blocks( db.head_block_time() + fc::hours(22) );\n      set_expiration( db, trx );\n\n      // maximum amount that can be settled now is round_down(807 * 20%) = 161,\n      // settle_id4 will pay 161 usd, will get round_down(161*5/101) = 7 core\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 75 ); // 236 - 161\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n      BOOST_CHECK_EQUAL( settle_id6(db).balance.amount.value, 20 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id7(db).balance.amount.value, 21 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 22 ); // no change since not expired\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 17); // 10 + 7\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20); // no change\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37);\n\n      BOOST_CHECK_EQUAL( 600, call_paul_id(db).debt.value ); // 761 - 161\n      BOOST_CHECK_EQUAL( 83, call_paul_id(db).collateral.value ); // 90 - 7\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 646 ); // 807 - 161\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 161 ); // reset to 0, then 161 more\n\n      // current cny data\n      BOOST_CHECK_EQUAL( settle_id101(db).balance.amount.value, 100 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 1000 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 ); // no change since not expired\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 100); // 1500 - 100 - 1000 - 300\n\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitcny_id(db).dynamic_data(db).current_supply.value, 2000 );\n      BOOST_CHECK_EQUAL( bitcny_id(db).bitasset_data(db).force_settled_volume.value, 0 );\n\n      // bob borrow some\n      const call_order_object& call_bob = *borrow( bob_id(db), bitusd_id(db).amount(19), core_id(db).amount(2) );\n      call_order_id_type call_bob_id = call_bob.id;\n\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 9999998); // 10000000 - 2\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), bitusd_id(db)), 19); // new\n\n      BOOST_CHECK_EQUAL( 19, call_bob_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2, call_bob_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 665 ); // 646 + 19\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 161 );\n\n      generate_block();\n\n      // maximum amount that can be settled now is round_down((665+161) * 20%) = 165,\n      // settle_id4 will pay 165-161=4 usd, will get nothing\n      // bob's call order will get partially settled since its collateral ratio is the lowest\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 71 ); // 75 - 4\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n      BOOST_CHECK_EQUAL( settle_id6(db).balance.amount.value, 20 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id7(db).balance.amount.value, 21 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 22 ); // no change since not expired\n\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 9999998);\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), bitusd_id(db)), 19);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 17); // no change\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20); // no change\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37);\n\n      BOOST_CHECK_EQUAL( 15, call_bob_id(db).debt.value ); // 19 - 4\n      BOOST_CHECK_EQUAL( 2, call_bob_id(db).collateral.value ); // no change\n      BOOST_CHECK_EQUAL( 600, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 83, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 661 ); // 665 - 4\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 165 ); // 161 + 4\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // generate some blocks\n      generate_blocks( db.head_block_time() + fc::hours(10) );\n      set_expiration( db, trx );\n\n      // check cny\n      // maximum amount that can be settled now is round_down(2000 * 20%) = 400,\n      //   settle_id101's remaining amount is 100, so it can be fully processed,\n      //      according to price 50 core / 101 cny, it will get 49 core and pay 100 cny;\n      //   settle_id102's remaining amount is 1000, so 400-100=300 cny will be processed,\n      //      according to price 50 core / 101 cny, it will get 148 core and pay 300 cny;\n      //   settle_id103 won't be processed since it's after settle_id102\n      BOOST_CHECK( !db.find( settle_id101 ) );\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 700 ); // 1000 - 300\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 ); // no change since it's after settle_id102\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 197); // 49 + 148\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 100);\n\n      BOOST_CHECK_EQUAL( 1600, call_jim_id(db).debt.value ); // 2000 - 100 - 300\n      BOOST_CHECK_EQUAL( 1803, call_jim_id(db).collateral.value ); // 2000 - 49 - 148\n\n      BOOST_CHECK_EQUAL( bitcny_id(db).dynamic_data(db).current_supply.value, 1600 );\n      BOOST_CHECK_EQUAL( bitcny_id(db).bitasset_data(db).force_settled_volume.value, 400 ); // 100 + 300\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // get to another maintenance interval\n      generate_blocks( db.head_block_time() + fc::hours(14) );\n      set_expiration( db, trx );\n\n      // maximum amount that can be settled now is round_down(661 * 20%) = 132,\n      //   settle_id4's remaining amount is 71,\n      //      firstly it will pay 15 usd to call_bob and get nothing,\n      //        call_bob will pay off all debt, so it will be closed and remaining collateral (2 core) will be returned;\n      //      then it will pay 71-15=56 usd to call_paul and get round_down(56*5/101) = 2 core;\n      //   settle_id5 (has 5 usd) will pay 5 usd and get nothing;\n      //   settle_id6 (has 20 usd) will pay 20 usd and get nothing;\n      //   settle_id7 (has 21 usd) will pay 21 usd and get 1 core;\n      //   settle_id8 (has 22 usd) will pay 15 usd and get nothing, since reached 132\n      BOOST_CHECK( !db.find( settle_id4 ) );\n      BOOST_CHECK( !db.find( settle_id5 ) );\n      BOOST_CHECK( !db.find( settle_id6 ) );\n      BOOST_CHECK( !db.find( settle_id7 ) );\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 7 ); // 22 - 15\n\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 10000000); // 9999998 + 2\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), bitusd_id(db)), 19);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 19); // 17 + 2\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 20);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 1); // 0 + 1\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37);\n\n      BOOST_CHECK( !db.find( call_bob_id ) );\n      BOOST_CHECK_EQUAL( 483, call_paul_id(db).debt.value ); // 600 - 56 - 5 - 20 - 21 - 15\n      BOOST_CHECK_EQUAL( 80, call_paul_id(db).collateral.value ); // 83 - 2 - 1\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 529 ); // 661 - 132\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 132 ); // reset to 0, then 132 more\n\n      // check cny\n      // maximum amount that can be settled now is round_down(1600 * 20%) = 320,\n      //   settle_id102's remaining amount is 700, so 320 cny will be processed,\n      //      according to price 50 core / 101 cny, it will get 158 core and pay 320 cny;\n      //   settle_id103 won't be processed since it's after settle_id102\n      BOOST_CHECK( !db.find( settle_id101 ) );\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 380 ); // 700 - 320\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 ); // no change since it's after settle_id102\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 355); // 197 + 158\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 100);\n\n      BOOST_CHECK_EQUAL( 1280, call_jim_id(db).debt.value ); // 1600 - 320\n      BOOST_CHECK_EQUAL( 1645, call_jim_id(db).collateral.value ); // 1803 - 158\n\n      BOOST_CHECK_EQUAL( bitcny_id(db).dynamic_data(db).current_supply.value, 1280 );\n      BOOST_CHECK_EQUAL( bitcny_id(db).bitasset_data(db).force_settled_volume.value, 320 ); // reset to 0, then 320\n\n      generate_block();\n\n      // Note: the scenario that a big settle order matching several smaller call orders,\n      //       and another scenario about force_settlement_offset_percent parameter,\n      //       are tested in force_settle_test in operation_test2.cpp.\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( settle_rounding_test_after_hf_184 )\n{\n   try {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_184_TIME - mi);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      set_expiration( db, trx );\n\n      ACTORS((paul)(michael)(rachel)(alice)(bob)(ted)(joe)(jim));\n\n      // create assets\n      const auto& bitusd = create_bitasset(\"USDBIT\", paul_id);\n      const auto& bitcny = create_bitasset(\"CNYBIT\", paul_id);\n      const auto& core   = asset_id_type()(db);\n      asset_id_type bitusd_id = bitusd.id;\n      asset_id_type bitcny_id = bitcny.id;\n      asset_id_type core_id = core.id;\n\n      // fund accounts\n      transfer(committee_account, michael_id, asset( 100000000 ) );\n      transfer(committee_account, paul_id, asset(10000000));\n      transfer(committee_account, alice_id, asset(10000000));\n      transfer(committee_account, bob_id, asset(10000000));\n      transfer(committee_account, jim_id, asset(10000000));\n\n      // add a feed to asset\n      update_feed_producers( bitusd, {paul.id} );\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(5);\n      publish_feed( bitusd, paul, current_feed );\n\n      // paul gets some bitusd\n      const call_order_object& call_paul = *borrow( paul, bitusd.amount(1000), core.amount(100) );\n      call_order_id_type call_paul_id = call_paul.id;\n      BOOST_REQUIRE_EQUAL( get_balance( paul, bitusd ), 1000 );\n\n      // and transfer some to rachel\n      transfer(paul.id, rachel.id, asset(200, bitusd.id));\n\n      BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael, bitusd), 0);\n      BOOST_CHECK_EQUAL(get_balance(michael, core), 100000000);\n\n      // michael gets some bitusd\n      const call_order_object& call_michael = *borrow(michael, bitusd.amount(6), core.amount(8));\n      call_order_id_type call_michael_id = call_michael.id;\n\n      // add settle order and check rounding issue\n      const operation_result result = force_settle(rachel, bitusd.amount(4));\n\n      force_settlement_id_type settle_id = result.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id(db).balance.amount.value, 4 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel, core), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel, bitusd), 196);\n      BOOST_CHECK_EQUAL(get_balance(michael, bitusd), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael, core), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul, core), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul, bitusd), 800);\n\n      BOOST_CHECK_EQUAL( 1000, call_paul.debt.value );\n      BOOST_CHECK_EQUAL( 100, call_paul.collateral.value );\n      BOOST_CHECK_EQUAL( 6, call_michael.debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael.collateral.value );\n\n      generate_blocks( db.head_block_time() + fc::hours(20) );\n      set_expiration( db, trx );\n\n      // default feed and settlement expires at the same time\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      // now yes expire settlement\n      generate_blocks( db.head_block_time() + fc::hours(6) );\n\n      // checks\n      BOOST_CHECK( !db.find( settle_id ) );\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 200); // rachel's settle order is cancelled and he get refunded\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 800);\n\n      BOOST_CHECK_EQUAL( 1000, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 100, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 6, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 1006 ); // 1000 + 6\n\n      // settle more and check rounding issue\n      // by default 20% of total supply can be settled per maintenance interval, here we test less than it\n      set_expiration( db, trx );\n      const operation_result result2 = force_settle(rachel_id(db), bitusd_id(db).amount(34));\n\n      force_settlement_id_type settle_id2 = result2.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id2(db).balance.amount.value, 34 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 166); // 200-34\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 800);\n\n      BOOST_CHECK_EQUAL( 1000, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 100, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 6, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).collateral.value );\n\n      generate_blocks( db.head_block_time() + fc::hours(10) );\n      set_expiration( db, trx );\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      // now yes expire settlement\n      generate_blocks( db.head_block_time() + fc::hours(16) );\n      set_expiration( db, trx );\n\n      // checks\n      BOOST_CHECK( !db.find( settle_id2 ) );\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 1); // rachel got 1 core\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 179); // paid 21 usd since 1 core worths a little more than 20 usd\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999992);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 800);\n\n      BOOST_CHECK_EQUAL( 979, call_paul_id(db).debt.value ); // 1000 - 21\n      BOOST_CHECK_EQUAL( 99, call_paul_id(db).collateral.value ); // 100 - 1\n      BOOST_CHECK_EQUAL( 6, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 985 ); // 1006 - 21\n\n      // prepare for more tests\n      transfer(paul_id, rachel_id, asset(300, bitusd_id));\n      borrow(michael_id(db), bitusd_id(db).amount(2), core_id(db).amount(3));\n\n      // settle even more and check rounding issue\n      // by default 20% of total supply can be settled per maintenance interval, here we test more than it\n      const operation_result result3 = force_settle(rachel_id(db), bitusd_id(db).amount(3));\n      const operation_result result4 = force_settle(rachel_id(db), bitusd_id(db).amount(434));\n      const operation_result result5 = force_settle(rachel_id(db), bitusd_id(db).amount(5));\n\n      force_settlement_id_type settle_id3 = result3.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id3(db).balance.amount.value, 3 );\n\n      force_settlement_id_type settle_id4 = result4.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 434 );\n\n      force_settlement_id_type settle_id5 = result5.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 1);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 37); // 179 + 300 - 3 - 434 - 5\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 8); // 6 + 2\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999989); // 99999992 - 3\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500); // 800 - 300\n\n      BOOST_CHECK_EQUAL( 979, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 99, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).debt.value ); // 6 + 2\n      BOOST_CHECK_EQUAL( 11, call_michael_id(db).collateral.value ); // 8 + 3\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 987 ); // 985 + 2\n\n      generate_blocks( db.head_block_time() + fc::hours(4) );\n      set_expiration( db, trx );\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // now yes expire settlement\n      generate_blocks( db.head_block_time() + fc::hours(22) );\n      set_expiration( db, trx );\n\n      // checks\n      // settle_id3 will be cancelled due to too small.\n      // maximum amount that can be settled now is round_down(987 * 20%) = 197,\n      //   according to price (101/5), the amount worths more than 9 core but less than 10 core, so 9 core will be settled,\n      //   and 9 core worths 181.5 usd, so rachel will pay 182 usd and get 9 core\n      BOOST_CHECK( !db.find( settle_id3 ) );\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 252 ); // 434 - 182\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10); // 1 + 9\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40); // 37 + 3\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 8);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999989);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 797, call_paul_id(db).debt.value ); // 979 - 182\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value ); // 99 - 9\n      BOOST_CHECK_EQUAL( 8, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 11, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 805 ); // 987 - 182\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 182 );\n\n      generate_block();\n\n      // michael borrows more\n      set_expiration( db, trx );\n      borrow(michael_id(db), bitusd_id(db).amount(18), core_id(db).amount(200));\n\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 252 );\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 26); // 8 + 18\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999789); // 99999989 - 200\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 797, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 26, call_michael_id(db).debt.value ); // 8 + 18\n      BOOST_CHECK_EQUAL( 211, call_michael_id(db).collateral.value ); // 11 + 200\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 823 ); // 805 + 18\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 182 );\n\n      generate_block();\n\n      // maximum amount that can be settled now is round_down((823+182) * 20%) = 201,\n      //   already settled 182, so 201 - 182 = 19 more usd can be settled,\n      //   according to price (101/5), the amount worths less than 1 core,\n      //   so nothing will happen.\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 252 );\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 26);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999789);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 797, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 26, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 211, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 823 );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 182 );\n\n      // michael borrows a little more\n      set_expiration( db, trx );\n      borrow(michael_id(db), bitusd_id(db).amount(20), core_id(db).amount(20));\n\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 252 );\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 10);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46); // 26 + 20\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769); // 99999789 - 20\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 797, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 90, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value ); // 26 + 20\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value ); // 211 + 20\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 843 ); // 823 + 20\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 182 );\n\n      generate_block();\n\n      // maximum amount that can be settled now is round_down((843+182) * 20%) = 205,\n      //   already settled 182, so 205 - 182 = 23 more usd can be settled,\n      //   according to price (101/5), the amount worths more than 1 core but less than 2 core,\n      //   so settle order will fill 1 more core, since 1 core worth more than 20 usd but less than 21 usd,\n      //   so rachel will pay 21 usd and get 1 core\n\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 231 ); // 252 - 21\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 11); // 10 + 1\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40); // no change\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 500);\n\n      BOOST_CHECK_EQUAL( 776, call_paul_id(db).debt.value ); // 797 - 21\n      BOOST_CHECK_EQUAL( 89, call_paul_id(db).collateral.value ); // 90 - 1\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 822 ); // 843 - 21\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 203 ); // 182 + 21\n\n      // jim borrow some cny\n      call_order_id_type call_jim_id = borrow(jim_id(db), bitcny_id(db).amount(2000), core_id(db).amount(2000))->id;\n\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 2000);\n\n      // jim transfer some cny to joe\n      transfer(jim_id, joe_id, asset(1500, bitcny_id));\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 1500);\n\n      generate_block();\n\n      // give ted some usd\n      transfer(paul_id, ted_id, asset(100, bitusd_id));\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 100); // new: 100\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400); // 500 - 100\n\n      // ted settle\n      const operation_result result6 = force_settle(ted_id(db), bitusd_id(db).amount(20));\n      const operation_result result7 = force_settle(ted_id(db), bitusd_id(db).amount(21));\n      const operation_result result8 = force_settle(ted_id(db), bitusd_id(db).amount(22));\n\n      force_settlement_id_type settle_id6 = result6.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id6(db).balance.amount.value, 20 );\n\n      force_settlement_id_type settle_id7 = result7.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id7(db).balance.amount.value, 21 );\n\n      force_settlement_id_type settle_id8 = result8.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 22 );\n\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37); // 100 - 20 - 21 - 22\n\n      // joe settle\n      const operation_result result101 = force_settle(joe_id(db), bitcny_id(db).amount(100));\n      const operation_result result102 = force_settle(joe_id(db), bitcny_id(db).amount(1000));\n      const operation_result result103 = force_settle(joe_id(db), bitcny_id(db).amount(300));\n\n      force_settlement_id_type settle_id101 = result101.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id101(db).balance.amount.value, 100 );\n\n      force_settlement_id_type settle_id102 = result102.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 1000 );\n\n      force_settlement_id_type settle_id103 = result103.get<object_id_type>();\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 );\n\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 100); // 1500 - 100 - 1000 - 300\n\n      generate_block();\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // get to another maintenance interval\n      generate_blocks( db.head_block_time() + fc::hours(22) );\n      set_expiration( db, trx );\n\n      // maximum amount that can be settled now is round_down(822 * 20%) = 164,\n      //   according to price (101/5), the amount worths more than 8 core but less than 9 core,\n      //   so settle order will fill 8 more core, since 8 core worth more than 161 usd but less than 162 usd,\n      //   so rachel will pay 162 usd and get 8 core\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 69 ); // 231 - 162\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 ); // no change, since it's after settle_id4\n      BOOST_CHECK_EQUAL( settle_id6(db).balance.amount.value, 20 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id7(db).balance.amount.value, 21 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 22 ); // no change since not expired\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 19); // 11 + 8\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40); // no change\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37);\n\n      BOOST_CHECK_EQUAL( 614, call_paul_id(db).debt.value ); // 776 - 162\n      BOOST_CHECK_EQUAL( 81, call_paul_id(db).collateral.value ); // 89 - 8\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 660 ); // 822 - 162\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 162 ); // reset to 0, then 162 more\n\n      // current cny data\n      BOOST_CHECK_EQUAL( settle_id101(db).balance.amount.value, 100 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 1000 ); // no change since not expired\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 ); // no change since not expired\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 100); // 1500 - 100 - 1000 - 300\n\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2000, call_jim_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitcny_id(db).dynamic_data(db).current_supply.value, 2000 );\n      BOOST_CHECK_EQUAL( bitcny_id(db).bitasset_data(db).force_settled_volume.value, 0 );\n\n      // bob borrow some\n      const call_order_object& call_bob = *borrow( bob_id(db), bitusd_id(db).amount(19), core_id(db).amount(2) );\n      call_order_id_type call_bob_id = call_bob.id;\n\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 9999998); // 10000000 - 2\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), bitusd_id(db)), 19); // new\n\n      BOOST_CHECK_EQUAL( 19, call_bob_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2, call_bob_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 679 ); // 660 + 19\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 162 );\n\n      generate_block();\n\n      // maximum amount that can be settled now is round_down((679+162) * 20%) = 168,\n      //   already settled 162, so 168 - 162 = 6 more usd can be settled,\n      //   according to price (101/5), the amount worths less than 1 core,\n      //   so nothing will happen.\n      BOOST_CHECK_EQUAL( settle_id4(db).balance.amount.value, 69 );\n      BOOST_CHECK_EQUAL( settle_id5(db).balance.amount.value, 5 );\n      BOOST_CHECK_EQUAL( settle_id6(db).balance.amount.value, 20 );\n      BOOST_CHECK_EQUAL( settle_id7(db).balance.amount.value, 21 );\n      BOOST_CHECK_EQUAL( settle_id8(db).balance.amount.value, 22 );\n\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 9999998);\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), bitusd_id(db)), 19);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 19);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 40);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 37);\n\n      BOOST_CHECK_EQUAL( 19, call_bob_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 2, call_bob_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 614, call_paul_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 81, call_paul_id(db).collateral.value );\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 679 );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 162 );\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // generate some blocks\n      generate_blocks( db.head_block_time() + fc::hours(10) );\n      set_expiration( db, trx );\n\n      // check cny\n      // maximum amount that can be settled now is round_down(2000 * 20%) = 400,\n      //   settle_id101's remaining amount is 100, so it can be fully processed,\n      //      according to price 50 core / 101 cny, it will get 49 core and pay 99 cny, the rest (1 cny) will be refunded;\n      //   settle_id102's remaining amount is 1000, so 400-99=301 cny will be processed,\n      //      according to price 50 core / 101 cny, it will get 149 core and pay 301 cny;\n      //   settle_id103 won't be processed since it's after settle_id102\n      BOOST_CHECK( !db.find( settle_id101 ) );\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 699 ); // 1000 - 301\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 ); // no change since it's after settle_id102\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 198); // 49 + 149\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 101); // 100 + 1\n\n      BOOST_CHECK_EQUAL( 1600, call_jim_id(db).debt.value ); // 2000 - 99 - 301\n      BOOST_CHECK_EQUAL( 1802, call_jim_id(db).collateral.value ); // 2000 - 49 - 149\n\n      BOOST_CHECK_EQUAL( bitcny_id(db).dynamic_data(db).current_supply.value, 1600 );\n      BOOST_CHECK_EQUAL( bitcny_id(db).bitasset_data(db).force_settled_volume.value, 400 ); // 99 + 301\n\n      // adding new feed so we have valid price to exit\n      update_feed_producers( bitusd_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 101 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), alice_id(db), current_feed );\n\n      update_feed_producers( bitcny_id(db), {alice_id} );\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitcny_id(db).amount( 101 ) / core_id(db).amount(50);\n      publish_feed( bitcny_id(db), alice_id(db), current_feed );\n\n      // get to another maintenance interval\n      generate_blocks( db.head_block_time() + fc::hours(14) );\n      set_expiration( db, trx );\n\n      // maximum amount that can be settled now is round_down(679 * 20%) = 135,\n      //   settle_id4's remaining amount is 69, so it can be fully processed:\n      //     firstly call_bob will be matched, since it owes only 19 usd which worths less than 1 core,\n      //       it will pay 1 core, and the rest (2-1=1 core) will be returned, short position will be closed;\n      //     then call_paul will be matched,\n      //       according to price (101/5), the amount (69-19=50 usd) worths more than 2 core but less than 3 core,\n      //       so settle_id4 will get 2 more core, since 2 core worth more than 40 usd but less than 41 usd,\n      //       call_rachel will pay 41 usd and get 2 core, the rest (50-41=9 usd) will be returned due to too small.\n      //   settle_id5 (has 5 usd) will be cancelled due to too small;\n      //   settle_id6 (has 20 usd) will be cancelled as well due to too small;\n      //   settle_id7 (has 21 usd) will be filled and get 1 core, since it worths more than 1 core; but no more fund can be returned;\n      //   settle_id8 (has 22 usd) will be filled and get 1 core, and 1 usd will be returned.\n      BOOST_CHECK( !db.find( settle_id4 ) );\n      BOOST_CHECK( !db.find( settle_id5 ) );\n      BOOST_CHECK( !db.find( settle_id6 ) );\n      BOOST_CHECK( !db.find( settle_id7 ) );\n      BOOST_CHECK( !db.find( settle_id8 ) );\n\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), core_id(db)), 9999999); // 9999998 + 1\n      BOOST_CHECK_EQUAL(get_balance(bob_id(db), bitusd_id(db)), 19);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 22); // 19 + 1 + 2\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 54); // 40 + 9 + 5\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 46);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999769);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 400);\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), core_id(db)), 2); // 0 + 1 + 1\n      BOOST_CHECK_EQUAL(get_balance(ted_id(db), bitusd_id(db)), 58); // 37 + 20 + 1\n\n      BOOST_CHECK( !db.find( call_bob_id ) );\n      BOOST_CHECK_EQUAL( 531, call_paul_id(db).debt.value ); // 614 - 41 - 21 - 21\n      BOOST_CHECK_EQUAL( 77, call_paul_id(db).collateral.value ); // 81 - 2 - 1 - 1\n      BOOST_CHECK_EQUAL( 46, call_michael_id(db).debt.value );\n      BOOST_CHECK_EQUAL( 231, call_michael_id(db).collateral.value );\n\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 577 ); // 679 - 19 - 41 - 21 - 21\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).force_settled_volume.value, 102 ); // reset to 0, then 19 + 41 + 21 + 21\n\n      // check cny\n      // maximum amount that can be settled now is round_down(1600 * 20%) = 320,\n      //   settle_id102's remaining amount is 699, so 320 cny will be processed,\n      //      according to price 50 core / 101 cny, it will get 158 core and pay 320 cny;\n      //   settle_id103 won't be processed since it's after settle_id102\n      BOOST_CHECK( !db.find( settle_id101 ) );\n      BOOST_CHECK_EQUAL( settle_id102(db).balance.amount.value, 379 ); // 699 - 320\n      BOOST_CHECK_EQUAL( settle_id103(db).balance.amount.value, 300 ); // no change since it's after settle_id102\n\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), core_id(db)), 9998000);\n      BOOST_CHECK_EQUAL(get_balance(jim_id(db), bitcny_id(db)), 500);\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), core_id(db)), 356); // 198 + 158\n      BOOST_CHECK_EQUAL(get_balance(joe_id(db), bitcny_id(db)), 101);\n\n      BOOST_CHECK_EQUAL( 1280, call_jim_id(db).debt.value ); // 1600 - 320\n      BOOST_CHECK_EQUAL( 1644, call_jim_id(db).collateral.value ); // 1802 - 158\n\n      BOOST_CHECK_EQUAL( bitcny_id(db).dynamic_data(db).current_supply.value, 1280 );\n      BOOST_CHECK_EQUAL( bitcny_id(db).bitasset_data(db).force_settled_volume.value, 320 ); // reset to 0, then 320\n\n      generate_block();\n\n      // Note: the scenario that a big settle order matching several smaller call orders,\n      //       and another scenario about force_settlement_offset_percent parameter,\n      //       are tested in force_settle_test in operation_test2.cpp.\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( global_settle_rounding_test )\n{\n   try {\n      // get around Graphene issue #615 feed expiration bug\n      generate_blocks(HARDFORK_615_TIME);\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((paul)(michael)(rachel)(alice));\n\n      // create assets\n      const auto& bitusd = create_bitasset(\"USDBIT\", paul_id);\n      const auto& core   = asset_id_type()(db);\n      asset_id_type bitusd_id = bitusd.id;\n      asset_id_type core_id = core.id;\n\n      // fund accounts\n      transfer(committee_account, michael_id, asset( 100000000 ) );\n      transfer(committee_account, paul_id,    asset(  10000000 ) );\n      transfer(committee_account, alice_id,   asset(  10000000 ) );\n\n      // allow global settle in bitusd\n      asset_update_operation op;\n      op.issuer = bitusd.issuer;\n      op.asset_to_update = bitusd.id;\n      op.new_options.issuer_permissions = global_settle;\n      op.new_options.flags = bitusd.options.flags;\n      op.new_options.core_exchange_rate = price( asset(1,bitusd_id), asset(1,core_id) );\n      trx.operations.push_back(op);\n      sign(trx, paul_private_key);\n      PUSH_TX(db, trx);\n      generate_block();\n      trx.clear();\n\n      // add a feed to asset\n      update_feed_producers( bitusd_id(db), {paul_id} );\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 100 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), paul_id(db), current_feed );\n\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 10000000);\n\n      // paul gets some bitusd\n      const call_order_object& call_paul = *borrow( paul_id(db), bitusd_id(db).amount(1001), core_id(db).amount(101));\n      call_order_id_type call_paul_id = call_paul.id;\n      BOOST_REQUIRE_EQUAL( get_balance( paul_id(db), bitusd_id(db) ), 1001 );\n      BOOST_REQUIRE_EQUAL( get_balance( paul_id(db), core_id(db) ), 10000000-101);\n\n      // and transfer some to rachel\n      transfer(paul_id, rachel_id, asset(200, bitusd_id));\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999899);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n      // michael borrow some bitusd\n      const call_order_object& call_michael = *borrow(michael_id(db), bitusd_id(db).amount(6), core_id(db).amount(8));\n      call_order_id_type call_michael_id = call_michael.id;\n\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000-8);\n\n      // add global settle\n      force_global_settle(bitusd_id(db), bitusd_id(db).amount(10) / core_id(db).amount(1));\n      generate_block();\n\n      BOOST_CHECK( bitusd_id(db).bitasset_data(db).settlement_price\n                   == price( bitusd_id(db).amount(1007), core_id(db).amount(100) ) );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).settlement_fund.value, 100 ); // 100 from paul, and 0 from michael\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 1007 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000); // michael paid nothing for 6 usd\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900); // paul paid 100 core for 1001 usd\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n      // all call orders are gone after global settle\n      BOOST_CHECK( !db.find_object(call_paul_id) );\n      BOOST_CHECK( !db.find_object(call_michael_id) );\n\n      // add settle order and check rounding issue\n      force_settle(rachel_id(db), bitusd_id(db).amount(4));\n      generate_block();\n\n      BOOST_CHECK( bitusd_id(db).bitasset_data(db).settlement_price\n                   == price( bitusd_id(db).amount(1007), core_id(db).amount(100) ) );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).settlement_fund.value, 100 ); // paid nothing\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 1003 ); // settled 4 usd\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 196); // rachel paid 4 usd and got nothing\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n      // rachel settle more than 1 core\n      force_settle(rachel_id(db), bitusd_id(db).amount(13));\n      generate_block();\n\n      BOOST_CHECK( bitusd_id(db).bitasset_data(db).settlement_price\n                   == price( bitusd_id(db).amount(1007), core_id(db).amount(100) ) );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).settlement_fund.value, 99 ); // paid 1 core\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 990 ); // settled 13 usd\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 1);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 183); // rachel paid 13 usd and got 1 core\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999900);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( global_settle_rounding_test_after_hf_184 )\n{\n   try {\n      auto mi = db.get_global_properties().parameters.maintenance_interval;\n      generate_blocks(HARDFORK_CORE_184_TIME - mi); // assume that hard fork core-184 and core-342 happen at same time\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      set_expiration( db, trx );\n\n      ACTORS((paul)(michael)(rachel)(alice));\n\n      // create assets\n      const auto& bitusd = create_bitasset(\"USDBIT\", paul_id);\n      const auto& core   = asset_id_type()(db);\n      asset_id_type bitusd_id = bitusd.id;\n      asset_id_type core_id = core.id;\n\n      // fund accounts\n      transfer(committee_account, michael_id, asset( 100000000 ) );\n      transfer(committee_account, paul_id,    asset(  10000000 ) );\n      transfer(committee_account, alice_id,   asset(  10000000 ) );\n\n      // allow global settle in bitusd\n      asset_update_operation op;\n      op.issuer = bitusd_id(db).issuer;\n      op.asset_to_update = bitusd_id;\n      op.new_options.issuer_permissions = global_settle;\n      op.new_options.flags = bitusd.options.flags;\n      op.new_options.core_exchange_rate = price( asset(1,bitusd_id), asset(1,core_id) );\n      trx.operations.push_back(op);\n      sign(trx, paul_private_key);\n      PUSH_TX(db, trx);\n      generate_block();\n      trx.clear();\n\n      // add a feed to asset\n      update_feed_producers( bitusd_id(db), {paul_id} );\n      price_feed current_feed;\n      current_feed.maintenance_collateral_ratio = 1750;\n      current_feed.maximum_short_squeeze_ratio = 1100;\n      current_feed.settlement_price = bitusd_id(db).amount( 100 ) / core_id(db).amount(5);\n      publish_feed( bitusd_id(db), paul_id(db), current_feed );\n\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 10000000);\n\n      // paul gets some bitusd\n      const call_order_object& call_paul = *borrow( paul_id(db), bitusd_id(db).amount(1001), core_id(db).amount(101));\n      call_order_id_type call_paul_id = call_paul.id;\n      BOOST_REQUIRE_EQUAL( get_balance( paul_id(db), bitusd_id(db) ), 1001 );\n      BOOST_REQUIRE_EQUAL( get_balance( paul_id(db), core_id(db) ), 10000000-101);\n\n      // and transfer some to rachel\n      transfer(paul_id, rachel_id, asset(200, bitusd_id));\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999899);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n      // michael borrow some bitusd\n      const call_order_object& call_michael = *borrow(michael_id(db), bitusd_id(db).amount(6), core_id(db).amount(8));\n      call_order_id_type call_michael_id = call_michael.id;\n\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 100000000-8);\n\n      // add global settle\n      force_global_settle(bitusd_id(db), bitusd_id(db).amount(10) / core_id(db).amount(1));\n      generate_block();\n\n      BOOST_CHECK( bitusd_id(db).bitasset_data(db).settlement_price\n                   == price( bitusd_id(db).amount(1007), core_id(db).amount(102) ) );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).settlement_fund.value, 102 ); // 101 from paul, and 1 from michael\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 1007 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999999); // michael paid 1 core for 6 usd\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999899); // paul paid 101 core for 1001 usd\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n      // all call orders are gone after global settle\n      BOOST_CHECK( !db.find_object(call_paul_id));\n      BOOST_CHECK( !db.find_object(call_michael_id));\n\n      // settle order will not execute after HF due to too small\n      GRAPHENE_REQUIRE_THROW( force_settle(rachel_id(db), bitusd_id(db).amount(4)), fc::exception );\n\n      generate_block();\n\n      // balances unchanged\n      BOOST_CHECK( bitusd_id(db).bitasset_data(db).settlement_price\n                   == price( bitusd_id(db).amount(1007), core_id(db).amount(102) ) );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).settlement_fund.value, 102 );\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 1007 );\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 0);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 200);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999999);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999899);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n      // rachel settle more than 1 core\n      force_settle(rachel_id(db), bitusd_id(db).amount(13));\n      generate_block();\n\n      BOOST_CHECK( bitusd_id(db).bitasset_data(db).settlement_price\n                   == price( bitusd_id(db).amount(1007), core_id(db).amount(102) ) );\n      BOOST_CHECK_EQUAL( bitusd_id(db).bitasset_data(db).settlement_fund.value, 101 ); // paid 1 core\n      BOOST_CHECK_EQUAL( bitusd_id(db).dynamic_data(db).current_supply.value, 997 ); // settled 10 usd\n\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), core_id(db)), 1);\n      BOOST_CHECK_EQUAL(get_balance(rachel_id(db), bitusd_id(db)), 190); // rachel paid 10 usd and got 1 core, 3 usd returned\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), bitusd_id(db)), 6);\n      BOOST_CHECK_EQUAL(get_balance(michael_id(db), core_id(db)), 99999999);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), core_id(db)), 9999899);\n      BOOST_CHECK_EQUAL(get_balance(paul_id(db), bitusd_id(db)), 801);\n\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( create_bitassets )\n{\n   try {\n\n      generate_blocks( HARDFORK_480_TIME ); // avoid being affected by the price feed bug\n      generate_block();\n      set_expiration( db, trx );\n\n      ACTORS((paul)(rachelregistrar)(rachelreferrer));\n\n      upgrade_to_lifetime_member(rachelregistrar);\n      upgrade_to_lifetime_member(rachelreferrer);\n\n      constexpr auto market_fee_percent      = 50 * GRAPHENE_1_PERCENT;\n      constexpr auto biteur_reward_percent   = 90 * GRAPHENE_1_PERCENT;\n      constexpr auto referrer_reward_percent = 10 * GRAPHENE_1_PERCENT;\n\n      const auto& biteur = create_bitasset( \"EURBIT\", paul_id, market_fee_percent, charge_market_fee, 2 );\n      asset_id_type biteur_id = biteur.id;\n\n      const auto& bitusd = create_bitasset( \"USDBIT\", paul_id, market_fee_percent, charge_market_fee, 2, biteur_id );\n\n      const account_object rachel  = create_account( \"rachel\", rachelregistrar, rachelreferrer,\n                                                     referrer_reward_percent );\n\n      transfer( committee_account, rachelregistrar_id, asset( 10000000 ) );\n      transfer( committee_account, rachelreferrer_id, asset( 10000000 ) );\n      transfer( committee_account, rachel.get_id(), asset( 10000000) );\n      transfer( committee_account, paul_id, asset( 10000000000 ) );\n\n      asset_update_operation op;\n      op.issuer = biteur.issuer;\n      op.asset_to_update = biteur_id;\n      op.new_options.issuer_permissions = charge_market_fee;\n      op.new_options.extensions.value.reward_percent = biteur_reward_percent;\n      op.new_options.flags = bitusd.options.flags | charge_market_fee;\n      op.new_options.core_exchange_rate = price( asset(20,biteur_id), asset(1,asset_id_type()) );\n      op.new_options.market_fee_percent = market_fee_percent;\n      trx.operations.push_back(op);\n      sign(trx, paul_private_key);\n      PUSH_TX(db, trx);\n      generate_block();\n      trx.clear();\n      set_expiration( db, trx );\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( market_fee_of_settle_order_before_hardfork_1780 )\n{\n   try {\n      INVOKE(create_bitassets);\n\n      GET_ACTOR(paul);\n      GET_ACTOR(rachel);\n      GET_ACTOR(rachelregistrar);\n      GET_ACTOR(rachelreferrer);\n\n      const asset_object &biteur = get_asset( \"EURBIT\" );\n      asset_id_type biteur_id = biteur.id;\n      const asset_object &bitusd = get_asset( \"USDBIT\" );\n      asset_id_type bitusd_id = bitusd.id;\n\n      const auto& core = asset_id_type()(db);\n\n      {// add a feed to asset bitusd\n         update_feed_producers( bitusd, {paul_id} );\n         price_feed feed;\n         feed.settlement_price = price( bitusd.amount(100), biteur.amount(5) );\n         feed.core_exchange_rate = price( bitusd.amount(100), asset(1) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( bitusd_id, paul_id, feed );\n      }\n\n      {// add a feed to asset biteur\n         update_feed_producers( biteur, {paul_id} );\n         price_feed feed;\n         feed.settlement_price = price( biteur.amount(100), core.amount(5) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( biteur_id, paul_id, feed );\n      }\n\n      enable_fees();\n\n      // paul gets some bitusd and biteur\n      borrow( paul_id, biteur.amount(20000), core.amount(2000) );\n      borrow( paul_id, bitusd.amount(10000), biteur.amount(1000) );\n\n      // and transfer some bitusd to rachel\n      constexpr auto rachel_bitusd_count = 1000;\n      transfer( paul_id, rachel_id, asset(rachel_bitusd_count, bitusd_id) );\n\n      force_settle( rachel, bitusd.amount(rachel_bitusd_count) );\n      generate_block();\n      generate_blocks( db.head_block_time() + fc::hours(24) );\n      set_expiration( db, trx );\n\n      // Check results\n      int64_t biteur_balance = 0;\n      int64_t biteur_accumulated_fee = 0;\n      int64_t bitusd_accumulated_fee = 0;\n      {\n         // 1 biteur = 20 bitusd see publish_feed\n         const auto biteur_expected_result = rachel_bitusd_count/20;\n         const auto biteur_market_fee = biteur_expected_result / 2; // market fee percent = 50%\n         biteur_balance += biteur_expected_result - biteur_market_fee;\n\n         BOOST_CHECK_EQUAL( get_balance(rachel, biteur), biteur_balance );\n         BOOST_CHECK_EQUAL( get_balance(rachel, bitusd), 0 );\n\n         const auto rachelregistrar_reward = get_market_fee_reward( rachelregistrar, biteur );\n         const auto rachelreferrer_reward = get_market_fee_reward( rachelreferrer, biteur );\n\n         BOOST_CHECK_EQUAL( rachelregistrar_reward, 0 );\n         BOOST_CHECK_EQUAL( rachelreferrer_reward, 0 );\n\n         // market fee\n         biteur_accumulated_fee += biteur_market_fee;\n         bitusd_accumulated_fee += 0; // usd market fee percent 50%, but call orders don't pay\n         BOOST_CHECK_EQUAL( biteur.dynamic_data(db).accumulated_fees.value, biteur_accumulated_fee);\n         BOOST_CHECK_EQUAL( bitusd.dynamic_data(db).accumulated_fees.value, bitusd_accumulated_fee );\n      }\n\n      // Update the feed to asset bitusd to trigger a global settlement\n      {\n         price_feed feed;\n         feed.settlement_price = price( bitusd.amount(10), biteur.amount(5) );\n         feed.core_exchange_rate = price( bitusd.amount(100), asset(1) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( bitusd_id, paul_id, feed );\n      }\n\n      // Transfer more bitusd to rachel\n      transfer( paul_id, rachel_id, asset(rachel_bitusd_count, bitusd_id) );\n      // Instant settlement\n      force_settle( rachel, bitusd.amount(rachel_bitusd_count) );\n\n      // Check results\n      {\n         // 950 biteur = 9000 bitusd in settlement fund\n         const auto biteur_expected_result = rachel_bitusd_count * 950 / 9000;\n         const auto biteur_market_fee = 0; // market fee percent = 50%, but doesn't pay before hf\n         biteur_balance += biteur_expected_result - biteur_market_fee;\n\n         BOOST_CHECK_EQUAL( get_balance(rachel, biteur), biteur_balance );\n         BOOST_CHECK_EQUAL( get_balance(rachel, bitusd), 0 );\n\n         const auto rachelregistrar_reward = get_market_fee_reward( rachelregistrar, biteur );\n         const auto rachelreferrer_reward = get_market_fee_reward( rachelreferrer, biteur );\n\n         BOOST_CHECK_EQUAL( rachelregistrar_reward, 0 );\n         BOOST_CHECK_EQUAL( rachelreferrer_reward, 0 );\n\n         // No market fee for instant settlement before hf\n         biteur_accumulated_fee += 0;\n         bitusd_accumulated_fee += 0;\n         BOOST_CHECK_EQUAL( biteur.dynamic_data(db).accumulated_fees.value, biteur_accumulated_fee);\n         BOOST_CHECK_EQUAL( bitusd.dynamic_data(db).accumulated_fees.value, bitusd_accumulated_fee );\n      }\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( market_fee_of_settle_order_after_hardfork_1780 )\n{\n   try {\n      INVOKE(create_bitassets);\n\n      generate_blocks( HARDFORK_CORE_1780_TIME );\n      set_expiration( db, trx );\n\n      GET_ACTOR(paul);\n      GET_ACTOR(rachel);\n      GET_ACTOR(rachelregistrar);\n      GET_ACTOR(rachelreferrer);\n\n      const asset_object &biteur = get_asset( \"EURBIT\" );\n      asset_id_type biteur_id = biteur.id;\n      const asset_object &bitusd = get_asset( \"USDBIT\" );\n      asset_id_type bitusd_id = bitusd.id;\n\n      const auto& core = asset_id_type()(db);\n\n      {// add a feed to asset bitusd\n         update_feed_producers( bitusd, {paul_id} );\n         price_feed feed;\n         feed.settlement_price = price( bitusd.amount(100), biteur.amount(5) );\n         feed.core_exchange_rate = price( bitusd.amount(100), asset(1) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( bitusd_id, paul_id, feed );\n      }\n\n      {// add a feed to asset biteur\n         update_feed_producers( biteur, {paul_id} );\n         price_feed feed;\n         feed.settlement_price = price( biteur.amount(100), core.amount(5) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( biteur_id, paul_id, feed );\n      }\n\n      enable_fees();\n\n      // paul gets some bitusd and biteur\n      borrow( paul_id, biteur.amount(20000), core.amount(2000) );\n      borrow( paul_id, bitusd.amount(10000), biteur.amount(1000) );\n\n      // and transfer some bitusd to rachel\n      constexpr auto rachel_bitusd_count = 1000;\n      transfer( paul_id, rachel_id, asset(rachel_bitusd_count, bitusd_id) );\n\n      force_settle( rachel, bitusd.amount(rachel_bitusd_count) );\n      generate_block();\n      generate_blocks( db.head_block_time() + fc::hours(24) );\n      set_expiration( db, trx );\n\n      // Check results\n      int64_t biteur_balance = 0;\n      int64_t biteur_accumulated_fee = 0;\n      int64_t bitusd_accumulated_fee = 0;\n      {\n         // 1 biteur = 20 bitusd see publish_feed\n         const auto biteur_expected_result = rachel_bitusd_count/20;\n         const auto biteur_market_fee = biteur_expected_result / 2; // market fee percent = 50%\n         biteur_balance += biteur_expected_result - biteur_market_fee;\n\n         BOOST_CHECK_EQUAL( get_balance(rachel, biteur), biteur_balance );\n         BOOST_CHECK_EQUAL( get_balance(rachel, bitusd), 0 );\n\n         const auto rachelregistrar_reward = get_market_fee_reward( rachelregistrar, biteur );\n         const auto rachelreferrer_reward = get_market_fee_reward( rachelreferrer, biteur );\n\n         const auto biteur_reward = biteur_market_fee * 9 / 10; // 90%\n         const auto referrer_reward = biteur_reward / 10; // 10%\n         const auto registrar_reward = biteur_reward - referrer_reward;\n\n         BOOST_CHECK_EQUAL( rachelregistrar_reward, registrar_reward  );\n         BOOST_CHECK_EQUAL( rachelreferrer_reward, referrer_reward );\n\n         // market fee\n         biteur_accumulated_fee += biteur_market_fee - biteur_reward;\n         bitusd_accumulated_fee += 0; // usd market fee percent 50%, but call orders don't pay\n         BOOST_CHECK_EQUAL( biteur.dynamic_data(db).accumulated_fees.value, biteur_accumulated_fee);\n         BOOST_CHECK_EQUAL( bitusd.dynamic_data(db).accumulated_fees.value, bitusd_accumulated_fee );\n\n      }\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( market_fee_of_instant_settle_order_after_hardfork_1780 )\n{\n   try {\n      INVOKE(create_bitassets);\n\n      generate_blocks( HARDFORK_CORE_1780_TIME );\n      set_expiration( db, trx );\n\n      GET_ACTOR(paul);\n      GET_ACTOR(rachel);\n      GET_ACTOR(rachelregistrar);\n      GET_ACTOR(rachelreferrer);\n\n      const asset_object &biteur = get_asset( \"EURBIT\" );\n      asset_id_type biteur_id = biteur.id;\n      const asset_object &bitusd = get_asset( \"USDBIT\" );\n      asset_id_type bitusd_id = bitusd.id;\n\n      const auto& core = asset_id_type()(db);\n\n      {// add a feed to asset bitusd\n         update_feed_producers( bitusd, {paul_id} );\n         price_feed feed;\n         feed.settlement_price = price( bitusd.amount(100), biteur.amount(5) );\n         feed.core_exchange_rate = price( bitusd.amount(100), asset(1) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( bitusd_id, paul_id, feed );\n      }\n\n      {// add a feed to asset biteur\n         update_feed_producers( biteur, {paul_id} );\n         price_feed feed;\n         feed.settlement_price = price( biteur.amount(100), core.amount(5) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( biteur_id, paul_id, feed );\n      }\n\n      enable_fees();\n\n      // paul gets some bitusd and biteur\n      borrow( paul_id, biteur.amount(20000), core.amount(2000) );\n      borrow( paul_id, bitusd.amount(10000), biteur.amount(1000) );\n\n      // Update the feed to asset bitusd to trigger a global settlement\n      {\n         price_feed feed;\n         feed.settlement_price = price( bitusd.amount(10), biteur.amount(5) );\n         feed.core_exchange_rate = price( bitusd.amount(100), asset(1) );\n         feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         feed.maximum_short_squeeze_ratio = 110 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;\n         publish_feed( bitusd_id, paul_id, feed );\n      }\n\n      // Transfer some bitusd to rachel\n      constexpr auto rachel_bitusd_count = 1000;\n      transfer( paul_id, rachel_id, asset(rachel_bitusd_count, bitusd_id) );\n      // Instant settlement\n      force_settle( rachel, bitusd.amount(rachel_bitusd_count) ); // instant settlement\n\n      // Check results\n      int64_t biteur_balance = 0;\n      int64_t biteur_accumulated_fee = 0;\n      int64_t bitusd_accumulated_fee = 0;\n      {\n         // 1000 biteur = 10000 bitusd in settlement fund\n         const auto biteur_expected_result = rachel_bitusd_count/10;\n         const auto biteur_market_fee = biteur_expected_result / 2; // market fee percent = 50%\n         biteur_balance += biteur_expected_result - biteur_market_fee;\n\n         BOOST_CHECK_EQUAL( get_balance(rachel, biteur), biteur_balance );\n         BOOST_CHECK_EQUAL( get_balance(rachel, bitusd), 0 );\n\n         const auto rachelregistrar_reward = get_market_fee_reward( rachelregistrar, biteur );\n         const auto rachelreferrer_reward = get_market_fee_reward( rachelreferrer, biteur );\n\n         const auto biteur_reward = biteur_market_fee * 9 / 10; // 90%\n         const auto referrer_reward = biteur_reward / 10; // 10%\n         const auto registrar_reward = biteur_reward - referrer_reward;\n\n         BOOST_CHECK_EQUAL( rachelregistrar_reward, registrar_reward  );\n         BOOST_CHECK_EQUAL( rachelreferrer_reward, referrer_reward );\n\n         // market fee\n         biteur_accumulated_fee += biteur_market_fee - biteur_reward;\n         bitusd_accumulated_fee += 0; // usd market fee percent 50%, but call orders don't pay\n         BOOST_CHECK_EQUAL( biteur.dynamic_data(db).accumulated_fees.value, biteur_accumulated_fee);\n         BOOST_CHECK_EQUAL( bitusd.dynamic_data(db).accumulated_fees.value, bitusd_accumulated_fee );\n\n      }\n\n   } FC_LOG_AND_RETHROW()\n}\n\n/**\n * Test case to reproduce https://github.com/bitshares/bitshares-core/issues/1883.\n * When there is only one fill_order object in the ticker rolling buffer, it should only be rolled out once.\n */\nBOOST_AUTO_TEST_CASE( global_settle_ticker_test )\n{\n   try {\n      generate_block();\n\n      const auto& meta_idx = db.get_index_type<simple_index<graphene::market_history::market_ticker_meta_object>>();\n      const auto& ticker_idx = db.get_index_type<graphene::market_history::market_ticker_index>().indices();\n      const auto& history_idx = db.get_index_type<graphene::market_history::history_index>().indices();\n\n      BOOST_CHECK_EQUAL( meta_idx.size(), 0 );\n      BOOST_CHECK_EQUAL( ticker_idx.size(), 0 );\n      BOOST_CHECK_EQUAL( history_idx.size(), 0 );\n\n      ACTORS((judge)(alice));\n\n      const auto& pmark = create_prediction_market(\"PMARK\", judge_id);\n      const auto& core  = asset_id_type()(db);\n\n      int64_t init_balance(1000000);\n      transfer(committee_account, judge_id, asset(init_balance));\n      transfer(committee_account, alice_id, asset(init_balance));\n\n      BOOST_TEST_MESSAGE( \"Open position with equal collateral\" );\n      borrow( alice, pmark.amount(1000), asset(1000) );\n\n      BOOST_TEST_MESSAGE( \"Globally settling\" );\n      force_global_settle( pmark, pmark.amount(1) / core.amount(1) );\n\n      generate_block();\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      {\n         BOOST_CHECK_EQUAL( meta_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( ticker_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( history_idx.size(), 1 );\n\n         const auto& meta = *meta_idx.begin();\n         const auto& tick = *ticker_idx.begin();\n         const auto& hist = *history_idx.begin();\n\n         BOOST_CHECK( meta.rolling_min_order_his_id == hist.id );\n         BOOST_CHECK( meta.skip_min_order_his_id == false );\n\n         BOOST_CHECK( tick.base_volume == 1000 );\n         BOOST_CHECK( tick.quote_volume == 1000 );\n      }\n\n      generate_blocks( db.head_block_time() + 86000 ); // less than a day\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      // nothing changes\n      {\n         BOOST_CHECK_EQUAL( meta_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( ticker_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( history_idx.size(), 1 );\n\n         const auto& meta = *meta_idx.begin();\n         const auto& tick = *ticker_idx.begin();\n         const auto& hist = *history_idx.begin();\n\n         BOOST_CHECK( meta.rolling_min_order_his_id == hist.id );\n         BOOST_CHECK( meta.skip_min_order_his_id == false );\n\n         BOOST_CHECK( tick.base_volume == 1000 );\n         BOOST_CHECK( tick.quote_volume == 1000 );\n      }\n\n      generate_blocks( db.head_block_time() + 4000 ); // now more than 24 hours\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      // the history is rolled out, new 24h volume should be 0\n      {\n         BOOST_CHECK_EQUAL( meta_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( ticker_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( history_idx.size(), 1 );\n\n         const auto& meta = *meta_idx.begin();\n         const auto& tick = *ticker_idx.begin();\n         const auto& hist = *history_idx.begin();\n\n         BOOST_CHECK( meta.rolling_min_order_his_id == hist.id );\n         BOOST_CHECK( meta.skip_min_order_his_id == true ); // the order should be skipped on next roll\n\n         BOOST_CHECK( tick.base_volume == 0 );\n         BOOST_CHECK( tick.quote_volume == 0 );\n      }\n\n      generate_block();\n      fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread\n\n      // nothing changes\n      {\n         BOOST_CHECK_EQUAL( meta_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( ticker_idx.size(), 1 );\n         BOOST_CHECK_EQUAL( history_idx.size(), 1 );\n\n         const auto& meta = *meta_idx.begin();\n         const auto& tick = *ticker_idx.begin();\n         const auto& hist = *history_idx.begin();\n\n         BOOST_CHECK( meta.rolling_min_order_his_id == hist.id );\n         BOOST_CHECK( meta.skip_min_order_his_id == true );\n\n         BOOST_CHECK( tick.base_volume == 0 );\n         BOOST_CHECK( tick.quote_volume == 0 );\n      }\n\n\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/simple_maker_taker_fee_tests.cpp",
    "content": "/*\n * Copyright (c) 2020 Michel Santos, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <string>\n#include <boost/test/unit_test.hpp>\n#include <fc/exception/exception.hpp>\n\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nstruct simple_maker_taker_database_fixture : database_fixture {\n   simple_maker_taker_database_fixture()\n           : database_fixture() {\n   }\n\n   const limit_order_create_operation\n   create_sell_operation(account_id_type user, const asset &amount, const asset &recv) {\n      const time_point_sec order_expiration = time_point_sec::maximum();\n      const price &fee_core_exchange_rate = price::unit_price();\n      limit_order_create_operation op = create_sell_operation(user, amount, recv, order_expiration,\n                                                              fee_core_exchange_rate);\n      return op;\n   }\n\n   const limit_order_create_operation\n   create_sell_operation(account_id_type user, const asset &amount, const asset &recv,\n                         const time_point_sec order_expiration,\n                         const price &fee_core_exchange_rate) {\n      limit_order_create_operation op = create_sell_operation(user(db), amount, recv, order_expiration,\n                                                              fee_core_exchange_rate);\n      return op;\n   }\n\n   const limit_order_create_operation\n   create_sell_operation(const account_object &user, const asset &amount, const asset &recv,\n                         const time_point_sec order_expiration,\n                         const price &fee_core_exchange_rate) {\n      limit_order_create_operation sell_order;\n      sell_order.seller = user.id;\n      sell_order.amount_to_sell = amount;\n      sell_order.min_to_receive = recv;\n      sell_order.expiration = order_expiration;\n\n      return sell_order;\n   }\n\n   const asset_create_operation create_user_issued_asset_operation(const string &name, const account_object &issuer,\n                                                                   uint16_t flags, const price &core_exchange_rate,\n                                                                   uint8_t precision, uint16_t maker_fee_percent,\n                                                                   uint16_t taker_fee_percent) {\n      asset_create_operation creator;\n      creator.issuer = issuer.id;\n      creator.fee = asset();\n      creator.symbol = name;\n      creator.common_options.max_supply = 0;\n      creator.precision = precision;\n\n      creator.common_options.core_exchange_rate = core_exchange_rate;\n      creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;\n      creator.common_options.flags = flags;\n      creator.common_options.issuer_permissions = flags;\n      creator.common_options.market_fee_percent = maker_fee_percent;\n      creator.common_options.extensions.value.taker_fee_percent = taker_fee_percent;\n\n      return creator;\n\n   }\n};\n\n\n/**\n * BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders\n */\nBOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_database_fixture)\n\n   /**\n    * Test of setting taker fee before HF and after HF for a UIA\n    */\n   BOOST_AUTO_TEST_CASE(setting_taker_fees_uia) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy));\n         account_id_type issuer_id = jill.id;\n         fc::ecc::private_key issuer_private_key = jill_private_key;\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n         uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT;\n         const asset_object jillcoin = create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                market_fee_percent);\n\n         //////\n         // Before HF, test inability to set taker fees\n         //////\n         asset_update_operation uop;\n         uop.issuer = issuer_id;\n         uop.asset_to_update = jillcoin.get_id();\n         uop.new_options = jillcoin.options;\n         uint16_t new_taker_fee_percent = uop.new_options.market_fee_percent / 2;\n         uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent;\n\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, issuer_private_key);\n         GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception);\n         // TODO: Check the specific exception?\n\n         // Check the taker fee\n         asset_object updated_asset = jillcoin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n\n         //////\n         // Before HF, test inability to set taker fees with an asset update operation inside of a proposal\n         //////\n         {\n            trx.clear();\n            set_expiration(db, trx);\n\n            uint64_t alternate_taker_fee_percent = new_taker_fee_percent * 2;\n            uop.new_options.extensions.value.taker_fee_percent = alternate_taker_fee_percent;\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.operations.push_back(cop);\n            // sign(trx, issuer_private_key);\n            GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception);\n\n            // Check the taker fee is not changed because the proposal has not been approved\n            updated_asset = jillcoin.get_id()(db);\n            BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n         }\n\n\n         //////\n         // Before HF, test inability to set taker fees with an asset create operation inside of a proposal\n         //////\n         {\n            trx.clear();\n            set_expiration(db, trx);\n\n            uint64_t maker_fee_percent = 10 * GRAPHENE_1_PERCENT;\n            uint64_t taker_fee_percent = 2 * GRAPHENE_1_PERCENT;\n            asset_create_operation ac_op = create_user_issued_asset_operation(\"JCOIN2\", jill, charge_market_fee, price,\n                                                                              2,\n                                                                              maker_fee_percent, taker_fee_percent);\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(ac_op);\n\n            trx.operations.push_back(cop);\n            // sign(trx, issuer_private_key);\n\n            GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // The proposal should be rejected\n\n         }\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test default values of taker fee after HF\n         // After the HF its default value should still not be set\n         //////\n         updated_asset = jillcoin.get_id()(db);\n         uint16_t expected_taker_fee_percent = updated_asset.options.market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n\n         //////\n         // After HF, test invalid taker fees\n         //////\n         uop.new_options.extensions.value.taker_fee_percent = GRAPHENE_100_PERCENT + 1;\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, issuer_private_key);\n         GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason\n         // TODO: Check the specific exception?\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent;\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, issuer_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee\n         updated_asset = jillcoin.get_id()(db);\n         expected_taker_fee_percent = new_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n\n         //////\n         // After HF, test ability to set taker fees with an asset update operation inside of a proposal\n         //////\n         {\n            trx.clear();\n            set_expiration(db, trx);\n\n            uint64_t alternate_taker_fee_percent = new_taker_fee_percent * 2;\n            uop.new_options.extensions.value.taker_fee_percent = alternate_taker_fee_percent;\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(uop);\n\n            trx.operations.push_back(cop);\n            // sign(trx, issuer_private_key);\n            processed_transaction processed = PUSH_TX(db, trx); // No exception should be thrown\n\n            // Check the taker fee is not changed because the proposal has not been approved\n            updated_asset = jillcoin.get_id()(db);\n            expected_taker_fee_percent = new_taker_fee_percent;\n            BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n            BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n\n            // Approve the proposal\n            trx.clear();\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = jill.id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(jill.id);\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, jill_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n            // Advance to after proposal expires\n            generate_blocks(cop.expiration_time);\n\n            // Check the taker fee is not changed because the proposal has not been approved\n            updated_asset = jillcoin.get_id()(db);\n            expected_taker_fee_percent = alternate_taker_fee_percent;\n            BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n            BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         }\n\n\n         //////\n         // After HF, test ability to set taker fees with an asset create operation inside of a proposal\n         //////\n         {\n            trx.clear();\n            set_expiration(db, trx);\n\n            uint64_t maker_fee_percent = 10 * GRAPHENE_1_PERCENT;\n            uint64_t taker_fee_percent = 2 * GRAPHENE_1_PERCENT;\n            asset_create_operation ac_op = create_user_issued_asset_operation(\"JCOIN2\", jill, charge_market_fee, price,\n                                                                              2,\n                                                                              maker_fee_percent, taker_fee_percent);\n\n            proposal_create_operation cop;\n            cop.review_period_seconds = 86400;\n            uint32_t buffer_seconds = 60 * 60;\n            cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds;\n            cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;\n            cop.proposed_ops.emplace_back(ac_op);\n\n            trx.operations.push_back(cop);\n            // sign(trx, issuer_private_key);\n\n            processed_transaction processed = PUSH_TX(db, trx); // No exception should be thrown\n\n            // Check the asset does not exist because the proposal has not been approved\n            const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_symbol>();\n            const auto itr = asset_idx.find(\"JCOIN2\");\n            BOOST_CHECK(itr == asset_idx.end());\n\n            // Approve the proposal\n            trx.clear();\n            proposal_id_type pid = processed.operation_results[0].get<object_id_type>();\n\n            proposal_update_operation pup;\n            pup.fee_paying_account = jill.id;\n            pup.proposal = pid;\n            pup.active_approvals_to_add.insert(jill.id);\n            trx.operations.push_back(pup);\n            set_expiration(db, trx);\n            sign(trx, jill_private_key);\n\n            PUSH_TX(db, trx); // No exception should be thrown\n\n            // Advance to after proposal expires\n            generate_blocks(cop.expiration_time);\n\n            // Check the taker fee is not changed because the proposal has not been approved\n            BOOST_CHECK(asset_idx.find(\"JCOIN2\") != asset_idx.end());\n            updated_asset = *asset_idx.find(\"JCOIN2\");\n            expected_taker_fee_percent = taker_fee_percent;\n            BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n            BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n            uint16_t expected_maker_fee_percent = maker_fee_percent;\n            BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent);\n\n         }\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of setting taker fee before HF and after HF for a smart asset\n    */\n   BOOST_AUTO_TEST_CASE(setting_taker_fees_smart_asset) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         create_bitasset(\"SMARTBIT\", smartissuer.id);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object &bitsmart = get_asset(\"SMARTBIT\");\n\n         generate_blocks(HARDFORK_615_TIME); // get around Graphene issue #615 feed expiration bug\n         generate_block();\n\n\n         //////\n         // Before HF, test inability to set taker fees\n         //////\n         asset_update_operation uop;\n         uop.issuer = smartissuer.id;\n         uop.asset_to_update = bitsmart.get_id();\n         uop.new_options = bitsmart.options;\n         uint16_t new_taker_fee_percent = uop.new_options.market_fee_percent / 2;\n         uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent;\n\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, smartissuer_private_key);\n         GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason\n         // TODO: Check the specific exception?\n\n         // Check the taker fee\n         asset_object updated_asset = bitsmart.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test default values of taker fee after HF\n         // After the HF its default value should still not be set\n         //////\n         updated_asset = bitsmart.get_id()(db);\n         uint16_t expected_taker_fee_percent = updated_asset.options.market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n\n         //////\n         // After HF, test invalid taker fees\n         //////\n         uop.new_options.extensions.value.taker_fee_percent = GRAPHENE_100_PERCENT + 1;\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, smartissuer_private_key);\n         GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason\n         // TODO: Check the specific exception?\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent;\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee\n         updated_asset = bitsmart.get_id()(db);\n         expected_taker_fee_percent = new_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test the default taker fee values of multiple different assets after HF\n    */\n   BOOST_AUTO_TEST_CASE(default_taker_fees) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((alice)(bob)(charlie)(smartissuer));\n\n         // Initialize tokens with custom market fees\n         price price(asset(1, asset_id_type(1)), asset(1));\n\n         const uint16_t alice1coin_market_fee_percent = 1 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"ALICE1COIN\", alice, charge_market_fee, price, 2,\n                                                                  alice1coin_market_fee_percent);\n\n         const uint16_t alice2coin_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"ALICE2COIN\", alice, charge_market_fee, price, 2,\n                                                                  alice2coin_market_fee_percent);\n\n         const uint16_t bob1coin_market_fee_percent = 3 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"BOB1COIN\", alice, charge_market_fee, price, 2,\n                                                                bob1coin_market_fee_percent);\n\n         const uint16_t bob2coin_market_fee_percent = 4 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"BOB2COIN\", alice, charge_market_fee, price, 2,\n                                                                bob2coin_market_fee_percent);\n\n         const uint16_t charlie1coin_market_fee_percent = 4 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"CHARLIE1COIN\", alice, charge_market_fee, price, 2,\n                                                                    charlie1coin_market_fee_percent);\n\n         const uint16_t charlie2coin_market_fee_percent = 5 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"CHARLIE2COIN\", alice, charge_market_fee, price, 2,\n                                                                    charlie2coin_market_fee_percent);\n\n         const uint16_t bitsmart1coin_market_fee_percent = 7 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT1\", smartissuer.id, bitsmart1coin_market_fee_percent);\n\n\n         const uint16_t bitsmart2coin_market_fee_percent = 8 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT2\", smartissuer.id, bitsmart2coin_market_fee_percent);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object& alice1coin = get_asset(\"ALICE1COIN\");\n         const asset_object& alice2coin = get_asset(\"ALICE2COIN\");\n         const asset_object& bob1coin = get_asset(\"BOB1COIN\");\n         const asset_object& bob2coin = get_asset(\"BOB2COIN\");\n         const asset_object& charlie1coin = get_asset(\"CHARLIE1COIN\");\n         const asset_object& charlie2coin = get_asset(\"CHARLIE2COIN\");\n         const asset_object& bitsmart1 = get_asset(\"SMARTBIT1\");\n         const asset_object& bitsmart2 = get_asset(\"SMARTBIT2\");\n\n\n         //////\n         // Before HF, test the market/maker fees for each asset\n         //////\n         asset_object updated_asset;\n         uint16_t expected_fee_percent;\n\n         updated_asset = alice1coin.get_id()(db);\n         expected_fee_percent = alice1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = alice2coin.get_id()(db);\n         expected_fee_percent = alice2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bob1coin.get_id()(db);\n         expected_fee_percent = bob1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bob2coin.get_id()(db);\n         expected_fee_percent = bob2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = charlie1coin.get_id()(db);\n         expected_fee_percent = charlie1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = charlie2coin.get_id()(db);\n         expected_fee_percent = charlie2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bitsmart1.get_id()(db);\n         expected_fee_percent = bitsmart1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bitsmart2.get_id()(db);\n         expected_fee_percent = bitsmart2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n\n         //////\n         // Before HF, test that taker fees are not set\n         //////\n         // Check the taker fee\n         updated_asset = alice1coin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = alice2coin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bob1coin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bob2coin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = charlie1coin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = charlie2coin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bitsmart1.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bitsmart2.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test the maker fees for each asset are unchanged\n         //////\n         updated_asset = alice1coin.get_id()(db);\n         expected_fee_percent = alice1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = alice2coin.get_id()(db);\n         expected_fee_percent = alice2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bob1coin.get_id()(db);\n         expected_fee_percent = bob1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bob2coin.get_id()(db);\n         expected_fee_percent = bob2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = charlie1coin.get_id()(db);\n         expected_fee_percent = charlie1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = charlie2coin.get_id()(db);\n         expected_fee_percent = charlie2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bitsmart1.get_id()(db);\n         expected_fee_percent = bitsmart1coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n         updated_asset = bitsmart2.get_id()(db);\n         expected_fee_percent = bitsmart2coin_market_fee_percent;\n         BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent);\n\n\n         //////\n         // After HF, test the taker fees for each asset are not set\n         //////\n         updated_asset = alice1coin.get_id()(db);\n         expected_fee_percent = alice1coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = alice2coin.get_id()(db);\n         expected_fee_percent = alice2coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bob1coin.get_id()(db);\n         expected_fee_percent = bob1coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bob2coin.get_id()(db);\n         expected_fee_percent = bob2coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = charlie1coin.get_id()(db);\n         expected_fee_percent = charlie1coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = charlie2coin.get_id()(db);\n         expected_fee_percent = charlie2coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bitsmart1.get_id()(db);\n         expected_fee_percent = bitsmart1coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         updated_asset = bitsmart2.get_id()(db);\n         expected_fee_percent = bitsmart2coin_market_fee_percent;\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of different maker and taker fees charged when filling limit orders after HF for a UIA\n    */\n   BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_1) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                jill_market_fee_percent);\n\n         const uint16_t IZZY_PRECISION = 1000;\n         const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"ICOIN\", izzy, charge_market_fee, price, 3,\n                                                                izzy_market_fee_percent);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object& jillcoin = get_asset(\"JCOIN\");\n         const asset_object& izzycoin = get_asset(\"ICOIN\");\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         // Define the new taker fees\n         uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = jill_maker_fee_percent / 2;\n\n         uint16_t izzy_maker_fee_percent = izzy_market_fee_percent;\n         uint16_t izzy_taker_fee_percent = izzy_maker_fee_percent / 2;\n\n         // Set the new taker fee for JILLCOIN\n         asset_update_operation uop;\n         uop.issuer = jill.id;\n         uop.asset_to_update = jillcoin.get_id();\n         uop.new_options = jillcoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, jill_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for JILLCOIN\n         asset_object updated_asset = jillcoin.get_id()(db);\n         uint16_t expected_taker_fee_percent = jill_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         // Set the new taker fee for IZZYCOIN\n         uop.issuer = izzy.id;\n         uop.asset_to_update = izzycoin.get_id();\n         uop.new_options = izzycoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = izzy_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, izzy_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for IZZYCOIN\n         updated_asset = izzycoin.get_id()(db);\n         expected_taker_fee_percent = izzy_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n\n         //////\n         // After HF, create limit orders that will perfectly match\n         //////\n         BOOST_TEST_MESSAGE(\"Issuing 10 jillcoin to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 300 izzycoin to bob\");\n         issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice.id,\n                                                                            jillcoin.amount(10 * JILL_PRECISION),\n                                                                            izzycoin.amount(300 *\n                                                                                            IZZY_PRECISION));\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object* alice_order_before = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order_before != nullptr);\n\n         // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN\n         limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION),\n                                                                          jillcoin.amount(\n                                                                                  10 *\n                                                                                  JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(bob_sell_op);\n         asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type bob_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that the orders were filled by ensuring that they are no longer on the order books\n         const limit_order_object* alice_order = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order == nullptr);\n         const limit_order_object* bob_order = db.find<limit_order_object>(bob_order_id);\n         BOOST_CHECK(bob_order == nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving IZZYCOIN\n         asset expected_izzy_fee = izzycoin.amount(\n                 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin),\n                             (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin),\n                             (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0);\n\n         // Check the asset issuer's accumulated fees\n         BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of different maker and taker fees charged when filling limit orders after HF for a UIA\n    *\n    * Test the filling of a taker fee when the **maker** fee percent is set to 0.  This tests some optimizations\n    * in database::calculate_market_fee().\n    */\n   BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_2) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 0 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                jill_market_fee_percent);\n\n         const uint16_t IZZY_PRECISION = 1000;\n         const uint16_t izzy_market_fee_percent = 0 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"ICOIN\", izzy, charge_market_fee, price, 3,\n                                                                izzy_market_fee_percent);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object& jillcoin = get_asset(\"JCOIN\");\n         const asset_object& izzycoin = get_asset(\"ICOIN\");\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         // Define the new taker fees\n         uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = 1 * GRAPHENE_1_PERCENT;\n\n         uint16_t izzy_maker_fee_percent = izzy_market_fee_percent;\n         uint16_t izzy_taker_fee_percent = 3 * GRAPHENE_1_PERCENT;\n\n         // Set the new taker fee for JILLCOIN\n         asset_update_operation uop;\n         uop.issuer = jill.id;\n         uop.asset_to_update = jillcoin.get_id();\n         uop.new_options.market_fee_percent = jill_maker_fee_percent;\n         uop.new_options = jillcoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, jill_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for JILLCOIN\n         asset_object updated_asset = jillcoin.get_id()(db);\n         uint16_t expected_taker_fee_percent = jill_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         // Set the new taker fee for IZZYCOIN\n         uop.issuer = izzy.id;\n         uop.asset_to_update = izzycoin.get_id();\n         uop.new_options.market_fee_percent = izzy_maker_fee_percent;\n         uop.new_options = izzycoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = izzy_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, izzy_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for IZZYCOIN\n         updated_asset = izzycoin.get_id()(db);\n         expected_taker_fee_percent = izzy_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n\n         //////\n         // After HF, create limit orders that will perfectly match\n         //////\n         BOOST_TEST_MESSAGE(\"Issuing 10 jillcoin to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 300 izzycoin to bob\");\n         issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice.id,\n                                                                            jillcoin.amount(10 * JILL_PRECISION),\n                                                                            izzycoin.amount(300 *\n                                                                                            IZZY_PRECISION));\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object* alice_order_before = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order_before != nullptr);\n\n         // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN\n         limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION),\n                                                                          jillcoin.amount(\n                                                                                  10 *\n                                                                                  JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(bob_sell_op);\n         asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type bob_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that the orders were filled by ensuring that they are no longer on the order books\n         const limit_order_object* alice_order = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order == nullptr);\n         const limit_order_object* bob_order = db.find<limit_order_object>(bob_order_id);\n         BOOST_CHECK(bob_order == nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving IZZYCOIN\n         asset expected_izzy_fee = izzycoin.amount(\n                 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin),\n                             (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin),\n                             (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0);\n\n         // Check the asset issuer's accumulated fees\n         BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of different maker and taker fees charged when filling limit orders after HF for a UIA\n    *\n    * Test the filling of a taker fee when the **taker** fee percent is set to 0.  This tests some optimizations\n    * in database::calculate_market_fee().\n    */\n   BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_3) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                jill_market_fee_percent);\n\n         const uint16_t IZZY_PRECISION = 1000;\n         const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"ICOIN\", izzy, charge_market_fee, price, 3,\n                                                                izzy_market_fee_percent);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object& jillcoin = get_asset(\"JCOIN\");\n         const asset_object& izzycoin = get_asset(\"ICOIN\");\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         // Define the new taker fees\n         uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = 0 * GRAPHENE_1_PERCENT;\n\n         uint16_t izzy_maker_fee_percent = izzy_market_fee_percent;\n         uint16_t izzy_taker_fee_percent = 0 * GRAPHENE_1_PERCENT;\n\n         // Set the new taker fee for JILLCOIN\n         asset_update_operation uop;\n         uop.issuer = jill.id;\n         uop.asset_to_update = jillcoin.get_id();\n         uop.new_options.market_fee_percent = jill_maker_fee_percent;\n         uop.new_options = jillcoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, jill_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for JILLCOIN\n         asset_object updated_asset = jillcoin.get_id()(db);\n         uint16_t expected_taker_fee_percent = jill_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         // Set the new taker fee for IZZYCOIN\n         uop.issuer = izzy.id;\n         uop.asset_to_update = izzycoin.get_id();\n         uop.new_options.market_fee_percent = izzy_maker_fee_percent;\n         uop.new_options = izzycoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = izzy_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, izzy_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for IZZYCOIN\n         updated_asset = izzycoin.get_id()(db);\n         expected_taker_fee_percent = izzy_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n\n         //////\n         // After HF, create limit orders that will perfectly match\n         //////\n         BOOST_TEST_MESSAGE(\"Issuing 10 jillcoin to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 300 izzycoin to bob\");\n         issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice.id,\n                                                                            jillcoin.amount(10 * JILL_PRECISION),\n                                                                            izzycoin.amount(300 *\n                                                                                            IZZY_PRECISION));\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object* alice_order_before = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order_before != nullptr);\n\n         // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN\n         limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION),\n                                                                          jillcoin.amount(\n                                                                                  10 *\n                                                                                  JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(bob_sell_op);\n         asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type bob_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that the orders were filled by ensuring that they are no longer on the order books\n         const limit_order_object* alice_order = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order == nullptr);\n         const limit_order_object* bob_order = db.find<limit_order_object>(bob_order_id);\n         BOOST_CHECK(bob_order == nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving IZZYCOIN\n         asset expected_izzy_fee = izzycoin.amount(\n                 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin),\n                             (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin),\n                             (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0);\n\n         // Check the asset issuer's accumulated fees\n         BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of **default** taker fees charged when filling limit orders after HF for a UIA.\n    *\n    * This test is similar to simple_match_and_fill_with_different_fees_uia_1\n    * except that the taker fee is not explicitly set and instead defaults to the maker fee.\n    */\n   BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_4) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                  jill_market_fee_percent);\n\n         const uint16_t IZZY_PRECISION = 1000;\n         const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT;\n         create_user_issued_asset(\"ICOIN\", izzy, charge_market_fee, price, 3,\n                                  izzy_market_fee_percent);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object& jillcoin = get_asset(\"JCOIN\");\n         const asset_object& izzycoin = get_asset(\"ICOIN\");\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test that default taker values has not been set\n         //////\n         // The taker fees should automatically default to maker fees if the taker fee is not explicitly set\n         // UNUSED: uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = jill_market_fee_percent;\n\n         uint16_t izzy_maker_fee_percent = izzy_market_fee_percent;\n         // UNUSED: uint16_t izzy_taker_fee_percent = izzy_market_fee_percent;\n\n         // Check the taker fee for JCOIN: it should still not be set\n         asset_object updated_asset = jillcoin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         // Check the taker fee for ICOIN: it should still not be set\n         updated_asset = izzycoin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n\n         //////\n         // After HF, create limit orders that will perfectly match\n         //////\n         BOOST_TEST_MESSAGE(\"Issuing 10 jillcoin to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 300 izzycoin to bob\");\n         issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice.id,\n                                                                            jillcoin.amount(10 * JILL_PRECISION),\n                                                                            izzycoin.amount(300 *\n                                                                                            IZZY_PRECISION));\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object* alice_order_before = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order_before != nullptr);\n\n         // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN\n         limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION),\n                                                                          jillcoin.amount(\n                                                                                  10 *\n                                                                                  JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(bob_sell_op);\n         asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type bob_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that the orders were filled by ensuring that they are no longer on the order books\n         const limit_order_object* alice_order = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order == nullptr);\n         const limit_order_object* bob_order = db.find<limit_order_object>(bob_order_id);\n         BOOST_CHECK(bob_order == nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving IZZYCOIN\n         asset expected_izzy_fee = izzycoin.amount(\n                 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin),\n                             (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin),\n                             (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0);\n\n         // Check the asset issuer's accumulated fees\n         BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n\n   /**\n    * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset\n    */\n   BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_smart_asset) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const asset_object jillcoin = create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                jill_market_fee_percent);\n\n         const uint16_t SMARTBIT_PRECISION = 10000;\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT\", smartissuer.id, smartbit_market_fee_percent,\n                                                        charge_market_fee, 4);\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object &smartbit = get_asset(\"SMARTBIT\");\n\n         const auto &core = asset_id_type()(db);\n\n         update_feed_producers(smartbit, {feedproducer.id});\n\n         price_feed current_feed;\n         current_feed.settlement_price = smartbit.amount(100) / core.amount(100);\n         current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n         publish_feed(smartbit, feedproducer, current_feed);\n\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         // Define the new taker fees\n         uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = jill_maker_fee_percent / 2;\n\n         uint16_t smartbit_maker_fee_percent = 1 * GRAPHENE_1_PERCENT;\n         uint16_t smartbit_taker_fee_percent = 3 * GRAPHENE_1_PERCENT;\n\n         // Set the new taker fee for JILLCOIN\n         asset_update_operation uop;\n         uop.issuer = jill.id;\n         uop.asset_to_update = jillcoin.get_id();\n         uop.new_options = jillcoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, jill_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for JILLCOIN\n         asset_object updated_asset = jillcoin.get_id()(db);\n         uint16_t expected_taker_fee_percent = jill_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n\n         // Set the new taker fee for SMARTBIT\n         uop = asset_update_operation();\n         uop.issuer = smartissuer.id;\n         uop.asset_to_update = smartbit.get_id();\n         uop.new_options = smartbit.options;\n         uop.new_options.market_fee_percent = smartbit_maker_fee_percent;\n         uop.new_options.extensions.value.taker_fee_percent = smartbit_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for SMARTBIT\n         updated_asset = smartbit.get_id()(db);\n         expected_taker_fee_percent = smartbit_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         // Check the maker fee for SMARTBIT\n         updated_asset = smartbit.get_id()(db);\n         expected_taker_fee_percent = smartbit_maker_fee_percent;\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.market_fee_percent);\n\n\n         //////\n         // After HF, create limit orders that will perfectly match\n         //////\n         BOOST_TEST_MESSAGE(\"Issuing 10 jillcoin to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 300 SMARTBIT to bob\");\n         transfer(committee_account, bob.id, asset(10000000));\n         publish_feed(smartbit, feedproducer, current_feed); // Publish a recent feed\n         borrow(bob, smartbit.amount(300 * SMARTBIT_PRECISION), asset(2 * 300 * SMARTBIT_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 300 * SMARTBIT_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 SMARTBIT\n         limit_order_create_operation alice_sell_op = create_sell_operation(alice.id,\n                                                                            jillcoin.amount(10 * JILL_PRECISION),\n                                                                            smartbit.amount(300 * SMARTBIT_PRECISION));\n         trx.clear();\n         trx.operations.push_back(alice_sell_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type alice_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object *alice_order_before = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order_before != nullptr);\n\n\n         // Bob is willing to sell 300 SMARTBIT for at least 10 JILLCOIN\n         limit_order_create_operation bob_sell_op\n                 = create_sell_operation(bob.id, smartbit.amount(300 * SMARTBIT_PRECISION),\n                                         jillcoin.amount(10 * JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(bob_sell_op);\n         asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type bob_order_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that the orders were filled by ensuring that they are no longer on the order books\n         const limit_order_object *alice_order = db.find<limit_order_object>(alice_order_id);\n         BOOST_CHECK(alice_order == nullptr);\n         const limit_order_object *bob_order = db.find<limit_order_object>(bob_order_id);\n         BOOST_CHECK(bob_order == nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving SMARTBIT\n         asset expected_smartbit_fee = smartbit.amount(\n                 300 * SMARTBIT_PRECISION * smartbit_maker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit),\n                             (300 * SMARTBIT_PRECISION) - alice_sell_fee.amount.value -\n                             expected_smartbit_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin),\n                             (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0);\n\n         // Check the asset issuer's accumulated fees\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee.amount);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset\n    * and a user-issued asset\n    *\n    * 1. (Order 1) An order will be placed to offer JCOIN\n    *\n    * 2. (Order 2) A matching-order will be placed to offer SMARTBIT.\n    *     Order 2 is large enough that it should be partially filled, and Order 1 will be completely filled.\n    *     Order 1 should be charged a maker fee, and Order 2 should be charged a taker fee.\n    *     Order 2 should remain on the book.\n    *\n    * 3. (Order 3) A matching order will be placed to offer JCOIN.\n    *     Order 3 should be charged a taker fee, and Order 2 should be charged a maker fee.\n    *\n    * Summary: Order 2 should be charged a taker fee when matching Order 1,\n    * and Order 2 should be charged a maker fee when matching Order 3.\n    */\n   BOOST_AUTO_TEST_CASE(partial_maker_partial_taker_fills_1) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob)(charlie));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const asset_object jillcoin = create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                jill_market_fee_percent);\n\n         const uint16_t SMARTBIT_PRECISION = 10000;\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT\", smartissuer.id, smartbit_market_fee_percent,\n                                                        charge_market_fee, 4);\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object &smartbit = get_asset(\"SMARTBIT\");\n\n         const auto &core = asset_id_type()(db);\n\n         update_feed_producers(smartbit, {feedproducer.id});\n\n         price_feed current_feed;\n         current_feed.settlement_price = smartbit.amount(100) / core.amount(100);\n         current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n         publish_feed(smartbit, feedproducer, current_feed);\n\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // Advance to activate hardfork\n         //////\n         generate_blocks(HARDFORK_BSIP_81_TIME);\n         generate_block();\n         trx.clear();\n         set_expiration(db, trx);\n\n\n         //////\n         // After HF, test that new values can be set\n         //////\n         // Define the new taker fees\n         uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = jill_maker_fee_percent / 2;\n\n         uint16_t smartbit_maker_fee_percent = 1 * GRAPHENE_1_PERCENT;\n         uint16_t smartbit_taker_fee_percent = 3 * GRAPHENE_1_PERCENT;\n\n         // Set the new taker fee for JILLCOIN\n         asset_update_operation uop;\n         uop.issuer = jill.id;\n         uop.asset_to_update = jillcoin.get_id();\n         uop.new_options = jillcoin.options;\n         uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, jill_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for JILLCOIN\n         asset_object updated_asset = jillcoin.get_id()(db);\n         uint16_t expected_taker_fee_percent = jill_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         // Check the maker fee for JILLCOIN\n         uint16_t expected_maker_fee_percent = jill_maker_fee_percent;\n         BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent);\n\n\n         // Set the new taker fee for SMARTBIT\n         uop = asset_update_operation();\n         uop.issuer = smartissuer.id;\n         uop.asset_to_update = smartbit.get_id();\n         uop.new_options = smartbit.options;\n         uop.new_options.market_fee_percent = smartbit_maker_fee_percent;\n         uop.new_options.extensions.value.taker_fee_percent = smartbit_taker_fee_percent;\n\n         trx.clear();\n         trx.operations.push_back(uop);\n         db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, smartissuer_private_key);\n         PUSH_TX(db, trx); // No exception should be thrown\n\n         // Check the taker fee for SMARTBIT\n         updated_asset = smartbit.get_id()(db);\n         expected_taker_fee_percent = smartbit_taker_fee_percent;\n         BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid());\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent);\n\n         // Check the maker fee for SMARTBIT\n         updated_asset = smartbit.get_id()(db);\n         expected_taker_fee_percent = smartbit_maker_fee_percent;\n         BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.market_fee_percent);\n\n\n         //////\n         // Create Orders 1 and 2 to match.\n         // Order 1 will be completely filled, and Order 2 will be partially filled.\n         //////\n         // Initialize token balance of actors\n         BOOST_TEST_MESSAGE(\"Issuing 10 JCOIN to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 600 SMARTBIT to bob\");\n         transfer(committee_account, bob.id, asset(2 * 1000 * SMARTBIT_PRECISION));\n         publish_feed(smartbit, feedproducer, current_feed); // Publish a recent feed\n         borrow(bob, smartbit.amount(600 * SMARTBIT_PRECISION), asset(2 * 600 * SMARTBIT_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 600 * SMARTBIT_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 SMARTBIT\n         limit_order_create_operation order_1_op = create_sell_operation(alice.id,\n                                                                            jillcoin.amount(10 * JILL_PRECISION),\n                                                                            smartbit.amount(300 * SMARTBIT_PRECISION));\n         trx.clear();\n         trx.operations.push_back(order_1_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type order_1_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object *order_1_before = db.find<limit_order_object>(order_1_id);\n         BOOST_CHECK(order_1_before != nullptr);\n\n\n         // Bob is willing to sell 600 SMARTBIT for at least 20 JILLCOIN\n         limit_order_create_operation order_2_op\n                 = create_sell_operation(bob.id, smartbit.amount(600 * SMARTBIT_PRECISION),\n                                         jillcoin.amount(20 * JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(order_2_op);\n         asset order_2_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type order_2_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that order 1 was completely filled by ensuring that they it is no longer on the order book\n         const limit_order_object *order_1 = db.find<limit_order_object>(order_1_id);\n         BOOST_CHECK(order_1 == nullptr);\n         // Check that order 2  was partially filled by ensuring that they it is still on the order book\n         const limit_order_object *order_2 = db.find<limit_order_object>(order_2_id);\n         BOOST_CHECK(order_2 != nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving SMARTBIT\n         asset expected_smartbit_fee = smartbit.amount(\n                 300 * SMARTBIT_PRECISION * smartbit_maker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_alice_balance_after_order_2 =\n                 (300 * SMARTBIT_PRECISION) - alice_sell_fee.amount.value - expected_smartbit_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit), expected_alice_balance_after_order_2);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_bob_balance_after_order_2 =\n                 (10 * JILL_PRECISION) - order_2_sell_fee.amount.value - expected_jill_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_2);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0);\n\n         // Check the asset issuer's accumulated fees\n         share_type expected_smartbit_fee_after_order_2 = expected_smartbit_fee.amount;\n         share_type expected_jill_fee_after_order_2 = expected_jill_fee.amount;\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_2);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_2);\n\n\n         //////\n         // Create Order 3 to match the remainder of match Order 2\n         //////\n         // Initialize token balance of actors\n         BOOST_TEST_MESSAGE(\"Issuing 5 JCOIN to charlie\");\n         trx.clear();\n         issue_uia(charlie, jillcoin.amount(5 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking charlie's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 5 * JILL_PRECISION);\n\n         // Charlie is is willing to sell 5 JILLCOIN for at least 150 SMARTBIT\n         limit_order_create_operation order_3_op = create_sell_operation(charlie.id,\n                                                                         jillcoin.amount(5 * JILL_PRECISION),\n                                                                         smartbit.amount(150 * SMARTBIT_PRECISION));\n         trx.clear();\n         trx.operations.push_back(order_3_op);\n         asset charlie_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, charlie_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type order_3_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Order 3 should be completely filled\n         const limit_order_object *order_3 = db.find<limit_order_object>(order_3_id);\n         BOOST_CHECK(order_3 == nullptr);\n\n         // Order 2 should be partially filled and still present on the order books\n         const limit_order_object *order_2_after = db.find<limit_order_object>(order_2_id);\n         BOOST_CHECK(order_2_after != nullptr);\n\n         // Check the new balance of the taker\n         // Charlie was the taker; he is receiving SMARTBIT\n         expected_smartbit_fee = smartbit.amount(\n                 150 * SMARTBIT_PRECISION * smartbit_taker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_charlie_balance_after_order_3 =\n                 (150 * SMARTBIT_PRECISION) - charlie_sell_fee.amount.value - expected_smartbit_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, smartbit), expected_charlie_balance_after_order_3);\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 0);\n\n         // Check the new balance of the maker\n         // Bob was the maker; he is receiving JILLCOIN\n         asset expected_jill_order_3_fee = jillcoin.amount(\n                 5 * JILL_PRECISION * jill_maker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_bob_balance_after_order_3 =\n                 expected_bob_balance_after_order_2\n                 + (5 * JILL_PRECISION) - expected_jill_order_3_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_3);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0);\n\n         // Check the asset issuer's accumulated fees\n         share_type expected_smartbit_fee_after_order_3 =\n                 expected_smartbit_fee_after_order_2 + expected_smartbit_fee.amount;\n         share_type expected_jill_fee_after_order_3 = expected_jill_fee_after_order_2 + expected_jill_order_3_fee.amount;\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_3);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_3);\n\n      } FC_LOG_AND_RETHROW()\n   }\n\n\n   /**\n    * Test of **default** taker fees charged when filling limit orders after HF for a smart asset\n    * and a user-issued asset\n    *\n    * This test is similar to partial_maker_partial_taker_fills_1 except that\n    * (a) the taker fee is not explicitly set and instead defaults to the maker fee, and\n    * (b) Orders 1 and 2 are placed before the HF and Order 3 is placed after the HF.\n    *\n    * 1. (Order 1) An order will be placed to offer JCOIN\n    *\n    * 2. (Order 2) A matching-order will be placed to offer SMARTBIT.\n    *     Order 2 is large enough that it should be partially filled, and Order 1 will be completely filled.\n    *     Order 1 should be charged a maker fee, and Order 2 should be charged a taker fee.\n    *     Order 2 should remain on the book.\n    *\n    * 3. (Order 3) A matching order will be placed to offer JCOIN.\n    *     Order 3 should be charged a taker fee, and Order 2 should be charged a maker fee.\n    *\n    * Summary: Order 2 should be charged a taker fee when matching Order 1,\n    * and Order 2 should be charged a maker fee when matching Order 3.\n    */\n   BOOST_AUTO_TEST_CASE(partial_maker_partial_taker_fills_2) {\n      try {\n         // Initialize for the current time\n         trx.clear();\n         set_expiration(db, trx);\n\n         // Initialize actors\n         ACTORS((jill)(izzy)(alice)(bob)(charlie));\n         ACTORS((smartissuer)(feedproducer));\n\n         // Initialize tokens\n         price price(asset(1, asset_id_type(1)), asset(1));\n         const uint16_t JILL_PRECISION = 100;\n         const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         const asset_object jillcoin = create_user_issued_asset(\"JCOIN\", jill, charge_market_fee, price, 2,\n                                                                jill_market_fee_percent);\n\n         const uint16_t SMARTBIT_PRECISION = 10000;\n         const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT;\n         create_bitasset(\"SMARTBIT\", smartissuer.id, smartbit_market_fee_percent,\n                         charge_market_fee, 4);\n\n         uint16_t jill_maker_fee_percent = jill_market_fee_percent;\n         uint16_t jill_taker_fee_percent = jill_market_fee_percent;\n\n         uint16_t smartbit_maker_fee_percent = smartbit_market_fee_percent;\n         uint16_t smartbit_taker_fee_percent = smartbit_market_fee_percent;\n\n\n         // Obtain asset object after a block is generated to obtain the final object that is commited to the database\n         generate_block();\n         const asset_object &smartbit = get_asset(\"SMARTBIT\");\n\n         const auto &core = asset_id_type()(db);\n\n         update_feed_producers(smartbit, {feedproducer.id});\n\n         price_feed current_feed;\n         current_feed.settlement_price = smartbit.amount(100) / core.amount(100);\n         current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n         publish_feed(smartbit, feedproducer, current_feed);\n\n         FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price);\n\n\n         //////\n         // Create Orders 1 and 2 to match.\n         // Order 1 will be completely filled, and Order 2 will be partially filled.\n         //////\n         // Initialize token balance of actors\n         BOOST_TEST_MESSAGE(\"Issuing 10 JCOIN to alice\");\n         issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking alice's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION);\n\n         BOOST_TEST_MESSAGE(\"Issuing 600 SMARTBIT to bob\");\n         transfer(committee_account, bob.id, asset(2 * 1000 * SMARTBIT_PRECISION));\n         publish_feed(smartbit, feedproducer, current_feed); // Publish a recent feed\n         borrow(bob, smartbit.amount(600 * SMARTBIT_PRECISION), asset(2 * 600 * SMARTBIT_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking bob's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 600 * SMARTBIT_PRECISION);\n\n         // Alice and Bob place orders which match, and are completely filled by each other\n         // Alice is willing to sell 10 JILLCOIN for at least 300 SMARTBIT\n         limit_order_create_operation order_1_op = create_sell_operation(alice.id,\n                                                                         jillcoin.amount(10 * JILL_PRECISION),\n                                                                         smartbit.amount(300 * SMARTBIT_PRECISION));\n         trx.clear();\n         trx.operations.push_back(order_1_op);\n         asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, alice_private_key);\n         processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type order_1_id = ptx.operation_results[0].get<object_id_type>();\n\n         const limit_order_object *order_1_before = db.find<limit_order_object>(order_1_id);\n         BOOST_CHECK(order_1_before != nullptr);\n\n\n         // Bob is willing to sell 600 SMARTBIT for at least 20 JILLCOIN\n         limit_order_create_operation order_2_op\n                 = create_sell_operation(bob.id, smartbit.amount(600 * SMARTBIT_PRECISION),\n                                         jillcoin.amount(20 * JILL_PRECISION));\n         trx.clear();\n         trx.operations.push_back(order_2_op);\n         asset order_2_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, bob_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type order_2_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Check that order 1 was completely filled by ensuring that they it is no longer on the order book\n         const limit_order_object *order_1 = db.find<limit_order_object>(order_1_id);\n         BOOST_CHECK(order_1 == nullptr);\n         // Check that order 2  was partially filled by ensuring that they it is still on the order book\n         const limit_order_object *order_2 = db.find<limit_order_object>(order_2_id);\n         BOOST_CHECK(order_2 != nullptr);\n\n\n         // Check the new balances of the maker\n         // Alice was the maker; she is receiving SMARTBIT\n         asset expected_smartbit_fee = smartbit.amount(\n                 300 * SMARTBIT_PRECISION * smartbit_maker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_alice_balance_after_order_2 =\n                 (300 * SMARTBIT_PRECISION) - alice_sell_fee.amount.value - expected_smartbit_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit), expected_alice_balance_after_order_2);\n         BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0);\n\n         // Check the new balance of the taker\n         // Bob was the taker; he is receiving JILLCOIN\n         asset expected_jill_fee = jillcoin.amount(\n                 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_bob_balance_after_order_2 =\n                 (10 * JILL_PRECISION) - order_2_sell_fee.amount.value - expected_jill_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_2);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0);\n\n         // Check the asset issuer's accumulated fees\n         share_type expected_smartbit_fee_after_order_2 = expected_smartbit_fee.amount;\n         share_type expected_jill_fee_after_order_2 = expected_jill_fee.amount;\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_2);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_2);\n\n\n         //////\n         // After HF, The taker fees should automatically default to maker fees when the taker fee is not explicitly set\n         //////\n         // Check the taker fee for JILLCOIN\n         asset_object updated_asset = jillcoin.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         // Check the maker fee for JILLCOIN\n         uint16_t expected_maker_fee_percent = jill_maker_fee_percent;\n         BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent);\n\n         // Check the taker fee for SMARTBIT\n         updated_asset = smartbit.get_id()(db);\n         BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid());\n\n         // Check the maker fee for SMARTBIT\n         expected_maker_fee_percent = smartbit_maker_fee_percent;\n         BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent);\n\n\n         //////\n         // Create Order 3 to match the remainder of match Order 2\n         //////\n         // Initialize token balance of actors\n         BOOST_TEST_MESSAGE(\"Issuing 5 JCOIN to charlie\");\n         trx.clear();\n         issue_uia(charlie, jillcoin.amount(5 * JILL_PRECISION));\n         BOOST_TEST_MESSAGE(\"Checking charlie's balance\");\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 5 * JILL_PRECISION);\n\n         // Charlie is is willing to sell 5 JILLCOIN for at least 150 SMARTBIT\n         limit_order_create_operation order_3_op = create_sell_operation(charlie.id,\n                                                                         jillcoin.amount(5 * JILL_PRECISION),\n                                                                         smartbit.amount(150 * SMARTBIT_PRECISION));\n         trx.clear();\n         trx.operations.push_back(order_3_op);\n         asset charlie_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back());\n         sign(trx, charlie_private_key);\n         ptx = PUSH_TX(db, trx); // No exception should be thrown\n         limit_order_id_type order_3_id = ptx.operation_results[0].get<object_id_type>();\n\n         // Order 3 should be completely filled\n         const limit_order_object *order_3 = db.find<limit_order_object>(order_3_id);\n         BOOST_CHECK(order_3 == nullptr);\n\n         // Order 2 should be partially filled and still present on the order books\n         const limit_order_object *order_2_after = db.find<limit_order_object>(order_2_id);\n         BOOST_CHECK(order_2_after != nullptr);\n\n         // Check the new balance of the taker\n         // Charlie was the taker; he is receiving SMARTBIT\n         expected_smartbit_fee = smartbit.amount(\n                 150 * SMARTBIT_PRECISION * smartbit_taker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_charlie_balance_after_order_3 =\n                 (150 * SMARTBIT_PRECISION) - charlie_sell_fee.amount.value - expected_smartbit_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, smartbit), expected_charlie_balance_after_order_3);\n         BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 0);\n\n         // Check the new balance of the maker\n         // Bob was the maker; he is receiving JILLCOIN\n         asset expected_jill_order_3_fee = jillcoin.amount(\n                 5 * JILL_PRECISION * jill_maker_fee_percent / GRAPHENE_100_PERCENT);\n         int64_t expected_bob_balance_after_order_3 =\n                 expected_bob_balance_after_order_2\n                 + (5 * JILL_PRECISION) - expected_jill_order_3_fee.amount.value;\n         BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_3);\n         BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0);\n\n         // Check the asset issuer's accumulated fees\n         share_type expected_smartbit_fee_after_order_3 =\n                 expected_smartbit_fee_after_order_2 + expected_smartbit_fee.amount;\n         share_type expected_jill_fee_after_order_3 = expected_jill_fee_after_order_2 + expected_jill_order_3_fee.amount;\n         BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_3);\n         BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_3);\n         wdump((jillcoin.dynamic_asset_data_id(db).accumulated_fees)(expected_jill_fee_after_order_3)(expected_jill_fee_after_order_2)(expected_jill_fee.amount));\n         wdump((get_asset(\"JCOIN\").dynamic_asset_data_id(db).accumulated_fees));\n\n      } FC_LOG_AND_RETHROW()\n   }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/smartcoin_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/database_api.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/exceptions.hpp>\n\n#include <iostream>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\n\nBOOST_FIXTURE_TEST_SUITE(smartcoin_tests, database_fixture)\n\n\nBOOST_AUTO_TEST_CASE(bsip36)\n{\n   try\n   {\n      /* Issue affects only smartcoins(market pegged assets feeded by active witnesses or committee members).\n       * Test case reproduces, advance to hardfork and check if solved after it.\n       */\n\n      /* References:\n       * BSIP 36: https://github.com/bitshares/bsips/blob/master/bsip-0036.md\n       * and the former: CORE Issue 518: https://github.com/bitshares/bitshares-core/issues/518\n       */\n\n      // Create 12 accounts to be witnesses under our control\n      ACTORS( (witness0)(witness1)(witness2)(witness3)(witness4)(witness5)\n                   (witness6)(witness7)(witness8)(witness9)(witness10)(witness11) );\n\n      // Upgrade all accounts to LTM\n      upgrade_to_lifetime_member(witness0_id);\n      upgrade_to_lifetime_member(witness1_id);\n      upgrade_to_lifetime_member(witness2_id);\n      upgrade_to_lifetime_member(witness3_id);\n      upgrade_to_lifetime_member(witness4_id);\n      upgrade_to_lifetime_member(witness5_id);\n      upgrade_to_lifetime_member(witness6_id);\n      upgrade_to_lifetime_member(witness7_id);\n      upgrade_to_lifetime_member(witness8_id);\n      upgrade_to_lifetime_member(witness9_id);\n      upgrade_to_lifetime_member(witness10_id);\n      upgrade_to_lifetime_member(witness11_id);\n\n      // Create all the witnesses\n      const witness_id_type witness0_witness_id = create_witness(witness0_id, witness0_private_key).id;\n      const witness_id_type witness1_witness_id = create_witness(witness1_id, witness1_private_key).id;\n      const witness_id_type witness2_witness_id = create_witness(witness2_id, witness2_private_key).id;\n      const witness_id_type witness3_witness_id = create_witness(witness3_id, witness3_private_key).id;\n      const witness_id_type witness4_witness_id = create_witness(witness4_id, witness4_private_key).id;\n      const witness_id_type witness5_witness_id = create_witness(witness5_id, witness5_private_key).id;\n      const witness_id_type witness6_witness_id = create_witness(witness6_id, witness6_private_key).id;\n      const witness_id_type witness7_witness_id = create_witness(witness7_id, witness7_private_key).id;\n      const witness_id_type witness8_witness_id = create_witness(witness8_id, witness8_private_key).id;\n      const witness_id_type witness9_witness_id = create_witness(witness9_id, witness9_private_key).id;\n      const witness_id_type witness10_witness_id = create_witness(witness10_id, witness10_private_key).id;\n      const witness_id_type witness11_witness_id = create_witness(witness11_id, witness11_private_key).id;\n\n      // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time\n      const vector <fc::ecc::private_key> private_keys = {\n            witness0_private_key,\n            witness1_private_key,\n            witness2_private_key,\n            witness3_private_key,\n            witness4_private_key,\n            witness5_private_key,\n            witness6_private_key,\n            witness7_private_key,\n            witness8_private_key,\n            witness9_private_key,\n            witness10_private_key\n      };\n\n      // create a map with account id and witness id of the first 11 witnesses\n      const flat_map <account_id_type, witness_id_type> witness_map = {\n         {witness0_id, witness0_witness_id},\n         {witness1_id, witness1_witness_id},\n         {witness2_id, witness2_witness_id},\n         {witness3_id, witness3_witness_id},\n         {witness4_id, witness4_witness_id},\n         {witness5_id, witness5_witness_id},\n         {witness6_id, witness6_witness_id},\n         {witness7_id, witness7_witness_id},\n         {witness8_id, witness8_witness_id},\n         {witness9_id, witness9_witness_id},\n         {witness10_id, witness10_witness_id}\n      };\n\n      // Create the asset\n      const asset_id_type bit_usd_id = create_bitasset(\"USDBIT\").id;\n\n      // Update the asset to be fed by system witnesses\n      asset_update_operation op;\n      const asset_object &asset_obj = bit_usd_id(db);\n      op.asset_to_update = bit_usd_id;\n      op.issuer = asset_obj.issuer;\n      op.new_options = asset_obj.options;\n      op.new_options.flags &= witness_fed_asset;\n      op.new_options.issuer_permissions &= witness_fed_asset;\n      trx.operations.push_back(op);\n      PUSH_TX(db, trx, ~0);\n      generate_block();\n      trx.clear();\n\n      // Check current default witnesses, default chain is configured with 10 witnesses\n      auto witnesses = db.get_global_properties().active_witnesses;\n      BOOST_CHECK_EQUAL(witnesses.size(), INITIAL_WITNESS_COUNT);\n      BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 4u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 5u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 6u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9u);\n\n      // We need to activate 11 witnesses by voting for each of them.\n      // Each witness is voted with incremental stake so last witness created will be the ones with more votes\n\n      // by default we have 9 witnesses, we need to vote for desired witness count (11) to increase them\n      vote_for_committee_and_witnesses(9, 11);\n\n      int c = 0;\n      for (auto l : witness_map) {\n         // voting stake have step of 100\n         // so vote_for_committee_and_witnesses() with stake=10 does not affect the expected result\n         int stake = 100 * (c + 1);\n         transfer(committee_account, l.first, asset(stake));\n         {\n            account_update_operation op;\n            op.account = l.first;\n            op.new_options = l.first(db).options;\n            op.new_options->votes.insert(l.second(db).vote_id);\n            op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),\n                                                        [](vote_id_type id) {\n                                                           return id.type() == vote_id_type::witness;\n                                                        });\n            trx.operations.push_back(op);\n            sign(trx, private_keys.at(c));\n            PUSH_TX(db, trx);\n            trx.clear();\n         }\n         ++c;\n      }\n\n      // Trigger the new witnesses\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      // Check my witnesses are now in control of the system\n      witnesses = db.get_global_properties().active_witnesses;\n      BOOST_CHECK_EQUAL(witnesses.size(), 11u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 11u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 12u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 13u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 14u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 15u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 16u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 17u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 18u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 19u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 20u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 21u);\n\n      // Adding 2 feeds with witnesses 0 and 1, checking if they get inserted\n      const asset_object &core = asset_id_type()(db);\n      price_feed feed;\n      feed.settlement_price = bit_usd_id(db).amount(1) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness0_id(db), feed);\n\n      asset_bitasset_data_object bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 1u);\n      auto itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 16u);\n\n      feed.settlement_price = bit_usd_id(db).amount(2) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness1_id(db), feed);\n\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 16u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 17u);\n\n      // Activate witness11 with voting stake, will kick the witness with less votes(witness0) out of the active list\n      transfer(committee_account, witness11_id, asset(1200));\n      set_expiration(db, trx);\n      {\n         account_update_operation op;\n         op.account = witness11_id;\n         op.new_options = witness11_id(db).options;\n         op.new_options->votes.insert(witness11_witness_id(db).vote_id);\n         op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),\n                                                     [](vote_id_type id) {\n                                                        return id.type() == vote_id_type::witness;\n                                                     });\n         trx.operations.push_back(op);\n         sign(trx, witness11_private_key);\n         PUSH_TX(db, trx);\n         trx.clear();\n      }\n\n      // Trigger new witness\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      // Check active witness list now\n      witnesses = db.get_global_properties().active_witnesses;\n      BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 12u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 13u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 14u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 15u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 16u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 17u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 18u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 19u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 20u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 21u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 22u);\n\n      // witness0 has been removed but it was a feeder before\n      // Feed persist in the blockchain, this reproduces the issue\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 16u);\n\n      // Feed persist after expiration\n      const auto feed_lifetime = bit_usd_id(db).bitasset_data(db).options.feed_lifetime_sec;\n      generate_blocks(db.head_block_time() + feed_lifetime + 1);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 16u);\n\n      // Other witnesses add more feeds\n      feed.settlement_price = bit_usd_id(db).amount(4) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness2_id(db), feed);\n      feed.settlement_price = bit_usd_id(db).amount(3) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness3_id(db), feed);\n\n      // But the one from witness0 is never removed\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 4u);\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 16u);\n\n      // Feed from witness1 is also expired but never deleted\n      // All feeds should be deleted at this point\n      const auto minimum_feeds = bit_usd_id(db).bitasset_data(db).options.minimum_feeds;\n      BOOST_CHECK_EQUAL(minimum_feeds, 1u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 17u);\n\n      // Advancing into HF time\n      generate_blocks(HARDFORK_CORE_518_TIME);\n\n      // Advancing to next maint\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      //  All expired feeds are deleted\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 0u);\n\n      // witness1 start feed producing again\n      feed.settlement_price = bit_usd_id(db).amount(1) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness1_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 1u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n\n      // generate some blocks up to expiration but feed will not be deleted yet as need next maint time\n      generate_blocks(itr[0].second.first + feed_lifetime + 1);\n\n      // add another feed with witness2\n      feed.settlement_price = bit_usd_id(db).amount(1) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness2_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 18u);\n\n      // make the first feed expire\n      generate_blocks(itr[0].second.first + feed_lifetime + 1);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      // feed from witness0 expires and gets deleted, feed from witness is on time so persist\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 1u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 18u);\n\n      // expire everything\n      generate_blocks(itr[0].second.first + feed_lifetime + 1);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 0u);\n\n      // add new feed with witness1\n      feed.settlement_price = bit_usd_id(db).amount(1) / core.amount(5);\n      publish_feed(bit_usd_id(db), witness1_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 1u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n\n      // Reactivate witness0\n      transfer(committee_account, witness0_id, asset(1000));\n      set_expiration(db, trx);\n      {\n         account_update_operation op;\n         op.account = witness0_id;\n         op.new_options = witness0_id(db).options;\n         op.new_options->votes.insert(witness0_witness_id(db).vote_id);\n         op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),\n                                                     [](vote_id_type id) {\n                                                        return id.type() == vote_id_type::witness;\n                                                     });\n         trx.operations.push_back(op);\n         sign(trx, witness0_private_key);\n         PUSH_TX(db, trx);\n         trx.clear();\n      }\n\n      // This will deactivate witness1 as it is the one with less votes\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      // Checking\n      witnesses = db.get_global_properties().active_witnesses;\n      BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 11u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 13u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 14u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 15u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 16u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 17u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 18u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 19u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 20u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 21u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 22u);\n\n      // feed from witness1 is still here as the witness is no longer a producer but the feed is not yet expired\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 1u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n\n      // make feed from witness1 expire\n      generate_blocks(itr[0].second.first + feed_lifetime + 1);\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 0u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(bsip36_update_feed_producers)\n{\n   try\n   {\n      /* For MPA fed by non witnesses or non committee mmembers but by feed producers changes should do nothing */\n      ACTORS( (sam)(alice)(paul)(bob) );\n\n      // Create the asset\n      const asset_id_type bit_usd_id = create_bitasset(\"USDBIT\").id;\n\n      // Update asset issuer\n      const asset_object &asset_obj = bit_usd_id(db);\n      {\n         asset_update_operation op;\n         op.asset_to_update = bit_usd_id;\n         op.issuer = asset_obj.issuer;\n         op.new_issuer = bob_id;\n         op.new_options = asset_obj.options;\n         op.new_options.flags &= ~witness_fed_asset;\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n         generate_block();\n         trx.clear();\n      }\n\n      // Add 3 feed producers for asset\n      {\n         asset_update_feed_producers_operation op;\n         op.asset_to_update = bit_usd_id;\n         op.issuer = bob_id;\n         op.new_feed_producers = {sam_id, alice_id, paul_id};\n         trx.operations.push_back(op);\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n         generate_block();\n         trx.clear();\n      }\n\n      // Bitshares will create entries in the field feed after feed producers are added\n      auto bitasset_data = bit_usd_id(db).bitasset_data(db);\n\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 3u);\n      auto itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 16u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 17u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 18u);\n\n      // Removing a feed producer\n      {\n         asset_update_feed_producers_operation op;\n         op.asset_to_update = bit_usd_id;\n         op.issuer = bob_id;\n         op.new_feed_producers = {alice_id, paul_id};\n         trx.operations.push_back(op);\n         sign(trx, bob_private_key);\n         PUSH_TX(db, trx);\n         generate_block();\n         trx.clear();\n      }\n\n      // Feed for removed producer is removed\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 18u);\n\n      // Feed persist after expiration\n      const auto feed_lifetime = bit_usd_id(db).bitasset_data(db).options.feed_lifetime_sec;\n      generate_blocks(db.head_block_time() + feed_lifetime + 1);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 18u);\n\n      // Advancing into HF time\n      generate_blocks(HARDFORK_CORE_518_TIME);\n\n      // Advancing to next maint\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n      // Expired feeds persist, no changes\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 18u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(bsip36_additional)\n{\n   try\n   {\n      /* Check impact of bsip36 with multiple feeds */\n      INVOKE( bsip36 );\n\n      // get the stuff needed from invoked test\n      const asset_id_type bit_usd_id = get_asset(\"USDBIT\").id;\n      const asset_id_type core_id = asset_id_type();\n      const account_id_type witness5_id= get_account(\"witness5\").id;\n      const account_id_type witness6_id= get_account(\"witness6\").id;\n      const account_id_type witness7_id= get_account(\"witness7\").id;\n      const account_id_type witness8_id= get_account(\"witness8\").id;\n      const account_id_type witness9_id= get_account(\"witness9\").id;\n      const account_id_type witness10_id= get_account(\"witness10\").id;\n\n\n      set_expiration( db, trx );\n\n      // changing lifetime feed to 5 days\n      // maint interval default is every 1 day\n      {\n         asset_update_bitasset_operation op;\n         op.new_options.minimum_feeds = 3;\n         op.new_options.feed_lifetime_sec = 86400 * 5;\n         op.asset_to_update = bit_usd_id;\n         op.issuer = bit_usd_id(db).issuer;\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n         generate_block();\n         trx.clear();\n      }\n\n      price_feed feed;\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness5_id(db), feed);\n      auto bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 1u);\n      auto itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness6_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 22u);\n\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness7_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 3u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 22u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 23u);\n\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness8_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 4u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 22u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 23u);\n      BOOST_CHECK_EQUAL(itr[3].first.instance.value, 24u);\n\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness9_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 5u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 22u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 23u);\n      BOOST_CHECK_EQUAL(itr[3].first.instance.value, 24u);\n      BOOST_CHECK_EQUAL(itr[4].first.instance.value, 25u);\n\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness10_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 6u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 22u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 23u);\n      BOOST_CHECK_EQUAL(itr[3].first.instance.value, 24u);\n      BOOST_CHECK_EQUAL(itr[4].first.instance.value, 25u);\n      BOOST_CHECK_EQUAL(itr[5].first.instance.value, 26u);\n\n      // make the older feed expire\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 5u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 22u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 23u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 24u);\n      BOOST_CHECK_EQUAL(itr[3].first.instance.value, 25u);\n      BOOST_CHECK_EQUAL(itr[4].first.instance.value, 26u);\n\n      // make older 2 feeds expire\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 3u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 24u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 25u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 26u);\n\n      // witness5 add new feed, feeds are sorted by witness_id not by feed_time\n      feed.settlement_price = bit_usd_id(db).amount(1) / core_id(db).amount(5);\n      publish_feed(bit_usd_id(db), witness5_id(db), feed);\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 4u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 24u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 25u);\n      BOOST_CHECK_EQUAL(itr[3].first.instance.value, 26u);\n\n      // another feed expires\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 3u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[1].first.instance.value, 25u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 26u);\n\n      // another feed expires\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n      bitasset_data = bit_usd_id(db).bitasset_data(db);\n      BOOST_CHECK_EQUAL(bitasset_data.feeds.size(), 2u);\n      itr = bitasset_data.feeds.begin();\n      BOOST_CHECK_EQUAL(itr[0].first.instance.value, 21u);\n      BOOST_CHECK_EQUAL(itr[2].first.instance.value, 26u);\n\n      // and so on\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/swan_tests.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n#include <graphene/chain/market_object.hpp>\n\n#include <graphene/app/database_api.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nnamespace graphene { namespace chain {\n\nstruct swan_fixture : database_fixture {\n    limit_order_id_type init_standard_swan(share_type amount = 1000) {\n        standard_users();\n        standard_asset();\n        return trigger_swan(amount, amount);\n    }\n\n    void standard_users() {\n        set_expiration( db, trx );\n        ACTORS((borrower)(borrower2)(feedproducer));\n        _borrower = borrower_id;\n        _borrower2 = borrower2_id;\n        _feedproducer = feedproducer_id;\n\n        transfer(committee_account, borrower_id, asset(init_balance));\n        transfer(committee_account, borrower2_id, asset(init_balance));\n    }\n\n    void standard_asset() {\n        set_expiration( db, trx );\n        const auto& bitusd = create_bitasset(\"USDBIT\", _feedproducer);\n        _swan = bitusd.id;\n        _back = asset_id_type();\n        update_feed_producers(swan(), {_feedproducer});\n    }\n\n    limit_order_id_type trigger_swan(share_type amount1, share_type amount2) {\n        set_expiration( db, trx );\n        // starting out with price 1:1\n        set_feed( 1, 1 );\n        // start out with 2:1 collateral\n        borrow(borrower(), swan().amount(amount1), back().amount(2*amount1));\n        borrow(borrower2(), swan().amount(amount2), back().amount(4*amount2));\n\n        FC_ASSERT( get_balance(borrower(),  swan()) == amount1 );\n        FC_ASSERT( get_balance(borrower2(), swan()) == amount2 );\n        FC_ASSERT( get_balance(borrower() , back()) == init_balance - 2*amount1 );\n        FC_ASSERT( get_balance(borrower2(), back()) == init_balance - 4*amount2 );\n\n        set_feed( 1, 2 );\n        // this sell order is designed to trigger a black swan\n        limit_order_id_type oid = create_sell_order( borrower2(), swan().amount(1), back().amount(3) )->id;\n\n        FC_ASSERT( get_balance(borrower(),  swan()) == amount1 );\n        FC_ASSERT( get_balance(borrower2(), swan()) == amount2 - 1 );\n        FC_ASSERT( get_balance(borrower() , back()) == init_balance - 2*amount1 );\n        FC_ASSERT( get_balance(borrower2(), back()) == init_balance - 2*amount2 );\n\n        BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n        return oid;\n    }\n\n    void set_feed(share_type usd, share_type core) {\n        price_feed feed;\n        feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default\n        feed.settlement_price = swan().amount(usd) / back().amount(core);\n        publish_feed(swan(), feedproducer(), feed);\n    }\n\n    void expire_feed() {\n      generate_blocks(db.head_block_time() + GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME);\n      generate_block();\n      FC_ASSERT( swan().bitasset_data(db).current_feed.settlement_price.is_null() );\n    }\n\n    void wait_for_hf_core_216() {\n      generate_blocks( HARDFORK_CORE_216_TIME );\n      generate_block();\n    }\n    void wait_for_hf_core_1270() {\n       auto mi = db.get_global_properties().parameters.maintenance_interval;\n       generate_blocks(HARDFORK_CORE_1270_TIME - mi);\n       wait_for_maintenance();\n    }\n\n    void wait_for_maintenance() {\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      generate_block();\n    }\n\n    const account_object& borrower() { return _borrower(db); }\n    const account_object& borrower2() { return _borrower2(db); }\n    const account_object& feedproducer() { return _feedproducer(db); }\n    const asset_object& swan() { return _swan(db); }\n    const asset_object& back() { return _back(db); }\n\n    int64_t init_balance = 1000000;\n    account_id_type _borrower, _borrower2, _feedproducer;\n    asset_id_type _swan, _back;\n};\n\n}}\n\nBOOST_FIXTURE_TEST_SUITE( swan_tests, swan_fixture )\n\n/**\n *  This test sets up the minimum condition for a black swan to occur but does\n *  not test the full range of cases that may be possible during a black swan.\n */\nBOOST_AUTO_TEST_CASE( black_swan )\n{ try {\n      if(hf1270)\n         wait_for_hf_core_1270();\n\n      init_standard_swan();\n\n      force_settle( borrower(), swan().amount(100) );\n\n      expire_feed();\n      wait_for_hf_core_216();\n\n      force_settle( borrower(), swan().amount(100) );\n\n      set_feed( 100, 150 );\n\n      BOOST_TEST_MESSAGE( \"Verify that we cannot borrow after black swan\" );\n      GRAPHENE_REQUIRE_THROW( borrow(borrower(), swan().amount(1000), back().amount(2000)), fc::exception )\n      trx.operations.clear();\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/**\n * Black swan occurs when price feed falls, triggered by settlement\n * order.\n */\nBOOST_AUTO_TEST_CASE( black_swan_issue_346 )\n{ try {\n\n      ACTORS((buyer)(seller)(borrower)(borrower2)(settler)(feeder));\n\n      const asset_object& core = asset_id_type()(db);\n\n      int trial = 0;\n\n      vector< const account_object* > actors{ &buyer, &seller, &borrower, &borrower2, &settler, &feeder };\n\n      auto top_up = [&]()\n      {\n         for( const account_object* actor : actors )\n         {\n            int64_t bal = get_balance( *actor, core );\n            if( bal < init_balance )\n               transfer( committee_account, actor->id, asset(init_balance - bal) );\n            else if( bal > init_balance )\n               transfer( actor->id, committee_account, asset(bal - init_balance) );\n         }\n      };\n\n      auto setup_asset = [&]() -> const asset_object&\n      {\n         const asset_object& bitusd = create_bitasset(\"USDBIT\"+fc::to_string(trial)+\"X\", feeder_id);\n         update_feed_producers( bitusd, {feeder.id} );\n         BOOST_CHECK( !bitusd.bitasset_data(db).has_settlement() );\n         trial++;\n         return bitusd;\n      };\n\n      /*\n       * GRAPHENE_COLLATERAL_RATIO_DENOM\n      uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;\n      uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;\n      */\n\n      // situations to test:\n      // 1. minus short squeeze protection would be black swan, otherwise no\n      // 2. issue 346 (price feed drops followed by force settle, drop should trigger BS)\n      // 3. feed price < D/C of least collateralized short < call price < highest bid\n\n      auto set_price = [&](\n         const asset_object& bitusd,\n         const price& settlement_price\n         )\n      {\n         price_feed feed;\n         feed.settlement_price = settlement_price;\n         feed.core_exchange_rate = settlement_price;\n         wdump( (feed.max_short_squeeze_price()) );\n         publish_feed( bitusd, feeder, feed );\n      };\n\n      auto wait_for_settlement = [&]()\n      {\n         const auto& idx = db.get_index_type<force_settlement_index>().indices().get<by_expiration>();\n         const auto& itr = idx.rbegin();\n         if( itr == idx.rend() )\n            return;\n         generate_blocks( itr->settlement_date );\n         BOOST_CHECK( !idx.empty() );\n         generate_block();\n         BOOST_CHECK( idx.empty() );\n      };\n\n      {\n         const asset_object& bitusd = setup_asset();\n         top_up();\n         set_price( bitusd, bitusd.amount(1) / core.amount(5) );  // $0.20\n         borrow(borrower, bitusd.amount(100), asset(1000));       // 2x collat\n         transfer( borrower, settler, bitusd.amount(100) );\n\n         // drop to $0.02 and settle\n         BOOST_CHECK( !bitusd.bitasset_data(db).has_settlement() );\n         set_price( bitusd, bitusd.amount(1) / core.amount(50) ); // $0.02\n         BOOST_CHECK( bitusd.bitasset_data(db).has_settlement() );\n         GRAPHENE_REQUIRE_THROW( borrow( borrower2, bitusd.amount(100), asset(10000) ), fc::exception );\n         force_settle( settler, bitusd.amount(100) );\n\n         // wait for forced settlement to execute\n         // this would throw on Sep.18 testnet, see #346 (https://github.com/cryptonomex/graphene/issues/346)\n         wait_for_settlement();\n      }\n\n      // issue 350 (https://github.com/cryptonomex/graphene/issues/350)\n      {\n         // ok, new asset\n         const asset_object& bitusd = setup_asset();\n         top_up();\n         set_price( bitusd, bitusd.amount(40) / core.amount(1000) ); // $0.04\n         borrow( borrower, bitusd.amount(100), asset(5000) );    // 2x collat\n         transfer( borrower, seller, bitusd.amount(100) );\n         limit_order_id_type oid_019 = create_sell_order( seller, bitusd.amount(39), core.amount(2000) )->id;   // this order is at $0.019, we should not be able to match against it\n         limit_order_id_type oid_020 = create_sell_order( seller, bitusd.amount(40), core.amount(2000) )->id;   // this order is at $0.020, we should be able to match against it\n         set_price( bitusd, bitusd.amount(21) / core.amount(1000) ); // $0.021\n         //\n         // We attempt to match against $0.019 order and black swan,\n         // and this is intended behavior.  See discussion in ticket.\n         //\n         BOOST_CHECK( bitusd.bitasset_data(db).has_settlement() );\n         BOOST_CHECK( db.find_object( oid_019 ) != nullptr );\n         BOOST_CHECK( db.find_object( oid_020 ) == nullptr );\n      }\n\n   } catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/** Creates a black swan, recover price feed - asset should be revived\n */\nBOOST_AUTO_TEST_CASE( revive_recovered )\n{ try {\n      init_standard_swan( 700 );\n\n      if(hf1270)\n         wait_for_hf_core_1270();\n      else\n         wait_for_hf_core_216();\n\n      // revive after price recovers\n      set_feed( 700, 800 );\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n      set_feed( 701, 800 );\n      BOOST_CHECK( !swan().bitasset_data(db).has_settlement() );\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/** Creates a black swan, recover price feed - asset should be revived\n */\nBOOST_AUTO_TEST_CASE( recollateralize )\n{ try {\n      init_standard_swan( 700 );\n\n      // no hardfork yet\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(1000), swan().amount(100) ), fc::exception );\n\n      if(hf1270)\n         wait_for_hf_core_1270();\n      else\n         wait_for_hf_core_216();\n\n      int64_t b2_balance = get_balance( borrower2(), back() );\n      bid_collateral( borrower2(), back().amount(1000), swan().amount(100) );\n      BOOST_CHECK_EQUAL( get_balance( borrower2(), back() ), b2_balance - 1000 );\n      bid_collateral( borrower2(), back().amount(2000), swan().amount(200) );\n      BOOST_CHECK_EQUAL( get_balance( borrower2(), back() ), b2_balance - 2000 );\n      bid_collateral( borrower2(), back().amount(1000), swan().amount(0) );\n      BOOST_CHECK_EQUAL( get_balance( borrower2(), back() ), b2_balance );\n\n      // can't bid for non-bitassets\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), swan().amount(100), asset(100) ), fc::exception );\n      // can't cancel a non-existant bid\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(0), swan().amount(0) ), fc::exception );\n      // can't bid zero collateral\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(0), swan().amount(100) ), fc::exception );\n      // can't bid more than we have\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(b2_balance + 100), swan().amount(100) ), fc::exception );\n      trx.operations.clear();\n\n      // can't bid on a live bitasset\n      const asset_object& bitcny = create_bitasset(\"CNYBIT\", _feedproducer);\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), asset(100), bitcny.amount(100) ), fc::exception );\n      update_feed_producers(bitcny, {_feedproducer});\n      price_feed feed;\n      feed.settlement_price = bitcny.amount(1) / asset(1);\n      publish_feed( bitcny.id, _feedproducer, feed );\n      borrow( borrower2(), bitcny.amount(100), asset(1000) );\n\n      // can't bid wrong collateral type\n      GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), bitcny.amount(100), swan().amount(100) ), fc::exception );\n\n      BOOST_CHECK( swan().dynamic_data(db).current_supply == 1400 );\n      BOOST_CHECK( swan().bitasset_data(db).settlement_fund == 2800 );\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n      BOOST_CHECK( swan().bitasset_data(db).current_feed.settlement_price.is_null() );\n\n      // doesn't happen without price feed\n      bid_collateral( borrower(),  back().amount(1400), swan().amount(700) );\n      bid_collateral( borrower2(), back().amount(1400), swan().amount(700) );\n      wait_for_maintenance();\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      set_feed(1, 2);\n      // doesn't happen if cover is insufficient\n      bid_collateral( borrower2(), back().amount(1400), swan().amount(600) );\n      wait_for_maintenance();\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      set_feed(1, 2);\n      // doesn't happen if some bids have a bad swan price\n      bid_collateral( borrower2(), back().amount(1050), swan().amount(700) );\n      wait_for_maintenance();\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      set_feed(1, 2);\n      // works\n      bid_collateral( borrower(),  back().amount(1051), swan().amount(700) );\n      bid_collateral( borrower2(), back().amount(2100), swan().amount(1399) );\n\n      // check get_collateral_bids\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      GRAPHENE_REQUIRE_THROW( db_api.get_collateral_bids(back().symbol, 100, 0), fc::assert_exception );\n      auto swan_symbol = _swan(db).symbol;\n      vector<collateral_bid_object> bids = db_api.get_collateral_bids(swan_symbol, 100, 1);\n      BOOST_CHECK_EQUAL( 1u, bids.size() );\n      FC_ASSERT( _borrower2 == bids[0].bidder );\n      bids = db_api.get_collateral_bids(swan_symbol, 1, 0);\n      BOOST_CHECK_EQUAL( 1u, bids.size() );\n      FC_ASSERT( _borrower == bids[0].bidder );\n      bids = db_api.get_collateral_bids(swan_symbol, 100, 0);\n      BOOST_CHECK_EQUAL( 2u, bids.size() );\n      FC_ASSERT( _borrower == bids[0].bidder );\n      FC_ASSERT( _borrower2 == bids[1].bidder );\n\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n      // revive\n      wait_for_maintenance();\n      BOOST_CHECK( !swan().bitasset_data(db).has_settlement() );\n      bids = db_api.get_collateral_bids(swan_symbol, 100, 0);\n      BOOST_CHECK( bids.empty() );\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/** Creates a black swan, bid, adjust bid before/after hf_1692\n */\nBOOST_AUTO_TEST_CASE( bid_issue_1692 )\n{ try {\n   init_standard_swan( 700 );\n\n   generate_blocks( HARDFORK_CORE_1692_TIME - 30 );\n\n   int64_t b2_balance = get_balance( borrower2(), back() );\n   bid_collateral( borrower2(), back().amount(1000), swan().amount(100) );\n   BOOST_CHECK_EQUAL( get_balance( borrower2(), back() ), b2_balance - 1000 );\n   GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(b2_balance), swan().amount(200) ),\n                           fc::assert_exception );\n   GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(b2_balance-999), swan().amount(200) ),\n                           fc::assert_exception );\n\n   generate_blocks( HARDFORK_CORE_1692_TIME + 30 );\n\n   bid_collateral( borrower2(), back().amount(b2_balance-999), swan().amount(200) );\n   BOOST_CHECK_EQUAL( get_balance( borrower2(), back() ), 999 );\n   bid_collateral( borrower2(), back().amount(b2_balance), swan().amount(200) );\n   BOOST_CHECK_EQUAL( get_balance( borrower2(), back() ), 0 );\n} FC_LOG_AND_RETHROW() }\n\n/** Creates a black swan, settles all debts, recovers price feed - asset should be revived\n */\nBOOST_AUTO_TEST_CASE( revive_empty_recovered )\n{ try {\n      limit_order_id_type oid = init_standard_swan( 1000 );\n\n      if(hf1270)\n         wait_for_hf_core_1270();\n      else\n         wait_for_hf_core_216();\n\n      set_expiration( db, trx );\n      cancel_limit_order( oid(db) );\n      force_settle( borrower(), swan().amount(1000) );\n      force_settle( borrower2(), swan().amount(1000) );\n      BOOST_CHECK_EQUAL( 0, swan().dynamic_data(db).current_supply.value );\n      BOOST_CHECK_EQUAL( 0, swan().bitasset_data(db).settlement_fund.value );\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      // revive after price recovers\n      set_feed( 1, 1 );\n      BOOST_CHECK( !swan().bitasset_data(db).has_settlement() );\n\n      auto& call_idx = db.get_index_type<call_order_index>().indices().get<by_account>();\n      auto itr = call_idx.find( boost::make_tuple(_feedproducer, _swan) );\n      BOOST_CHECK( itr == call_idx.end() );\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/** Creates a black swan, settles all debts - asset should be revived in next maintenance\n */\nBOOST_AUTO_TEST_CASE( revive_empty )\n{ try {\n      if(hf1270)\n         wait_for_hf_core_1270();\n      else\n         wait_for_hf_core_216();\n\n      limit_order_id_type oid = init_standard_swan( 1000 );\n\n      cancel_limit_order( oid(db) );\n      force_settle( borrower(), swan().amount(1000) );\n      force_settle( borrower2(), swan().amount(1000) );\n      BOOST_CHECK_EQUAL( 0, swan().dynamic_data(db).current_supply.value );\n\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      // revive\n      wait_for_maintenance();\n      BOOST_CHECK( !swan().bitasset_data(db).has_settlement() );\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/** Creates a black swan, settles all debts - asset should be revived in next maintenance\n */\nBOOST_AUTO_TEST_CASE( revive_empty_with_bid )\n{ try {\n      if(hf1270)\n         wait_for_hf_core_1270();\n      else\n         wait_for_hf_core_216();\n\n      standard_users();\n      standard_asset();\n\n      set_feed( 1, 1 );\n      borrow(borrower(), swan().amount(1000), back().amount(2000));\n      borrow(borrower2(), swan().amount(1000), back().amount(1967));\n\n      set_feed( 1, 2 );\n      // this sell order is designed to trigger a black swan\n      limit_order_id_type oid = create_sell_order( borrower2(), swan().amount(1), back().amount(3) )->id;\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      cancel_limit_order( oid(db) );\n      force_settle( borrower(), swan().amount(500) );\n      force_settle( borrower(), swan().amount(500) );\n      force_settle( borrower2(), swan().amount(667) );\n      force_settle( borrower2(), swan().amount(333) );\n      BOOST_CHECK_EQUAL( 0, swan().dynamic_data(db).current_supply.value );\n      BOOST_CHECK_EQUAL( 0, swan().bitasset_data(db).settlement_fund.value );\n\n      bid_collateral( borrower(), back().amount(3000), swan().amount(700) );\n\n      BOOST_CHECK( swan().bitasset_data(db).has_settlement() );\n\n      // revive\n      wait_for_maintenance();\n      BOOST_CHECK( !swan().bitasset_data(db).has_settlement() );\n      graphene::app::database_api db_api( db, &( app.get_options() ));\n      auto swan_symbol = _swan(db).symbol;\n      vector<collateral_bid_object> bids = db_api.get_collateral_bids(swan_symbol, 100, 0);\n      BOOST_CHECK( bids.empty() );\n\n      auto& call_idx = db.get_index_type<call_order_index>().indices().get<by_account>();\n      auto itr = call_idx.find( boost::make_tuple(_borrower, _swan) );\n      BOOST_CHECK( itr == call_idx.end() );\n      itr = call_idx.find( boost::make_tuple(_feedproducer, _swan) );\n      BOOST_CHECK( itr == call_idx.end() );\n} catch( const fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE(black_swan_after_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(black_swan);\n\n} FC_LOG_AND_RETHROW() }\n\n// black_swan_issue_346_hf1270 is skipped as it is already failing with HARDFORK_CORE_834_TIME\n\nBOOST_AUTO_TEST_CASE(revive_recovered_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(revive_recovered);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(recollateralize_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(recollateralize);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(revive_empty_recovered_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(revive_empty_recovered);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(revive_empty_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(revive_empty);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(revive_empty_with_bid_hf1270)\n{ try {\n   hf1270 = true;\n   INVOKE(revive_empty_with_bid);\n\n} FC_LOG_AND_RETHROW() }\n\n/** Creates a black swan, bids on more than outstanding debt\n */\nBOOST_AUTO_TEST_CASE( overflow )\n{ try {\n   init_standard_swan( 700 );\n\n   wait_for_hf_core_216();\n\n   bid_collateral( borrower(),  back().amount(2200), swan().amount(GRAPHENE_MAX_SHARE_SUPPLY - 1) );\n   bid_collateral( borrower2(), back().amount(2100), swan().amount(1399) );\n   set_feed(1, 2);\n   wait_for_maintenance();\n\n   auto& call_idx = db.get_index_type<call_order_index>().indices().get<by_account>();\n   auto itr = call_idx.find( boost::make_tuple(_borrower, _swan) );\n   BOOST_REQUIRE( itr != call_idx.end() );\n   BOOST_CHECK_EQUAL( 1, itr->debt.value );\n   itr = call_idx.find( boost::make_tuple(_borrower2, _swan) );\n   BOOST_REQUIRE( itr != call_idx.end() );\n   BOOST_CHECK_EQUAL( 1399, itr->debt.value );\n\n   BOOST_CHECK( !swan().bitasset_data(db).has_settlement() );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/uia_tests.cpp",
    "content": "/*\n * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/chain/database.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n#include <graphene/chain/is_authorized_asset.hpp>\n\n#include <graphene/chain/account_object.hpp>\n#include <graphene/chain/asset_object.hpp>\n\n#include <fc/crypto/digest.hpp>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\nBOOST_FIXTURE_TEST_SUITE( uia_tests, database_fixture )\n\nBOOST_AUTO_TEST_CASE( create_advanced_uia )\n{\n   try {\n      asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();\n      asset_create_operation creator;\n      creator.issuer = account_id_type();\n      creator.fee = asset();\n      creator.symbol = \"ADVANCED\";\n      creator.common_options.max_supply = 100000000;\n      creator.precision = 2;\n      creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/\n      creator.common_options.issuer_permissions = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential;\n      creator.common_options.flags = charge_market_fee|white_list|override_authority|disable_confidential;\n      creator.common_options.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1)));\n      creator.common_options.whitelist_authorities = creator.common_options.blacklist_authorities = {account_id_type()};\n\n      trx.operations.push_back(std::move(creator));\n      PUSH_TX( db, trx, ~0 );\n\n      const asset_object& test_asset = test_asset_id(db);\n      BOOST_CHECK(test_asset.symbol == \"ADVANCED\");\n      BOOST_CHECK(asset(1, test_asset_id) * test_asset.options.core_exchange_rate == asset(2));\n      BOOST_CHECK(test_asset.options.flags & white_list);\n      BOOST_CHECK(test_asset.options.max_supply == 100000000);\n      BOOST_CHECK(!test_asset.bitasset_data_id.valid());\n      BOOST_CHECK(test_asset.options.market_fee_percent == GRAPHENE_MAX_MARKET_FEE_PERCENT/100);\n\n      const asset_dynamic_data_object& test_asset_dynamic_data = test_asset.dynamic_asset_data_id(db);\n      BOOST_CHECK(test_asset_dynamic_data.current_supply == 0);\n      BOOST_CHECK(test_asset_dynamic_data.accumulated_fees == 0);\n      BOOST_CHECK(test_asset_dynamic_data.fee_pool == 0);\n\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( override_transfer_test )\n{ try {\n   ACTORS( (dan)(eric)(sam) );\n   const asset_object& advanced = create_user_issued_asset( \"ADVANCED\", sam, override_authority );\n   BOOST_TEST_MESSAGE( \"Issuing 1000 ADVANCED to dan\" );\n   issue_uia( dan, advanced.amount( 1000 ) );\n   BOOST_TEST_MESSAGE( \"Checking dan's balance\" );\n   BOOST_REQUIRE_EQUAL( get_balance( dan, advanced ), 1000 );\n\n   override_transfer_operation otrans;\n   otrans.issuer = advanced.issuer;\n   otrans.from = dan.id;\n   otrans.to   = eric.id;\n   otrans.amount = advanced.amount(100);\n   trx.operations.push_back(otrans);\n\n   BOOST_TEST_MESSAGE( \"Require throwing without signature\" );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), tx_missing_active_auth );\n   BOOST_TEST_MESSAGE( \"Require throwing with dan's signature\" );\n   sign( trx,  dan_private_key  );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), tx_missing_active_auth );\n   BOOST_TEST_MESSAGE( \"Pass with issuer's signature\" );\n   trx.clear_signatures();\n   sign( trx,  sam_private_key  );\n   PUSH_TX( db, trx, 0 );\n\n   BOOST_REQUIRE_EQUAL( get_balance( dan, advanced ), 900 );\n   BOOST_REQUIRE_EQUAL( get_balance( eric, advanced ), 100 );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( override_transfer_test2 )\n{ try {\n   ACTORS( (dan)(eric)(sam) );\n   const asset_object& advanced = create_user_issued_asset( \"ADVANCED\", sam, 0 );\n   issue_uia( dan, advanced.amount( 1000 ) );\n   BOOST_REQUIRE_EQUAL( get_balance( dan, advanced ), 1000 );\n\n   trx.operations.clear();\n   override_transfer_operation otrans;\n   otrans.issuer = advanced.issuer;\n   otrans.from = dan.id;\n   otrans.to   = eric.id;\n   otrans.amount = advanced.amount(100);\n   trx.operations.push_back(otrans);\n\n   BOOST_TEST_MESSAGE( \"Require throwing without signature\" );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);\n   BOOST_TEST_MESSAGE( \"Require throwing with dan's signature\" );\n   sign( trx,  dan_private_key  );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception);\n   BOOST_TEST_MESSAGE( \"Fail because overide_authority flag is not set\" );\n   trx.clear_signatures();\n   sign( trx,  sam_private_key  );\n   GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, 0 ), fc::exception );\n\n   BOOST_REQUIRE_EQUAL( get_balance( dan, advanced ), 1000 );\n   BOOST_REQUIRE_EQUAL( get_balance( eric, advanced ), 0 );\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE( issue_whitelist_uia )\n{\n   try {\n      account_id_type izzy_id = create_account(\"izzy\").id;\n      const asset_id_type uia_id = create_user_issued_asset(\n         \"ADVANCED\", izzy_id(db), white_list ).id;\n      account_id_type nathan_id = create_account(\"nathan\").id;\n      account_id_type vikram_id = create_account(\"vikram\").id;\n      trx.clear();\n\n      asset_issue_operation op;\n      op.issuer = uia_id(db).issuer;\n      op.asset_to_issue = asset(1000, uia_id);\n      op.issue_to_account = nathan_id;\n      trx.operations.emplace_back(op);\n      set_expiration( db, trx );\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK(is_authorized_asset( db, nathan_id(db), uia_id(db) ));\n      BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000);\n\n      // committee-account is free as well\n      BOOST_CHECK( is_authorized_asset( db, account_id_type()(db), uia_id(db) ) );\n\n      // Make a whitelist, now it should fail\n      {\n         BOOST_TEST_MESSAGE( \"Changing the whitelist authority\" );\n         asset_update_operation uop;\n         uop.issuer = izzy_id;\n         uop.asset_to_update = uia_id;\n         uop.new_options = uia_id(db).options;\n         uop.new_options.whitelist_authorities.insert(izzy_id);\n         trx.operations.back() = uop;\n         PUSH_TX( db, trx, ~0 );\n         BOOST_CHECK( uia_id(db).options.whitelist_authorities.find(izzy_id) != uia_id(db).options.whitelist_authorities.end() );\n      }\n\n      // Fail because there is a whitelist authority and I'm not whitelisted\n      trx.operations.back() = op;\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );\n\n      // committee-account is blocked as well\n      BOOST_CHECK( !is_authorized_asset( db, account_id_type()(db), uia_id(db) ) );\n\n      account_whitelist_operation wop;\n      wop.authorizing_account = izzy_id;\n      wop.account_to_list = vikram_id;\n      wop.new_listing = account_whitelist_operation::white_listed;\n\n      trx.operations.back() = wop;\n      // Fail because whitelist function is restricted to members only\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );\n      upgrade_to_lifetime_member( izzy_id );\n      trx.operations.clear();\n      trx.operations.push_back( wop );\n      PUSH_TX( db, trx, ~0 );\n\n      // Still fail after an irrelevant account was added\n      trx.operations.back() = op;\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );\n\n      wop.account_to_list = nathan_id;\n      trx.operations.back() = wop;\n      PUSH_TX( db, trx, ~0 );\n      trx.operations.back() = op;\n      BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000);\n      // Finally succeed when we were whitelisted\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 2000);\n\n      // committee-account is still blocked\n      BOOST_CHECK( !is_authorized_asset( db, account_id_type()(db), uia_id(db) ) );\n      // izzy is still blocked\n      BOOST_CHECK( !is_authorized_asset( db, izzy_id(db), uia_id(db) ) );\n\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_CASE( transfer_whitelist_uia )\n{\n   try {\n      INVOKE(issue_whitelist_uia);\n      const asset_object& advanced = get_asset(\"ADVANCED\");\n      const asset_id_type uia_id = advanced.id;\n      const account_object& nathan = get_account(\"nathan\");\n      const account_object& dan = create_account(\"dan\");\n      account_id_type izzy_id = get_account(\"izzy\").id;\n      upgrade_to_lifetime_member(dan);\n      trx.clear();\n\n      BOOST_TEST_MESSAGE( \"Atempting to transfer asset ADVANCED from nathan to dan when dan is not whitelisted, should fail\" );\n      transfer_operation op;\n      op.fee = advanced.amount(0);\n      op.from = nathan.id;\n      op.to = dan.id;\n      op.amount = advanced.amount(100); //({advanced.amount(0), nathan.id, dan.id, advanced.amount(100)});\n      trx.operations.push_back(op);\n      //Fail because dan is not whitelisted.\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), transfer_to_account_not_whitelisted );\n\n      BOOST_TEST_MESSAGE( \"Adding dan to whitelist for asset ADVANCED\" );\n      account_whitelist_operation wop;\n      wop.authorizing_account = izzy_id;\n      wop.account_to_list = dan.id;\n      wop.new_listing = account_whitelist_operation::white_listed;\n      trx.operations.back() = wop;\n      PUSH_TX( db, trx, ~0 );\n      BOOST_TEST_MESSAGE( \"Attempting to transfer from nathan to dan after whitelisting dan, should succeed\" );\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n\n      BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1900);\n      BOOST_CHECK_EQUAL(get_balance(dan, advanced), 100);\n\n      BOOST_TEST_MESSAGE( \"Attempting to blacklist nathan\" );\n      {\n         BOOST_TEST_MESSAGE( \"Changing the blacklist authority\" );\n         asset_update_operation uop;\n         uop.issuer = izzy_id;\n         uop.asset_to_update = advanced.id;\n         uop.new_options = advanced.options;\n         uop.new_options.blacklist_authorities.insert(izzy_id);\n         trx.operations.back() = uop;\n         PUSH_TX( db, trx, ~0 );\n         BOOST_CHECK( advanced.options.blacklist_authorities.find(izzy_id) != advanced.options.blacklist_authorities.end() );\n      }\n\n      wop.new_listing |= account_whitelist_operation::black_listed;\n      wop.account_to_list = nathan.id;\n      trx.operations.back() = wop;\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK( !(is_authorized_asset( db, nathan, advanced )) );\n\n      BOOST_TEST_MESSAGE( \"Attempting to transfer from nathan after blacklisting, should fail\" );\n      op.amount = advanced.amount(50);\n      trx.operations.back() = op;\n      // it fails because the fees are not in a whitelisted asset\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception );\n\n      BOOST_TEST_MESSAGE( \"Attempting to burn from nathan after blacklisting, should fail\" );\n      asset_reserve_operation burn;\n      burn.payer = nathan.id;\n      burn.amount_to_reserve = advanced.amount(10);\n      trx.operations.back() = burn;\n      //Fail because nathan is blacklisted\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n      BOOST_TEST_MESSAGE( \"Attempting transfer from dan back to nathan, should fail because nathan is blacklisted\" );\n      std::swap(op.from, op.to);\n      trx.operations.back() = op;\n      //Fail because nathan is blacklisted\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n\n      {\n         BOOST_TEST_MESSAGE( \"Changing the blacklist authority to dan\" );\n         asset_update_operation op;\n         op.issuer = izzy_id;\n         op.asset_to_update = advanced.id;\n         op.new_options = advanced.options;\n         op.new_options.blacklist_authorities.clear();\n         op.new_options.blacklist_authorities.insert(dan.id);\n         trx.operations.back() = op;\n         PUSH_TX( db, trx, ~0 );\n         BOOST_CHECK(advanced.options.blacklist_authorities.find(dan.id) != advanced.options.blacklist_authorities.end());\n      }\n\n      BOOST_TEST_MESSAGE( \"Attempting to transfer from dan back to nathan\" );\n      trx.operations.back() = op;\n      PUSH_TX( db, trx, ~0 );\n      BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1950);\n      BOOST_CHECK_EQUAL(get_balance(dan, advanced), 50);\n\n      BOOST_TEST_MESSAGE( \"Blacklisting nathan by dan\" );\n      wop.authorizing_account = dan.id;\n      wop.account_to_list = nathan.id;\n      wop.new_listing = account_whitelist_operation::black_listed;\n      trx.operations.back() = wop;\n      PUSH_TX( db, trx, ~0 );\n\n      trx.operations.back() = op;\n      //Fail because nathan is blacklisted\n      BOOST_CHECK(!is_authorized_asset( db, nathan, advanced ));\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n\n      //Remove nathan from committee's whitelist, add him to dan's. This should not authorize him to hold ADVANCED.\n      wop.authorizing_account = izzy_id;\n      wop.account_to_list = nathan.id;\n      wop.new_listing = account_whitelist_operation::no_listing;\n      trx.operations.back() = wop;\n      PUSH_TX( db, trx, ~0 );\n      wop.authorizing_account = dan.id;\n      wop.account_to_list = nathan.id;\n      wop.new_listing = account_whitelist_operation::white_listed;\n      trx.operations.back() = wop;\n      PUSH_TX( db, trx, ~0 );\n\n      trx.operations.back() = op;\n      //Fail because nathan is not whitelisted\n      BOOST_CHECK(!is_authorized_asset( db, nathan, advanced ));\n      GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);\n\n      burn.payer = dan.id;\n      burn.amount_to_reserve = advanced.amount(10);\n      trx.operations.back() = burn;\n      PUSH_TX(db, trx, ~0);\n      BOOST_CHECK_EQUAL(get_balance(dan, advanced), 40);\n\n      // committee-account is still blocked\n      BOOST_CHECK( !is_authorized_asset( db, account_id_type()(db), uia_id(db) ) );\n      // izzy is still blocked\n      BOOST_CHECK( !is_authorized_asset( db, izzy_id(db), uia_id(db) ) );\n\n      // Pass BSIP 86 hardfork\n      generate_blocks( HARDFORK_BSIP_86_TIME );\n\n      // committee-account is now unblocked\n      BOOST_CHECK( is_authorized_asset( db, account_id_type()(db), uia_id(db) ) );\n      // izzy is still blocked\n      BOOST_CHECK( !is_authorized_asset( db, izzy_id(db), uia_id(db) ) );\n\n   } catch(fc::exception& e) {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/**\n * verify that issuers can halt transfers\n */\nBOOST_AUTO_TEST_CASE( transfer_restricted_test )\n{\n   try\n   {\n      ACTORS( (sam)(alice)(bob) );\n\n      BOOST_TEST_MESSAGE( \"Issuing 1000 UIA to Alice\" );\n\n      auto _issue_uia = [&]( const account_object& recipient, asset amount )\n      {\n         asset_issue_operation op;\n         op.issuer = amount.asset_id(db).issuer;\n         op.asset_to_issue = amount;\n         op.issue_to_account = recipient.id;\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      } ;\n\n      const asset_object& uia = create_user_issued_asset( \"TXRX\", sam, transfer_restricted );\n      _issue_uia( alice, uia.amount( 1000 ) );\n\n      auto _restrict_xfer = [&]( bool xfer_flag )\n      {\n         asset_update_operation op;\n         op.issuer = sam_id;\n         op.asset_to_update = uia.id;\n         op.new_options = uia.options;\n         if( xfer_flag )\n            op.new_options.flags |= transfer_restricted;\n         else\n            op.new_options.flags &= ~transfer_restricted;\n         transaction tx;\n         tx.operations.push_back( op );\n         set_expiration( db, tx );\n         PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );\n      } ;\n\n      BOOST_TEST_MESSAGE( \"Enable transfer_restricted, send fails\" );\n\n      transfer_operation xfer_op;\n      xfer_op.from = alice_id;\n      xfer_op.to = bob_id;\n      xfer_op.amount = uia.amount(100);\n      signed_transaction xfer_tx;\n      xfer_tx.operations.push_back( xfer_op );\n      set_expiration( db, xfer_tx );\n      sign( xfer_tx, alice_private_key );\n\n      _restrict_xfer( true );\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, xfer_tx ), transfer_restricted_transfer_asset );\n\n      BOOST_TEST_MESSAGE( \"Disable transfer_restricted, send succeeds\" );\n\n      _restrict_xfer( false );\n      PUSH_TX( db, xfer_tx );\n\n      xfer_op.amount = uia.amount(101);\n\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\n/***\n * Test to see if a asset name is valid\n * @param db the database\n * @param acct the account that will attempt to create the asset\n * @param asset_name the asset_name\n * @param allowed whether the creation should be successful\n * @returns true if meets expectations\n */\nbool test_asset_name(graphene::chain::database_fixture* db, const graphene::chain::account_object& acct, std::string asset_name, bool allowed)\n{\n   if (allowed)\n   {\n      try\n      {\n         db->create_user_issued_asset(asset_name, acct, 0);\n      } catch (...)\n      {\n         return false;\n      }\n   }\n   else\n   {\n      try\n      {\n         db->create_user_issued_asset(asset_name, acct, 0);\n         return false;\n      } catch (fc::exception& ex) \n      {\n         return true;\n      } catch (...)\n      {\n         return false;\n      }\n   }\n   return true;\n}\n\n/***\n * Test to see if an ascii character can be used in an asset name\n * @param c the ascii character (NOTE: includes extended ascii up to 255)\n * @param allowed_beginning true if it should be allowed as the first character of an asset name\n * @param allowed_middle true if it should be allowed in the middle of an asset name\n * @param allowed_end true if it should be allowed at the end of an asset name\n * @returns true if tests met expectations\n */\nbool test_asset_char(graphene::chain::database_fixture* db, const graphene::chain::account_object& acct, const unsigned char& c, bool allowed_beginning, bool allowed_middle, bool allowed_end)\n{\n   std::ostringstream asset_name;\n   // beginning\n   asset_name << c << \"CHARLIE\";\n   if (!test_asset_name(db, acct, asset_name.str(), allowed_beginning))\n      return false;\n\n   // middle\n   asset_name.str(\"\");\n   asset_name.clear();\n   asset_name << \"CHAR\" << c << \"LIE\";\n   if (!test_asset_name(db, acct, asset_name.str(), allowed_middle))\n      return false;\n\n   // end\n   asset_name.str(\"\");\n   asset_name.clear();\n   asset_name << \"CHARLIE\" << c;\n   return test_asset_name(db, acct, asset_name.str(), allowed_end);\n}\n\nBOOST_AUTO_TEST_CASE( asset_name_test )\n{\n   try\n   {\n      ACTORS( (alice)(bob)(sam) );\n\n      auto has_asset = [&]( std::string symbol ) -> bool\n      {\n         const auto& assets_by_symbol = db.get_index_type<asset_index>().indices().get<by_symbol>();\n         return assets_by_symbol.find( symbol ) != assets_by_symbol.end();\n      };\n\n      // Alice creates asset \"ALPHA\"\n      BOOST_CHECK( !has_asset(\"ALPHA\") );    BOOST_CHECK( !has_asset(\"ALPHA.ONE\") );\n      create_user_issued_asset( \"ALPHA\", alice_id(db), 0 );\n      BOOST_CHECK(  has_asset(\"ALPHA\") );    BOOST_CHECK( !has_asset(\"ALPHA.ONE\") );\n\n      // Nobody can create another asset named ALPHA\n      GRAPHENE_REQUIRE_THROW( create_user_issued_asset( \"ALPHA\",   bob_id(db), 0 ), fc::exception );\n      BOOST_CHECK(  has_asset(\"ALPHA\") );    BOOST_CHECK( !has_asset(\"ALPHA.ONE\") );\n      GRAPHENE_REQUIRE_THROW( create_user_issued_asset( \"ALPHA\", alice_id(db), 0 ), fc::exception );\n      BOOST_CHECK(  has_asset(\"ALPHA\") );    BOOST_CHECK( !has_asset(\"ALPHA.ONE\") );\n\n      generate_blocks( HARDFORK_385_TIME );\n      generate_block();\n\n      // Bob can't create ALPHA.ONE\n      GRAPHENE_REQUIRE_THROW( create_user_issued_asset( \"ALPHA.ONE\", bob_id(db), 0 ), fc::exception );\n      BOOST_CHECK(  has_asset(\"ALPHA\") );    BOOST_CHECK( !has_asset(\"ALPHA.ONE\") );\n\n      // Alice can create ALPHA.ONE\n      create_user_issued_asset( \"ALPHA.ONE\", alice_id(db), 0 );\n      BOOST_CHECK(  has_asset(\"ALPHA\") );    BOOST_CHECK( has_asset(\"ALPHA.ONE\") );\n\n      // create a proposal to create asset ending in a number\n      auto& core = asset_id_type()(db);\n      asset_create_operation op_p;\n      op_p.issuer = alice_id;\n      op_p.symbol = \"SP500\";\n      op_p.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );\n      op_p.fee = core.amount(0);\n\n      const auto& curfees = db.get_global_properties().parameters.get_current_fees();\n      const auto& proposal_create_fees = curfees.get<proposal_create_operation>();\n      proposal_create_operation prop;\n      prop.fee_paying_account = alice_id;\n      prop.proposed_ops.emplace_back( op_p );\n      prop.expiration_time =  db.head_block_time() + fc::days(1);\n      prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );\n\n      signed_transaction tx;\n      tx.operations.push_back( prop );\n      db.current_fee_schedule().set_fee( tx.operations.back() );\n      set_expiration( db, tx );\n      sign( tx, alice_private_key );\n      PUSH_TX( db, tx );\n\n      generate_block();\n\n      // Sam can create asset ending in number after hf_620\n      create_user_issued_asset( \"NIKKEI225\", sam_id(db), 0 );\n      BOOST_CHECK(  has_asset(\"NIKKEI225\") );\n\n      // make sure other assets can still be created after hf_620\n      create_user_issued_asset( \"ALPHA2\", alice_id(db), 0 );\n      create_user_issued_asset( \"ALPHA2.ONE\", alice_id(db), 0 );\n      BOOST_CHECK(  has_asset(\"ALPHA2\") );\n      BOOST_CHECK( has_asset(\"ALPHA2.ONE\") );\n\n      // proposal to create asset ending in number will now be created successfully as we are in > hf_620 time\n      prop.expiration_time =  db.head_block_time() + fc::days(3);\n      signed_transaction tx_hf620;\n      tx_hf620.operations.push_back( prop );\n      db.current_fee_schedule().set_fee( tx_hf620.operations.back() );\n      set_expiration( db, tx_hf620 );\n      sign( tx_hf620, alice_private_key );\n      PUSH_TX( db, tx_hf620 );\n\n      // assets with invalid characters should not be allowed\n      unsigned char c = 0;\n      do\n      {\n         if ( (c >= 48 && c <= 57) ) // numbers\n            BOOST_CHECK_MESSAGE( test_asset_char(this, alice_id(db), c, false, true, true), \"Failed on good ASCII value \" + std::to_string(c) );\n         else if ( c >= 65 && c <= 90) // letters\n            BOOST_CHECK_MESSAGE( test_asset_char(this, alice_id(db), c, true, true, true), \"Failed on good ASCII value \" + std::to_string(c) );\n         else                       // everything else\n            BOOST_CHECK_MESSAGE( test_asset_char(this, alice_id(db), c, false, false, false), \"Failed on bad ASCII value \" + std::to_string(c) );\n         c++;\n      } while (c != 0);\n   }\n   catch(fc::exception& e)\n   {\n      edump((e.to_detail_string()));\n      throw;\n   }\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/voting_tests.cpp",
    "content": "/*\n * Copyright (c) 2018 oxarbitrage, and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/database_api.hpp>\n#include <graphene/chain/exceptions.hpp>\n#include <graphene/chain/hardfork.hpp>\n\n#include <iostream>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\n\n\nBOOST_FIXTURE_TEST_SUITE(voting_tests, database_fixture)\n\nBOOST_FIXTURE_TEST_CASE( committee_account_initialization_test, database_fixture )\n{ try {\n   // Check current default committee\n   // By default chain is configured with INITIAL_COMMITTEE_MEMBER_COUNT=9 members\n   const auto &committee_members = db.get_global_properties().active_committee_members;\n   const auto &committee = committee_account(db);\n\n   BOOST_CHECK_EQUAL(committee_members.size(), INITIAL_COMMITTEE_MEMBER_COUNT);\n   BOOST_CHECK_EQUAL(committee.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT);\n\n   generate_blocks(HARDFORK_533_TIME);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n   generate_block();\n   set_expiration(db, trx);\n\n   // Check that committee not changed after 533 hardfork\n   // vote counting method changed, but any votes are absent\n   const auto &committee_members_after_hf533 = db.get_global_properties().active_committee_members;\n   const auto &committee_after_hf533 = committee_account(db);\n   BOOST_CHECK_EQUAL(committee_members_after_hf533.size(), INITIAL_COMMITTEE_MEMBER_COUNT);\n   BOOST_CHECK_EQUAL(committee_after_hf533.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT);\n\n   // You can't use uninitialized committee after 533 hardfork\n   // when any user with stake created (create_account method automatically set up votes for committee)\n   // committee is incomplete and consist of random active members\n   ACTOR(alice);\n   fund(alice);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   const auto &committee_after_hf533_with_stake = committee_account(db);\n   BOOST_CHECK_LT(committee_after_hf533_with_stake.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT);\n\n   // Initialize committee by voting for each memeber and for desired count\n   vote_for_committee_and_witnesses(INITIAL_COMMITTEE_MEMBER_COUNT, INITIAL_WITNESS_COUNT);\n   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n\n   const auto &committee_members_after_hf533_and_init = db.get_global_properties().active_committee_members;\n   const auto &committee_after_hf533_and_init = committee_account(db);\n   BOOST_CHECK_EQUAL(committee_members_after_hf533_and_init.size(), INITIAL_COMMITTEE_MEMBER_COUNT);\n   BOOST_CHECK_EQUAL(committee_after_hf533_and_init.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT);\n\n} FC_LOG_AND_RETHROW() }\n\nBOOST_AUTO_TEST_CASE(put_my_witnesses)\n{\n   try\n   {\n      ACTORS( (witness0)\n              (witness1)\n              (witness2)\n              (witness3)\n              (witness4)\n              (witness5)\n              (witness6)\n              (witness7)\n              (witness8)\n              (witness9)\n              (witness10)\n              (witness11)\n              (witness12)\n              (witness13) );\n\n      // Upgrade all accounts to LTM\n      upgrade_to_lifetime_member(witness0_id);\n      upgrade_to_lifetime_member(witness1_id);\n      upgrade_to_lifetime_member(witness2_id);\n      upgrade_to_lifetime_member(witness3_id);\n      upgrade_to_lifetime_member(witness4_id);\n      upgrade_to_lifetime_member(witness5_id);\n      upgrade_to_lifetime_member(witness6_id);\n      upgrade_to_lifetime_member(witness7_id);\n      upgrade_to_lifetime_member(witness8_id);\n      upgrade_to_lifetime_member(witness9_id);\n      upgrade_to_lifetime_member(witness10_id);\n      upgrade_to_lifetime_member(witness11_id);\n      upgrade_to_lifetime_member(witness12_id);\n      upgrade_to_lifetime_member(witness13_id);\n\n      // Create all the witnesses\n      const witness_id_type witness0_witness_id = create_witness(witness0_id, witness0_private_key).id;\n      const witness_id_type witness1_witness_id = create_witness(witness1_id, witness1_private_key).id;\n      const witness_id_type witness2_witness_id = create_witness(witness2_id, witness2_private_key).id;\n      const witness_id_type witness3_witness_id = create_witness(witness3_id, witness3_private_key).id;\n      const witness_id_type witness4_witness_id = create_witness(witness4_id, witness4_private_key).id;\n      const witness_id_type witness5_witness_id = create_witness(witness5_id, witness5_private_key).id;\n      const witness_id_type witness6_witness_id = create_witness(witness6_id, witness6_private_key).id;\n      const witness_id_type witness7_witness_id = create_witness(witness7_id, witness7_private_key).id;\n      const witness_id_type witness8_witness_id = create_witness(witness8_id, witness8_private_key).id;\n      const witness_id_type witness9_witness_id = create_witness(witness9_id, witness9_private_key).id;\n      const witness_id_type witness10_witness_id = create_witness(witness10_id, witness10_private_key).id;\n      const witness_id_type witness11_witness_id = create_witness(witness11_id, witness11_private_key).id;\n      const witness_id_type witness12_witness_id = create_witness(witness12_id, witness12_private_key).id;\n      const witness_id_type witness13_witness_id = create_witness(witness13_id, witness13_private_key).id;\n\n      // Create a vector with private key of all witnesses, will be used to activate 9 witnesses at a time\n      const vector <fc::ecc::private_key> private_keys = {\n            witness0_private_key,\n            witness1_private_key,\n            witness2_private_key,\n            witness3_private_key,\n            witness4_private_key,\n            witness5_private_key,\n            witness6_private_key,\n            witness7_private_key,\n            witness8_private_key,\n            witness9_private_key,\n            witness10_private_key,\n            witness11_private_key,\n            witness12_private_key,\n            witness13_private_key\n\n      };\n\n      // create a map with account id and witness id\n      const flat_map <account_id_type, witness_id_type> witness_map = {\n            {witness0_id, witness0_witness_id},\n            {witness1_id, witness1_witness_id},\n            {witness2_id, witness2_witness_id},\n            {witness3_id, witness3_witness_id},\n            {witness4_id, witness4_witness_id},\n            {witness5_id, witness5_witness_id},\n            {witness6_id, witness6_witness_id},\n            {witness7_id, witness7_witness_id},\n            {witness8_id, witness8_witness_id},\n            {witness9_id, witness9_witness_id},\n            {witness10_id, witness10_witness_id},\n            {witness11_id, witness11_witness_id},\n            {witness12_id, witness12_witness_id},\n            {witness13_id, witness13_witness_id}\n      };\n\n      // Check current default witnesses, default chain is configured with 9 witnesses\n      auto witnesses = db.get_global_properties().active_witnesses;\n      BOOST_CHECK_EQUAL(witnesses.size(), INITIAL_WITNESS_COUNT);\n      BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 4u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 5u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 6u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9u);\n\n      // Activate all witnesses\n      // Each witness is voted with incremental stake so last witness created will be the ones with more votes\n      int c = 0;\n      for (auto l : witness_map) {\n         int stake = 100 + c + 10;\n         transfer(committee_account, l.first, asset(stake));\n         {\n            set_expiration(db, trx);\n            account_update_operation op;\n            op.account = l.first;\n            op.new_options = l.first(db).options;\n            op.new_options->votes.insert(l.second(db).vote_id);\n\n            trx.operations.push_back(op);\n            sign(trx, private_keys.at(c));\n            PUSH_TX(db, trx);\n            trx.clear();\n         }\n         ++c;\n      }\n\n      // Trigger the new witnesses\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      // Check my witnesses are now in control of the system\n      witnesses = db.get_global_properties().active_witnesses;\n      BOOST_CHECK_EQUAL(witnesses.size(), INITIAL_WITNESS_COUNT);\n      BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 16u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 17u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 18u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 19u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 20u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 21u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 22u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 23u);\n      BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 24u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(track_votes_witnesses_enabled)\n{\n   try\n   {\n      graphene::app::database_api db_api1(db);\n\n      INVOKE(put_my_witnesses);\n\n      const account_id_type witness1_id= get_account(\"witness1\").id;\n      auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name);\n      BOOST_CHECK_EQUAL(witness1_object->total_votes, 111u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(track_votes_witnesses_disabled)\n{\n   try\n   {\n      graphene::app::database_api db_api1(db);\n\n      INVOKE(put_my_witnesses);\n\n      const account_id_type witness1_id= get_account(\"witness1\").id;\n      auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name);\n      BOOST_CHECK_EQUAL(witness1_object->total_votes, 0u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(put_my_committee_members)\n{\n   try\n   {\n      ACTORS( (committee0)\n              (committee1)\n              (committee2)\n              (committee3)\n              (committee4)\n              (committee5)\n              (committee6)\n              (committee7)\n              (committee8)\n              (committee9)\n              (committee10)\n              (committee11)\n              (committee12)\n              (committee13) );\n\n      // Upgrade all accounts to LTM\n      upgrade_to_lifetime_member(committee0_id);\n      upgrade_to_lifetime_member(committee1_id);\n      upgrade_to_lifetime_member(committee2_id);\n      upgrade_to_lifetime_member(committee3_id);\n      upgrade_to_lifetime_member(committee4_id);\n      upgrade_to_lifetime_member(committee5_id);\n      upgrade_to_lifetime_member(committee6_id);\n      upgrade_to_lifetime_member(committee7_id);\n      upgrade_to_lifetime_member(committee8_id);\n      upgrade_to_lifetime_member(committee9_id);\n      upgrade_to_lifetime_member(committee10_id);\n      upgrade_to_lifetime_member(committee11_id);\n      upgrade_to_lifetime_member(committee12_id);\n      upgrade_to_lifetime_member(committee13_id);\n\n      // Create all the committee\n      const committee_member_id_type committee0_committee_id = create_committee_member(committee0_id(db)).id;\n      const committee_member_id_type committee1_committee_id = create_committee_member(committee1_id(db)).id;\n      const committee_member_id_type committee2_committee_id = create_committee_member(committee2_id(db)).id;\n      const committee_member_id_type committee3_committee_id = create_committee_member(committee3_id(db)).id;\n      const committee_member_id_type committee4_committee_id = create_committee_member(committee4_id(db)).id;\n      const committee_member_id_type committee5_committee_id = create_committee_member(committee5_id(db)).id;\n      const committee_member_id_type committee6_committee_id = create_committee_member(committee6_id(db)).id;\n      const committee_member_id_type committee7_committee_id = create_committee_member(committee7_id(db)).id;\n      const committee_member_id_type committee8_committee_id = create_committee_member(committee8_id(db)).id;\n      const committee_member_id_type committee9_committee_id = create_committee_member(committee9_id(db)).id;\n      const committee_member_id_type committee10_committee_id = create_committee_member(committee10_id(db)).id;\n      const committee_member_id_type committee11_committee_id = create_committee_member(committee11_id(db)).id;\n      const committee_member_id_type committee12_committee_id = create_committee_member(committee12_id(db)).id;\n      const committee_member_id_type committee13_committee_id = create_committee_member(committee13_id(db)).id;\n\n      // Create a vector with private key of all committee members, will be used to activate 9 members at a time\n      const vector <fc::ecc::private_key> private_keys = {\n            committee0_private_key,\n            committee1_private_key,\n            committee2_private_key,\n            committee3_private_key,\n            committee4_private_key,\n            committee5_private_key,\n            committee6_private_key,\n            committee7_private_key,\n            committee8_private_key,\n            committee9_private_key,\n            committee10_private_key,\n            committee11_private_key,\n            committee12_private_key,\n            committee13_private_key\n      };\n\n      // create a map with account id and committee member id\n      const flat_map <account_id_type, committee_member_id_type> committee_map = {\n            {committee0_id, committee0_committee_id},\n            {committee1_id, committee1_committee_id},\n            {committee2_id, committee2_committee_id},\n            {committee3_id, committee3_committee_id},\n            {committee4_id, committee4_committee_id},\n            {committee5_id, committee5_committee_id},\n            {committee6_id, committee6_committee_id},\n            {committee7_id, committee7_committee_id},\n            {committee8_id, committee8_committee_id},\n            {committee9_id, committee9_committee_id},\n            {committee10_id, committee10_committee_id},\n            {committee11_id, committee11_committee_id},\n            {committee12_id, committee12_committee_id},\n            {committee13_id, committee13_committee_id}\n      };\n\n      // Check current default committee, default chain is configured with 9 committee members\n      auto committee_members = db.get_global_properties().active_committee_members;\n\n      BOOST_CHECK_EQUAL(committee_members.size(), INITIAL_COMMITTEE_MEMBER_COUNT);\n      BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 0u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 1u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 2u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 3u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 4u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 5u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 6u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 7u);\n      BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 8u);\n\n      // Activate all committee\n      // Each committee is voted with incremental stake so last member created will be the ones with more votes\n      int c = 0;\n      for (auto committee : committee_map) {\n         int stake = 100 + c + 10;\n         transfer(committee_account, committee.first, asset(stake));\n         {\n            set_expiration(db, trx);\n            account_update_operation op;\n            op.account = committee.first;\n            op.new_options = committee.first(db).options;\n\n            op.new_options->votes.clear();\n            op.new_options->votes.insert(committee.second(db).vote_id);\n            op.new_options->num_committee = 1;\n\n            trx.operations.push_back(op);\n            sign(trx, private_keys.at(c));\n            PUSH_TX(db, trx);\n            trx.clear();\n         }\n         ++c;\n      }\n\n      // Trigger the new committee\n      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);\n      generate_block();\n\n      // Check my witnesses are now in control of the system\n      committee_members = db.get_global_properties().active_committee_members;\n      std::sort(committee_members.begin(), committee_members.end());\n\n      BOOST_CHECK_EQUAL(committee_members.size(), INITIAL_COMMITTEE_MEMBER_COUNT);\n\n      // Check my committee members are now in control of the system\n      BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 15);\n      BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 16);\n      BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 17);\n      BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 18);\n      BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 19);\n      BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 20);\n      BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 21);\n      BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 22);\n      BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 23);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(track_votes_committee_enabled)\n{\n   try\n   {\n      graphene::app::database_api db_api1(db);\n\n      INVOKE(put_my_committee_members);\n\n      const account_id_type committee1_id= get_account(\"committee1\").id;\n      auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name);\n      BOOST_CHECK_EQUAL(committee1_object->total_votes, 111u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(track_votes_committee_disabled)\n{\n   try\n   {\n      graphene::app::database_api db_api1(db);\n\n      INVOKE(put_my_committee_members);\n\n      const account_id_type committee1_id= get_account(\"committee1\").id;\n      auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name);\n      BOOST_CHECK_EQUAL(committee1_object->total_votes, 0u);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE(invalid_voting_account)\n{\n   try\n   {\n      ACTORS((alice));\n\n      account_id_type invalid_account_id( (uint64_t)999999 );\n\n      BOOST_CHECK( !db.find( invalid_account_id ) );\n\n      graphene::chain::account_update_operation op;\n      op.account = alice_id;\n      op.new_options = alice.options;\n      op.new_options->voting_account = invalid_account_id;\n      trx.operations.push_back(op);\n      sign(trx, alice_private_key);\n\n      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );\n\n   } FC_LOG_AND_RETHROW()\n}\nBOOST_AUTO_TEST_CASE(last_voting_date)\n{\n   try\n   {\n      ACTORS((alice));\n\n      transfer(committee_account, alice_id, asset(100));\n\n      // we are going to vote for this witness\n      auto witness1 = witness_id_type(1)(db);\n\n      auto stats_obj = db.get_account_stats_by_owner(alice_id);\n      BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), 0u);\n\n      // alice votes\n      graphene::chain::account_update_operation op;\n      op.account = alice_id;\n      op.new_options = alice.options;\n      op.new_options->votes.insert(witness1.vote_id);\n      trx.operations.push_back(op);\n      sign(trx, alice_private_key);\n      PUSH_TX( db, trx, ~0 );\n\n      auto now = db.head_block_time().sec_since_epoch();\n\n      // last_vote_time is updated for alice\n      stats_obj = db.get_account_stats_by_owner(alice_id);\n      BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), now);\n\n   } FC_LOG_AND_RETHROW()\n}\nBOOST_AUTO_TEST_CASE(last_voting_date_proxy)\n{\n   try\n   {\n      ACTORS((alice)(proxy)(bob));\n\n      transfer(committee_account, alice_id, asset(100));\n      transfer(committee_account, bob_id, asset(200));\n      transfer(committee_account, proxy_id, asset(300));\n\n      generate_block();\n\n      // witness to vote for\n      auto witness1 = witness_id_type(1)(db);\n\n      // round1: alice changes proxy, this is voting activity\n      {\n         graphene::chain::account_update_operation op;\n         op.account = alice_id;\n         op.new_options = alice_id(db).options;\n         op.new_options->voting_account = proxy_id;\n         trx.operations.push_back(op);\n         sign(trx, alice_private_key);\n         PUSH_TX( db, trx, ~0 );\n      }\n      // alice last_vote_time is updated\n      auto alice_stats_obj = db.get_account_stats_by_owner(alice_id);\n      auto round1 = db.head_block_time().sec_since_epoch();\n      BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1);\n\n      generate_block();\n\n      // round 2: alice update account but no proxy or voting changes are done\n      {\n         graphene::chain::account_update_operation op;\n         op.account = alice_id;\n         op.new_options = alice_id(db).options;\n         trx.operations.push_back(op);\n         sign(trx, alice_private_key);\n         set_expiration( db, trx );\n         PUSH_TX( db, trx, ~0 );\n      }\n      // last_vote_time is not updated\n      alice_stats_obj = db.get_account_stats_by_owner(alice_id);\n      BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1);\n\n      generate_block();\n\n      // round 3: bob votes\n      {\n         graphene::chain::account_update_operation op;\n         op.account = bob_id;\n         op.new_options = bob_id(db).options;\n         op.new_options->votes.insert(witness1.vote_id);\n         trx.operations.push_back(op);\n         sign(trx, bob_private_key);\n         set_expiration( db, trx );\n         PUSH_TX(db, trx, ~0);\n      }\n\n      // last_vote_time for bob is updated as he voted\n      auto round3 = db.head_block_time().sec_since_epoch();\n      auto bob_stats_obj = db.get_account_stats_by_owner(bob_id);\n      BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3);\n\n      generate_block();\n\n      // round 4: proxy votes\n      {\n         graphene::chain::account_update_operation op;\n         op.account = proxy_id;\n         op.new_options = proxy_id(db).options;\n         op.new_options->votes.insert(witness1.vote_id);\n         trx.operations.push_back(op);\n         sign(trx, proxy_private_key);\n         PUSH_TX(db, trx, ~0);\n      }\n\n      // proxy just voted so the last_vote_time is updated\n      auto round4 = db.head_block_time().sec_since_epoch();\n      auto proxy_stats_obj = db.get_account_stats_by_owner(proxy_id);\n      BOOST_CHECK_EQUAL(proxy_stats_obj.last_vote_time.sec_since_epoch(), round4);\n\n      // alice haves proxy, proxy votes but last_vote_time is not updated for alice\n      alice_stats_obj = db.get_account_stats_by_owner(alice_id);\n      BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1);\n\n      // bob haves nothing to do with proxy so last_vote_time is not updated\n      bob_stats_obj = db.get_account_stats_by_owner(bob_id);\n      BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3);\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( witness_votes_calculation )\n{\n   try\n   {\n      auto original_wits = db.get_global_properties().active_witnesses;\n\n      INVOKE( put_my_witnesses );\n\n      GET_ACTOR( witness0 );\n      GET_ACTOR( witness1 );\n      GET_ACTOR( witness2 );\n      GET_ACTOR( witness3 );\n      GET_ACTOR( witness4 );\n      GET_ACTOR( witness5 );\n      GET_ACTOR( witness6 );\n      GET_ACTOR( witness7 );\n      GET_ACTOR( witness8 );\n      GET_ACTOR( witness9 );\n      GET_ACTOR( witness10 );\n      GET_ACTOR( witness11 );\n      GET_ACTOR( witness12 );\n      GET_ACTOR( witness13 );\n\n      graphene::app::database_api db_api1(db);\n\n      vector<account_id_type> wit_account_ids = { witness0_id, witness1_id, witness2_id, witness3_id,\n                                                  witness4_id, witness5_id, witness6_id, witness7_id,\n                                                  witness8_id, witness9_id, witness10_id, witness11_id,\n                                                  witness12_id, witness13_id };\n      vector<witness_id_type> wit_ids;\n      size_t total = wit_account_ids.size();\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         auto wit_object = db_api1.get_witness_by_account( wit_account_ids[i](db).name );\n         BOOST_REQUIRE( wit_object.valid() );\n         wit_ids.push_back( wit_object->id );\n      }\n\n      generate_blocks( HARDFORK_CORE_2103_TIME - 750 * 86400 );\n      set_expiration( db, trx );\n\n      // refresh last_vote_time\n      for( size_t i = 0; i < total; ++i )\n      {\n         account_id_type voter = wit_account_ids[ total - i - 1 ];\n\n         account_update_operation op;\n         op.account = voter;\n         op.new_options = op.account(db).options;\n         op.new_options->voting_account = account_id_type();\n\n         trx.operations.clear();\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n\n         op.new_options->voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n         trx.operations.clear();\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n\n         trx.clear();\n\n         generate_blocks( db.head_block_time() + 45 * 86400 );\n         set_expiration( db, trx );\n      }\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, 110u + i );\n      }\n\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      set_expiration( db, trx );\n\n      uint64_t expected_votes[14];\n\n      expected_votes[0] = 110; // 750 - 45 * 13 = 165 days\n      expected_votes[1] = 111; // 210 days\n      expected_votes[2] = 112; // 255 days\n      expected_votes[3] = 113; // 300 days\n      expected_votes[4] = 114; // 345 days\n      expected_votes[5] = 115 - 115 / 8; // 390 days\n      expected_votes[6] = 116 - 116 * 2 / 8; // 435 days\n      expected_votes[7] = 117 - 117 * 3 / 8; // 480 days\n      expected_votes[8] = 118 - 118 * 4 / 8; // 525 days\n      expected_votes[9] = 119 - 119 * 5 / 8; // 570 days\n      expected_votes[10] = 120 - 120 * 6 / 8; // 615 days\n      expected_votes[11] = 121 - 121 * 7 / 8; // 660 days\n      expected_votes[12] = 0; // 705 days\n      expected_votes[13] = 0; // 750 days\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      flat_set<witness_id_type> expected_active_witnesses = { wit_ids[0], wit_ids[1], wit_ids[2],\n                                                              wit_ids[3], wit_ids[4], wit_ids[5],\n                                                              wit_ids[6], wit_ids[7], wit_ids[8] };\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // new vote\n      {\n         account_update_operation op;\n         op.account = wit_account_ids[12];\n         op.new_options = op.account(db).options;\n         op.new_options->votes.insert( wit_ids[8](db).vote_id );\n\n         trx.operations.clear();\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n      }\n\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      expected_votes[8] += 122;\n      expected_votes[12] = 122;\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      expected_active_witnesses = { wit_ids[0], wit_ids[1], wit_ids[2],\n                                    wit_ids[3], wit_ids[4], wit_ids[5],\n                                    wit_ids[6], wit_ids[8], wit_ids[12] };\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // create some tickets\n      create_ticket( wit_account_ids[4], lock_forever, asset(40) );\n      create_ticket( wit_account_ids[7], lock_forever, asset(30) );\n      create_ticket( wit_account_ids[7], lock_720_days, asset(20) );\n\n      auto tick_start_time = db.head_block_time();\n\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      // votes doesn't change\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // some days passed\n      generate_blocks( tick_start_time + fc::days(15) );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      // check votes\n      expected_votes[0] = 110; // 180 days\n      expected_votes[1] = 111; // 225 days\n      expected_votes[2] = 112; // 270 days\n      expected_votes[3] = 113; // 315 days\n      expected_votes[4] = 114+40 - (114+40) / 8; // 360 days\n      expected_votes[5] = 115 - 115 * 2 / 8; // 405 days\n      expected_votes[6] = 116 - 116 * 3 / 8; // 450 days, 73\n      expected_votes[7] = 117+50 - (117+50) * 4 / 8; // 495 days, 84\n      expected_votes[8] = 118 - 118 * 5 / 8 + 122; // 540 days\n      expected_votes[9] = 119 - 119 * 6 / 8; // 585 days\n      expected_votes[10] = 120 - 120 * 7 / 8; // 630 days\n      expected_votes[11] = 0; // 675 days\n      expected_votes[12] = 122; // 15 days\n      expected_votes[13] = 0; // 765 days\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      expected_active_witnesses = { wit_ids[0], wit_ids[1], wit_ids[2],\n                                    wit_ids[3], wit_ids[4], wit_ids[5],\n                                    wit_ids[7], wit_ids[8], wit_ids[12] };\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // some days passed\n      generate_blocks( tick_start_time + fc::days(30) );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      // check votes\n      expected_votes[4] = 114+40*3 - (114+40*3) / 8; // 375 days\n      expected_votes[7] = 117+50*3 - (117+50*3) * 4 / 8; // 510 days\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // some days passed\n      generate_blocks( tick_start_time + fc::days(45) );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      // check votes\n      expected_votes[4] = 114+40*7 - (114+40*7) / 8; // 390 days\n      expected_votes[7] = 117+50*7 - (117+50*7) * 4 / 8; // 525 days\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // some days passed\n      generate_blocks( tick_start_time + fc::days(60) );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      // pob activated\n      expected_votes[0] = 0; // 225 days\n      expected_votes[1] = 0; // 270 days\n      expected_votes[2] = 0; // 315 days\n      expected_votes[3] = 0; // 360 days\n      int64_t base4 = 40 * 8 + (114 - 40) - 40;\n      expected_votes[4] = base4 - base4 * 2 / 8; // 405 days\n      expected_votes[5] = 0; // 450 days\n      expected_votes[6] = 0; // 495 days\n      int64_t base7 = 20 * 8 * 8 + (30 - 20) * 8 + (117 - 30 - 20) - (30 - 20);\n      expected_votes[7] = base7 - base7 * 5 / 8; // 540 days\n      expected_votes[8] = 0; // 585 days\n      expected_votes[9] = 0; // 630 days\n      expected_votes[10] = 0; // 675 days\n      expected_votes[11] = 0; // 720 days\n      expected_votes[12] = 0; // 60 days\n      expected_votes[13] = 0; // 810 days\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      expected_active_witnesses = original_wits;\n      expected_active_witnesses.erase( *expected_active_witnesses.rbegin() );\n      expected_active_witnesses.erase( *expected_active_witnesses.rbegin() );\n      expected_active_witnesses.insert( wit_ids[4] );\n      expected_active_witnesses.insert( wit_ids[7] );\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n      // some days passed\n      generate_blocks( tick_start_time + fc::days(60+180) );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n\n      // check votes\n      base4 = 40 * 6 + (114 - 40) - 40;\n      expected_votes[4] = base4 - base4 * 6 / 8; // 585 days\n      base7 = 20 * 8 * 6 + (30 - 20) * 6 + (117 - 30 - 20) - (30 - 20);\n      expected_votes[7] = 0; // 720 days\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( wit_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      expected_active_witnesses = original_wits;\n      expected_active_witnesses.erase( *expected_active_witnesses.rbegin() );\n      expected_active_witnesses.insert( wit_ids[4] );\n      BOOST_CHECK( db.get_global_properties().active_witnesses == expected_active_witnesses );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_CASE( committee_votes_calculation )\n{\n   try\n   {\n      INVOKE( put_my_committee_members );\n\n      GET_ACTOR( committee0 );\n      GET_ACTOR( committee1 );\n      GET_ACTOR( committee2 );\n      GET_ACTOR( committee3 );\n      GET_ACTOR( committee4 );\n      GET_ACTOR( committee5 );\n      GET_ACTOR( committee6 );\n      GET_ACTOR( committee7 );\n      GET_ACTOR( committee8 );\n      GET_ACTOR( committee9 );\n      GET_ACTOR( committee10 );\n      GET_ACTOR( committee11 );\n      GET_ACTOR( committee12 );\n      GET_ACTOR( committee13 );\n\n      graphene::app::database_api db_api1(db);\n\n      vector<account_id_type> com_account_ids = { committee0_id, committee1_id, committee2_id, committee3_id,\n                                                  committee4_id, committee5_id, committee6_id, committee7_id,\n                                                  committee8_id, committee9_id, committee10_id, committee11_id,\n                                                  committee12_id, committee13_id };\n      vector<committee_member_id_type> com_ids;\n      size_t total = com_account_ids.size();\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         auto com_object = db_api1.get_committee_member_by_account( com_account_ids[i](db).name );\n         BOOST_REQUIRE( com_object.valid() );\n         com_ids.push_back( com_object->id );\n      }\n\n      generate_blocks( HARDFORK_CORE_2103_TIME - 750 * 86400 );\n      set_expiration( db, trx );\n\n      // refresh last_vote_time\n      for( size_t i = 0; i < total; ++i )\n      {\n         account_id_type voter = com_account_ids[ total - i - 1 ];\n\n         account_update_operation op;\n         op.account = voter;\n         op.new_options = op.account(db).options;\n         op.new_options->voting_account = account_id_type();\n\n         trx.operations.clear();\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n\n         op.new_options->voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT;\n         trx.operations.clear();\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n\n         trx.clear();\n\n         generate_blocks( db.head_block_time() + 45 * 86400 );\n         set_expiration( db, trx );\n      }\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( com_ids[i](db).total_votes, 110u + i );\n      }\n\n      generate_blocks( HARDFORK_CORE_2103_TIME );\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      set_expiration( db, trx );\n\n      uint64_t expected_votes[14];\n\n      expected_votes[0] = 110; // 750 - 45 * 13 = 165 days\n      expected_votes[1] = 111; // 210 days\n      expected_votes[2] = 112; // 255 days\n      expected_votes[3] = 113; // 300 days\n      expected_votes[4] = 114; // 345 days\n      expected_votes[5] = 115 - 115 / 8; // 390 days\n      expected_votes[6] = 116 - 116 * 2 / 8; // 435 days\n      expected_votes[7] = 117 - 117 * 3 / 8; // 480 days\n      expected_votes[8] = 118 - 118 * 4 / 8; // 525 days\n      expected_votes[9] = 119 - 119 * 5 / 8; // 570 days\n      expected_votes[10] = 120 - 120 * 6 / 8; // 615 days\n      expected_votes[11] = 121 - 121 * 7 / 8; // 660 days\n      expected_votes[12] = 0; // 705 days\n      expected_votes[13] = 0; // 750 days\n\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( com_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      vector<committee_member_id_type> expected_active_committee_members = {\n                                                              com_ids[0], com_ids[1], com_ids[2],\n                                                              com_ids[3], com_ids[4], com_ids[5],\n                                                              com_ids[6], com_ids[7], com_ids[8] };\n      auto current_committee_members = db.get_global_properties().active_committee_members;\n      sort( current_committee_members.begin(), current_committee_members.end() );\n      BOOST_CHECK( current_committee_members == expected_active_committee_members );\n\n      // new vote\n      {\n         account_update_operation op;\n         op.account = com_account_ids[12];\n         op.new_options = op.account(db).options;\n         op.new_options->votes.insert( com_ids[11](db).vote_id );\n         op.new_options->votes.insert( com_ids[12](db).vote_id );\n\n         trx.operations.clear();\n         trx.operations.push_back(op);\n         PUSH_TX(db, trx, ~0);\n      }\n\n      generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );\n      set_expiration( db, trx );\n\n      expected_votes[11] += 122/2;\n      expected_votes[12] = 122/2;\n      for( size_t i = 0; i < total; ++i )\n      {\n         BOOST_CHECK_EQUAL( com_ids[i](db).total_votes, expected_votes[i] );\n      }\n\n      expected_active_committee_members = { com_ids[0], com_ids[1], com_ids[2],\n                                            com_ids[3], com_ids[4], com_ids[5],\n                                            com_ids[6], com_ids[7], com_ids[11] };\n      current_committee_members = db.get_global_properties().active_committee_members;\n      sort( current_committee_members.begin(), current_committee_members.end() );\n      BOOST_CHECK( current_committee_members == expected_active_committee_members );\n\n   } FC_LOG_AND_RETHROW()\n}\n\nBOOST_AUTO_TEST_SUITE_END()\n"
  },
  {
    "path": "tests/tests/wallet_tests.cpp",
    "content": "/*\n * Copyright (c) 2017 Cryptonomex, Inc., and contributors.\n *\n * The MIT License\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include <boost/test/unit_test.hpp>\n\n#include <graphene/app/database_api.hpp>\n#include <graphene/wallet/wallet.hpp>\n#include <fc/crypto/digest.hpp>\n\n#include <iostream>\n\n#include \"../common/database_fixture.hpp\"\n\nusing namespace graphene::chain;\nusing namespace graphene::chain::test;\nusing namespace graphene::wallet;\n\nBOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture)\n\n  /***\n   * Check the basic behavior of deriving potential owner keys from a brain key\n   */\n  BOOST_AUTO_TEST_CASE(derive_owner_keys_from_brain_key) {\n      try {\n          /***\n           * Act\n           */\n          unsigned int nbr_keys_desired = 3;\n          vector<brain_key_info> derived_keys = graphene::wallet::utility::derive_owner_keys_from_brain_key(\"SOME WORDS GO HERE\", nbr_keys_desired);\n\n\n          /***\n           * Assert: Check the number of derived keys\n           */\n          BOOST_CHECK_EQUAL(nbr_keys_desired, derived_keys.size());\n\n          /***\n           * Assert: Check that each derived key is unique\n           */\n          set<string> set_derived_public_keys;\n          for (auto info : derived_keys) {\n              string description = (string) info.pub_key;\n              set_derived_public_keys.emplace(description);\n          }\n          BOOST_CHECK_EQUAL(nbr_keys_desired, set_derived_public_keys.size());\n\n          /***\n           * Assert: Check whether every public key begins with the expected prefix\n           */\n          string expected_prefix = GRAPHENE_ADDRESS_PREFIX;\n          for (auto info : derived_keys) {\n              string description = (string) info.pub_key;\n              BOOST_CHECK_EQUAL(0u, description.find(expected_prefix));\n          }\n\n      } FC_LOG_AND_RETHROW()\n  }\n  \nBOOST_AUTO_TEST_SUITE_END()\n\n"
  }
]